Cloudflare Terraforming

June 27, 2025 · View on GitHub

Overview

cf-terraforming is a command line utility to facilitate terraforming your existing Cloudflare resources. It does this by using your account credentials to retrieve your configurations from the Cloudflare API and converting them to Terraform configurations that can be used with the Terraform Cloudflare provider.

This tool is ideal if you already have Cloudflare resources defined but want to start managing them via Terraform, and don't want to spend the time to manually write the Terraform configuration to describe them. The intention is that this would be a one-time HCL generation of whichever resource(s) you want to begin managing exclusively through Terraform.

Read the announcement blog for further details on using cf-terraforming in your workflow.

Warning

This tool is not intended for use in CI.

Note

If you would like to export resources compatible with Terraform < 0.12.x, you will need to download an older release as this tool no longer supports it.

Usage

Usage:
  cf-terraforming import [flags]

Flags:
  -h, --help   help for import

Global Flags:
  -a, --account string                      Target the provided account ID for the command
  -c, --config string                       Path to config file (default "/Users/vaishak/.cf-terraforming.yaml")
  -e, --email string                        API Email address associated with your account
      --hostname string                     Hostname to use to query the API
  -k, --key string                          API Key generated on the 'My Profile' page. See: https://dash.cloudflare.com/profile
      --modern-import-block                 Whether to generate HCL import blocks for generated resources instead of terraform import compatible CLI commands. This is only compatible with Terraform 1.5+
      --provider-registry-hostname string   Hostname to use for provider registry lookups. Deprecated: this is no longer needed to be configured for custom registries.
      --resource-id key                     Resource type and IDs mapping in the format of key to comma separated values. Example: `cloudflare_zone_setting=always_online,cache_level,...`
      --resource-type string                Comma delimitered string of which resource(s) you wish to generate
      --terraform-binary-path string        Path to an existing Terraform binary (otherwise, one will be downloaded)
      --terraform-install-path string       Path to an initialized Terraform working directory (default ".")
  -t, --token string                        API Token
  -v, --verbose                             Specify verbose output (same as setting log level to debug)
  -z, --zone string                         Target the provided zone ID for the command

Authentication

Cloudflare supports two authentication methods to the API:

  • API Token - gives access only to resources and permissions specified for that token (recommended)
  • API key - gives access to everything your user profile has access to

Both can be retrieved on the user profile page.

Tip

We recommend that you store your Cloudflare credentials (API key, email, token) as environment variables as demonstrated below.

# if using API Token
export CLOUDFLARE_API_TOKEN='Hzsq3Vub-7Y-hSTlAaLH3Jq_YfTUOCcgf22_Fs-j'

# if using API Key
export CLOUDFLARE_EMAIL='user@example.com'
export CLOUDFLARE_API_KEY='1150bed3f45247b99f7db9696fffa17cbx9'

# specify zone ID
export CLOUDFLARE_ZONE_ID='81b06ss3228f488fh84e5e993c2dc17'

# now call cf-terraforming, e.g.
cf-terraforming generate \
  --resource-type "cloudflare_record" \
  --zone $CLOUDFLARE_ZONE_ID

cf-terraforming supports the following environment variables:

  • CLOUDFLARE_API_TOKEN - API Token based authentication
  • CLOUDFLARE_EMAIL, CLOUDFLARE_API_KEY - API Key based authentication

Alternatively, if using a config file, then specify the inputs using the same names the flag names. Example:

cat ~/.cf-terraforming.yaml
email: "email@domain.com"
key: "<key>"
#or
token: "<token>"

Example usage

cf-terraforming generate \
  --zone $CLOUDFLARE_ZONE_ID \
  --resource-type "cloudflare_record"

will contact the Cloudflare API on your behalf and result in a valid Terraform configuration representing the resource you requested:

resource "cloudflare_record" "terraform_managed_resource" {
  name = "example.com"
  proxied = false
  ttl = 120
  type = "A"
  value = "198.51.100.4"
  zone_id = "0da42c8d2132a9ddaf714f9e7c920711"
}

Some resource require an ID to be passed in to be able to either generate the hcl block or import command. The resources which require an id are listed in the table below for the v5 provider. Example usage:

cf-terraforming generate \
  --zone $CLOUDFLARE_ZONE_ID \
  --resource-type "cloudflare_hostname_tls_setting" \
  --resource-id "cloudflare_hostname_tls_setting=ciphers"

Define --terraform-binary-path on the generate command which will ensure we're reusing the installed version of terraform instead of fetching a new one each time, if you're seeing issues.

Prerequisites

  • A Cloudflare account with resources defined (e.g. a few zones, some load balancers, spectrum applications, etc)
  • A valid Cloudflare API key and sufficient permissions to access the resources you are requesting via the API
  • An initialised Terraform directory (terraform init has run and providers installed). See the provider documentation if you have not yet setup the Terraform directory.

Installation

Homebrew

brew tap cloudflare/cloudflare
brew install cloudflare/cloudflare/cf-terraforming

Note

If you have installed an older version of cf-terraforming via Homebrew, you may need to first uninstall cf-terraforming and then install it to pick up the updated install process and address the signing/notarisation issues.

Go

go install github.com/cloudflare/cf-terraforming/cmd/cf-terraforming@latest

If you use another OS, you will need to download the release directly from GitHub Releases or build the Go source.

Importing with Terraform state

cf-terraforming has the ability to generate the configuration for you to import existing resources.

Depending on your version of Terraform, you can generate the import block (Terraform 1.5+) using the --modern-import-block flag or the terraform import compatible CLI output (all versions).

This command assumes you have already ran cf-terraforming generate ... to output your resources.

# All versions of Terraform
cf-terraforming import \
  --resource-type "cloudflare_record" \
  --email $CLOUDFLARE_EMAIL \
  --key $CLOUDFLARE_API_KEY \
  --zone $CLOUDFLARE_ZONE_ID
# Terraform 1.5+ only
cf-terraforming import \
  --resource-type "cloudflare_record" \
  --modern-import-block \
  --email $CLOUDFLARE_EMAIL \
  --key $CLOUDFLARE_API_KEY \
  --zone $CLOUDFLARE_ZONE_ID

Using non-standard Terraform binaries

Internally, we use terraform-exec library to run Terraform operations in the same way that the CLI tooling would. If a terraform binary is not available on your system path, we will attempt to download the latest to use it.

Should you have the binary stored in a non-standard location, want to use an existing binary, or you wish to provide a Terraform compatible binary (such as tofu), you need to provide the --terraform-binary-path flag or CLOUDFLARE_TERRAFORM_BINARY_PATH environment variable to instruct cf-terraforming which you expect to use.

CDKTF

If you'd like to use cdktf for your project resources, you can pipe the output from cf-terraforming into cdktf convert in order to correctly generate CDKTF output automatically.

Example:

cf-terraforming generate \
  --resource-type "cloudflare_record" \
  --zone "0da42c8d2132a9ddaf714f9e7c920711" \
| cdktf convert --language "typescript" --provider "cloudflare/cloudflare"

Supported Resources

v5

Any resource that is released within the Terraform Provider is automatically supported for generation and import. The cf-terraforming cli tool should be able to generate the HCL config for resource in the version 5 of the provider. Certain resources generated might not pass terraform validate command due to inconsistencies with the schema. These are known issues and will be addressed in the later releases.

Generate

Any resources not listed may have known issues. The HCL config may still be generated but might need manual modifications.

Resource TypeIdentifier TypeCLI Flags Example
cloudflare_accountaccount
cloudflare_account_memberaccount
cloudflare_account_subscriptionaccount
cloudflare_address_mapaccount
cloudflare_api_shield_discovery_operationzone
cloudflare_api_shield_operationzone
cloudflare_api_shield_operation_schema_validation_settingszonecloudflare_api_shield_operation_schema_validation_settings=8255d5da-5a46-4928-ad00-01de7d48c1e7
cloudflare_api_shield_schemazone
cloudflare_api_shield_schema_validation_settingszone
cloudflare_argo_smart_routingzone
cloudflare_argo_tiered_cachingzone
cloudflare_authenticated_origin_pullszonecloudflare_authenticated_origin_pulls=jotsqcjaho.terraform.cfapi.net
cloudflare_authenticated_origin_pulls_certificatezone
cloudflare_bot_managementzone
cloudflare_calls_sfu_appaccount
cloudflare_calls_turn_appaccount
cloudflare_certificate_packzone
cloudflare_content_scanning_expressionzone
cloudflare_custom_hostnamezone
cloudflare_custom_hostname_fallback_originzone
cloudflare_d1_databaseaccount
cloudflare_dns_firewallaccount
cloudflare_dns_recordzone
cloudflare_dns_zone_transfers_aclaccount
cloudflare_dns_zone_transfers_incomingzone
cloudflare_dns_zone_transfers_outgoingzone
cloudflare_dns_zone_transfers_peeraccount
cloudflare_dns_zone_transfers_tsigaccount
cloudflare_email_routing_addressaccount
cloudflare_email_routing_catch_allzone
cloudflare_email_routing_dnszone
cloudflare_email_routing_rulezone
cloudflare_email_routing_settingszone
cloudflare_email_security_block_senderaccount
cloudflare_email_security_impersonation_registryaccount
cloudflare_email_security_trusted_domainsaccount
cloudflare_filterzone
cloudflare_healthcheckzone
cloudflare_hostname_tls_settingzonecloudflare_hostname_tls_setting=ciphers,min_tls_version
cloudflare_keyless_certificatezone
cloudflare_leaked_credential_checkzone
cloudflare_leaked_credential_check_rulezone
cloudflare_listaccount
cloudflare_list_itemaccountcloudflare_list_item=2a4b8b2017aa4b3cb9e1151b52c81d22
cloudflare_load_balancerzone
cloudflare_load_balancer_monitoraccount
cloudflare_load_balancer_poolaccount
cloudflare_logpull_retentionzone
cloudflare_logpush_jobaccount or zone
cloudflare_magic_wan_static_routeaccount
cloudflare_managed_transformszone
cloudflare_mtls_certificateaccount
cloudflare_notification_policyaccount
cloudflare_notification_policy_webhooksaccount
cloudflare_observatory_scheduled_testzonecloudflare_observatory_scheduled_test=terraform.cfapi.net/thyygxveip
cloudflare_origin_ca_certificatezone
cloudflare_page_rulezone
cloudflare_page_shield_policyzone
cloudflare_pages_domainaccountcloudflare_pages_domain=ykfjmcgpfs
cloudflare_pages_projectaccount
cloudflare_queueaccount
cloudflare_queue_consumeraccountcloudflare_queue_consumer=2dde6ac405cd457c9ce59dc4bda20c65
cloudflare_r2_bucketaccount
cloudflare_r2_custom_domainaccountcloudflare_r2_custom_domain=jb-test-bucket,bnfywlzwpt
cloudflare_r2_managed_domainaccountcloudflare_r2_managed_domain=jb-test-bucket,bnfywlzwpt
cloudflare_rate_limitzone
cloudflare_regional_hostnamezone
cloudflare_regional_tiered_cachezone
cloudflare_registrar_domainaccount
cloudflare_rulesetaccount or zone
cloudflare_snippet_ruleszone
cloudflare_snippetszone
cloudflare_spectrum_applicationzone
cloudflare_streamaccount
cloudflare_stream_keyaccount
cloudflare_stream_live_inputaccount
cloudflare_stream_watermarkaccount
cloudflare_stream_webhookaccount
cloudflare_tiered_cachezone
cloudflare_total_tlszone
cloudflare_turnstile_widgetaccount
cloudflare_url_normalization_settingszone
cloudflare_useraccount
cloudflare_waiting_roomaccount or zone
cloudflare_waiting_room_eventzonecloudflare_waiting_room_event=e7f9e4c190ea8d6c66cab32ac110f39a
cloudflare_waiting_room_ruleszonecloudflare_waiting_room_rules=8bbd1b13450f6c63ab6ab4e08a63762d
cloudflare_waiting_room_settingszone
cloudflare_web3_hostnamezone
cloudflare_web_analytics_ruleaccountcloudflare_web_analytics_rule=2fa89d8f-35f7-49ef-87d3-f24e866a5d5e
cloudflare_web_analytics_siteaccount
cloudflare_workers_cron_triggeraccountcloudflare_workers_cron_trigger=script_2
cloudflare_workers_custom_domainaccount
cloudflare_workers_deploymentaccountcloudflare_workers_deployment=script_2
cloudflare_workers_for_platforms_dispatch_namespaceaccount
cloudflare_workers_kv_namespaceaccount
cloudflare_workers_script_subdomainaccountcloudflare_workers_script_subdomain=accounts
cloudflare_zero_trust_access_applicationaccount or zone
cloudflare_zero_trust_access_custom_pageaccount
cloudflare_zero_trust_access_groupaccount or zone
cloudflare_zero_trust_access_identity_provideraccount or zone
cloudflare_zero_trust_access_infrastructure_targetaccount
cloudflare_zero_trust_access_key_configurationaccount
cloudflare_zero_trust_access_mtls_certificateaccount or zone
cloudflare_zero_trust_access_mtls_hostname_settingsaccount or zone
cloudflare_zero_trust_access_policyaccount
cloudflare_zero_trust_access_service_tokenaccount or zone
cloudflare_zero_trust_access_short_lived_certificateaccount or zone
cloudflare_zero_trust_access_tagaccount
cloudflare_zero_trust_device_custom_profileaccount
cloudflare_zero_trust_device_default_profileaccount
cloudflare_zero_trust_device_default_profile_certificateszone
cloudflare_zero_trust_device_default_profile_local_domain_fallbackaccount
cloudflare_zero_trust_device_managed_networksaccount
cloudflare_zero_trust_device_posture_integrationaccount
cloudflare_zero_trust_device_posture_ruleaccount
cloudflare_zero_trust_dex_testaccount
cloudflare_zero_trust_dlp_custom_profileaccountcloudflare_zero_trust_dlp_custom_profile=38f45ad8-476e-4b56-ad16-42f364250802
cloudflare_zero_trust_dlp_datasetaccount
cloudflare_zero_trust_dlp_predefined_profileaccountcloudflare_zero_trust_dlp_predefined_profile=c8932cc4-3312-4152-8041-f3f257122dc4,56a8c060-01bb-4f89-ba1e-3ad42770a342
cloudflare_zero_trust_dns_locationaccount
cloudflare_zero_trust_gateway_certificateaccount
cloudflare_zero_trust_gateway_policyaccount
cloudflare_zero_trust_gateway_proxy_endpointaccount
cloudflare_zero_trust_gateway_settingsaccount
cloudflare_zero_trust_listaccount
cloudflare_zero_trust_organizationaccount or zone
cloudflare_zero_trust_risk_behavioraccount
cloudflare_zero_trust_risk_scoring_integrationaccount
cloudflare_zero_trust_tunnel_cloudflaredaccount
cloudflare_zero_trust_tunnel_cloudflared_configaccountcloudflare_zero_trust_tunnel_cloudflared_config=285f508d-d6ef-4ce4-9293-983d5bdc269e
cloudflare_zero_trust_tunnel_cloudflared_routeaccount
cloudflare_zero_trust_tunnel_cloudflared_virtual_networkaccount
cloudflare_zonezone
cloudflare_zone_cache_reservezone
cloudflare_zone_cache_variantszone
cloudflare_zone_dnsseczone
cloudflare_zone_lockdownzone
cloudflare_zone_settingzonecloudflare_zone_setting=always_online,cache_level

Import

Any resources not listed may have known issues or may not yet support import.

Resource TypeIdentifier TypeCLI Flags Example
cloudflare_accountaccount
cloudflare_account_memberaccount
cloudflare_address_mapaccount
cloudflare_api_shield_operationzone
cloudflare_bot_managementzone
cloudflare_certificate_packzone
cloudflare_custom_hostnamezone
cloudflare_custom_hostname_fallback_originzone
cloudflare_d1_databaseaccount
cloudflare_dns_firewallaccount
cloudflare_dns_recordzone
cloudflare_dns_zone_transfers_aclaccount
cloudflare_dns_zone_transfers_incomingzone
cloudflare_dns_zone_transfers_outgoingzone
cloudflare_dns_zone_transfers_peeraccount
cloudflare_dns_zone_transfers_tsigaccount
cloudflare_email_routing_addressaccount
cloudflare_email_routing_catch_allzone
cloudflare_email_routing_dnszone
cloudflare_email_routing_rulezone
cloudflare_email_routing_settingszone
cloudflare_email_security_block_senderaccount
cloudflare_email_security_impersonation_registryaccount
cloudflare_email_security_trusted_domainsaccount
cloudflare_filterzone
cloudflare_healthcheckzone
cloudflare_hostname_tls_settingzonecloudflare_hostname_tls_setting=ciphers,min_tls_version
cloudflare_keyless_certificatezone
cloudflare_listaccount
cloudflare_list_itemaccountcloudflare_list_item=2a4b8b2017aa4b3cb9e1151b52c81d22
cloudflare_load_balancerzone
cloudflare_load_balancer_monitoraccount
cloudflare_load_balancer_poolaccount
cloudflare_logpush_jobaccount or zone
cloudflare_managed_transformszone
cloudflare_mtls_certificateaccount
cloudflare_notification_policyaccount
cloudflare_notification_policy_webhooksaccount
cloudflare_origin_ca_certificatezone
cloudflare_page_rulezone
cloudflare_page_shield_policyzone
cloudflare_pages_domainaccountcloudflare_pages_domain=ykfjmcgpfs
cloudflare_pages_projectaccount
cloudflare_queueaccount
cloudflare_r2_bucketaccount
cloudflare_r2_custom_domainaccountcloudflare_r2_custom_domain=jb-test-bucket,bnfywlzwpt
cloudflare_r2_managed_domainaccountcloudflare_r2_managed_domain=jb-test-bucket,bnfywlzwpt
cloudflare_rate_limitzone
cloudflare_regional_hostnamezone
cloudflare_regional_tiered_cachezone
cloudflare_rulesetaccount or zone
cloudflare_spectrum_applicationzone
cloudflare_tiered_cachezone
cloudflare_total_tlszone
cloudflare_turnstile_widgetaccount
cloudflare_url_normalization_settingszone
cloudflare_waiting_roomzone
cloudflare_waiting_room_eventzonecloudflare_waiting_room_event=e7f9e4c190ea8d6c66cab32ac110f39a
cloudflare_waiting_room_ruleszonecloudflare_waiting_room_rules=8bbd1b13450f6c63ab6ab4e08a63762d
cloudflare_waiting_room_settingszone
cloudflare_web3_hostnamezone
cloudflare_web_analytics_ruleaccountcloudflare_web_analytics_rule=2fa89d8f-35f7-49ef-87d3-f24e866a5d5e
cloudflare_web_analytics_siteaccount
cloudflare_workers_custom_domainaccount
cloudflare_workers_for_platforms_dispatch_namespaceaccount
cloudflare_workers_kv_namespaceaccount
cloudflare_zero_trust_access_applicationaccount or zone

v4

Any resources not listed are currently not supported.

ResourceResource ScopeGenerate SupportedImport Supported
cloudflare_access_applicationAccount
cloudflare_access_groupAccount
cloudflare_access_identity_providerAccount
cloudflare_access_mutual_tls_certificateAccount
cloudflare_access_policyAccount
cloudflare_access_ruleAccount
cloudflare_access_service_tokenAccount
cloudflare_account_memberAccount
cloudflare_api_shieldZone
cloudflare_api_tokenUser
cloudflare_argoZone
cloudflare_authenticated_origin_pullsZone
cloudflare_authenticated_origin_pulls_certificateZone
cloudflare_bot_managementZone
cloudflare_byo_ip_prefixAccount
cloudflare_certificate_packZone
cloudflare_custom_hostnameZone
cloudflare_custom_hostname_fallback_originAccount
cloudflare_custom_pagesAccount or Zone
cloudflare_custom_sslZone
cloudflare_filterZone
cloudflare_firewall_ruleZone
cloudflare_healthcheckZone
cloudflare_ip_listAccount
cloudflare_listAccount
cloudflare_load_balancerZone
cloudflare_load_balancer_monitorAccount
cloudflare_load_balancer_poolAccount
cloudflare_logpull_retentionZone
cloudflare_logpush_jobZone
cloudflare_logpush_ownership_challengeZone
cloudflare_magic_firewall_rulesetAccount
cloudflare_managed_headersZone
cloudflare_origin_ca_certificateZone
cloudflare_page_ruleZone
cloudflare_rate_limitZone
cloudflare_recordZone
cloudflare_rulesetAccount or Zone
cloudflare_spectrum_applicationZone
cloudflare_tiered_cacheZone
cloudflare_teams_listAccount
cloudflare_teams_locationAccount
cloudflare_teams_proxy_endpointAccount
cloudflare_teams_ruleAccount
cloudflare_tunnelAccount
cloudflare_turnstile_widgetAccount
cloudflare_url_normalization_settingsZone
cloudflare_waf_groupZone
cloudflare_waf_overrideZone
cloudflare_waf_packageZone
cloudflare_waf_ruleZone
cloudflare_waiting_roomZone
cloudflare_worker_cron_triggerAccount
cloudflare_worker_routeZone
cloudflare_worker_scriptAccount
cloudflare_workers_kvAccount
cloudflare_workers_kv_namespaceAccount
cloudflare_zoneAccount
cloudflare_zone_dnssecZone
cloudflare_zone_lockdownZone
cloudflare_zone_settings_overrideZone

Build cf-terraforming Locally

Quickstart

A quick and easy way to get it installed in the directory you’re currently working in is to run the following command.

GOBIN=$(pwd) go install -v github.com/cloudflare/cf-terraforming/cmd/cf-terraforming@master

Manual Build

Step 1: Clone the Repository

To clone the cf-terraforming repository from GitHub, run the following command:

git clone https://github.com/cloudflare/cf-terraforming.git
cd cf-terraforming

Step 2: Build cf-terraforming

To build cf-terraforming locally, run the following command in the terminal:

go build -o cf-terraforming ./cmd/cf-terraforming

This will compile the source code and create an executable named cf-terraforming in the directory you’re currently in. Once the binary is built, you can run the following command to run cf-terraforming directly from the directory where the binary was built.

./cf-terraforming --help

Step 3: Add to PATH (Optional)

The following is completely optional and might differ depending on the OS you’re on. To make cf-terraforming globally accessible from the terminal, move it to a directory included in your $PATH. For example, you can move it to /usr/local/bin/

sudo mv cf-terraforming /usr/local/bin/

You can also verify if the command is in your $PATH by typing:

cf-terraforming --help

Testing

To ensure changes don't introduce regressions this tool uses an automated test suite consisting of HTTP mocks via go-vcr and Terraform configuration files to assert against. The premise is that we mock the HTTP responses from the Cloudflare API to ensure we don't need to create and delete real resources to test. The Terraform files then allow us to build what the resource structure is expected to look like and once the tool parses the API response, we can compare that to the static file.

Suggested local testing steps:

  1. Create a file with the basic provider configuration (do not commit this file) The version should target the version of the provider. The latest versions are 5.x
cat > main.tf <<EOF
terraform {
  required_providers {
    cloudflare = {
      source = "cloudflare/cloudflare"
      version = "(~> 4 or ~> 5)"    
    }
  }
}
EOF
  1. Initialize terraform
terraform init
  1. Run tests (Cloudflare Install path should be path to repository)
make test

If you want to run a specific test case you can do so with the TESTARGS variable and -run flag

TESTARGS="-run '^TestResourceGeneration/cloudflare_teams_list'" make test

Updating VCR cassettes

Periodically, it is a good idea to recreate the VCR cassettes used in our testing to ensure they haven't drifted from actual responses. To do this, you will need to:

  • Create the appropriate resource in a Cloudflare account/zone you have access to. This is required as overwriting cassettes makes real API requests on your behalf.
  • Invoke the test suite with OVERWRITE_VCR_CASSETTES=true, CLOUDFLARE_DOMAIN=<real domain here>, authentication credentials (CLOUDFLARE_EMAIL, CLOUDFLARE_KEY, CLOUDFLARE_API_TOKEN) and the test you want to update. Example of updating the DNS CAA record test with a zone I own:
  OVERWRITE_VCR_CASSETTES=true \
    CLOUDFLARE_DOMAIN="terraform.cfapi.net" \
    CLOUDFLARE_EMAIL="jb@example.com" \
    CLOUDFLARE_API_KEY="..." \
    TESTARGS="-run '^TestResourceGeneration/cloudflare_record_caa'"  \
    make test
  • Commit your changes and push them via a Pull Request.