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:
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
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>"
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.
- 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.
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 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.
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
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.
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"
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.
Any resources not listed may have known issues. The HCL config may still be generated but might need manual modifications.
Resource Type | Identifier Type | CLI Flags Example |
---|---|---|
cloudflare_account | account | |
cloudflare_account_member | account | |
cloudflare_account_subscription | account | |
cloudflare_address_map | account | |
cloudflare_api_shield_discovery_operation | zone | |
cloudflare_api_shield_operation | zone | |
cloudflare_api_shield_operation_schema_validation_settings | zone | cloudflare_api_shield_operation_schema_validation_settings=8255d5da-5a46-4928-ad00-01de7d48c1e7 |
cloudflare_api_shield_schema | zone | |
cloudflare_api_shield_schema_validation_settings | zone | |
cloudflare_argo_smart_routing | zone | |
cloudflare_argo_tiered_caching | zone | |
cloudflare_authenticated_origin_pulls | zone | cloudflare_authenticated_origin_pulls=jotsqcjaho.terraform.cfapi.net |
cloudflare_authenticated_origin_pulls_certificate | zone | |
cloudflare_bot_management | zone | |
cloudflare_calls_sfu_app | account | |
cloudflare_calls_turn_app | account | |
cloudflare_certificate_pack | zone | |
cloudflare_content_scanning_expression | zone | |
cloudflare_custom_hostname | zone | |
cloudflare_custom_hostname_fallback_origin | zone | |
cloudflare_d1_database | account | |
cloudflare_dns_firewall | account | |
cloudflare_dns_record | zone | |
cloudflare_dns_zone_transfers_acl | account | |
cloudflare_dns_zone_transfers_incoming | zone | |
cloudflare_dns_zone_transfers_outgoing | zone | |
cloudflare_dns_zone_transfers_peer | account | |
cloudflare_dns_zone_transfers_tsig | account | |
cloudflare_email_routing_address | account | |
cloudflare_email_routing_catch_all | zone | |
cloudflare_email_routing_dns | zone | |
cloudflare_email_routing_rule | zone | |
cloudflare_email_routing_settings | zone | |
cloudflare_email_security_block_sender | account | |
cloudflare_email_security_impersonation_registry | account | |
cloudflare_email_security_trusted_domains | account | |
cloudflare_filter | zone | |
cloudflare_healthcheck | zone | |
cloudflare_hostname_tls_setting | zone | cloudflare_hostname_tls_setting=ciphers,min_tls_version |
cloudflare_keyless_certificate | zone | |
cloudflare_leaked_credential_check | zone | |
cloudflare_leaked_credential_check_rule | zone | |
cloudflare_list | account | |
cloudflare_list_item | account | cloudflare_list_item=2a4b8b2017aa4b3cb9e1151b52c81d22 |
cloudflare_load_balancer | zone | |
cloudflare_load_balancer_monitor | account | |
cloudflare_load_balancer_pool | account | |
cloudflare_logpull_retention | zone | |
cloudflare_logpush_job | account or zone | |
cloudflare_magic_wan_static_route | account | |
cloudflare_managed_transforms | zone | |
cloudflare_mtls_certificate | account | |
cloudflare_notification_policy | account | |
cloudflare_notification_policy_webhooks | account | |
cloudflare_observatory_scheduled_test | zone | cloudflare_observatory_scheduled_test=terraform.cfapi.net/thyygxveip |
cloudflare_origin_ca_certificate | zone | |
cloudflare_page_rule | zone | |
cloudflare_page_shield_policy | zone | |
cloudflare_pages_domain | account | cloudflare_pages_domain=ykfjmcgpfs |
cloudflare_pages_project | account | |
cloudflare_queue | account | |
cloudflare_queue_consumer | account | cloudflare_queue_consumer=2dde6ac405cd457c9ce59dc4bda20c65 |
cloudflare_r2_bucket | account | |
cloudflare_r2_custom_domain | account | cloudflare_r2_custom_domain=jb-test-bucket,bnfywlzwpt |
cloudflare_r2_managed_domain | account | cloudflare_r2_managed_domain=jb-test-bucket,bnfywlzwpt |
cloudflare_rate_limit | zone | |
cloudflare_regional_hostname | zone | |
cloudflare_regional_tiered_cache | zone | |
cloudflare_registrar_domain | account | |
cloudflare_ruleset | account or zone | |
cloudflare_snippet_rules | zone | |
cloudflare_snippets | zone | |
cloudflare_spectrum_application | zone | |
cloudflare_stream | account | |
cloudflare_stream_key | account | |
cloudflare_stream_live_input | account | |
cloudflare_stream_watermark | account | |
cloudflare_stream_webhook | account | |
cloudflare_tiered_cache | zone | |
cloudflare_total_tls | zone | |
cloudflare_turnstile_widget | account | |
cloudflare_url_normalization_settings | zone | |
cloudflare_user | account | |
cloudflare_waiting_room | account or zone | |
cloudflare_waiting_room_event | zone | cloudflare_waiting_room_event=e7f9e4c190ea8d6c66cab32ac110f39a |
cloudflare_waiting_room_rules | zone | cloudflare_waiting_room_rules=8bbd1b13450f6c63ab6ab4e08a63762d |
cloudflare_waiting_room_settings | zone | |
cloudflare_web3_hostname | zone | |
cloudflare_web_analytics_rule | account | cloudflare_web_analytics_rule=2fa89d8f-35f7-49ef-87d3-f24e866a5d5e |
cloudflare_web_analytics_site | account | |
cloudflare_workers_cron_trigger | account | cloudflare_workers_cron_trigger=script_2 |
cloudflare_workers_custom_domain | account | |
cloudflare_workers_deployment | account | cloudflare_workers_deployment=script_2 |
cloudflare_workers_for_platforms_dispatch_namespace | account | |
cloudflare_workers_kv_namespace | account | |
cloudflare_workers_script_subdomain | account | cloudflare_workers_script_subdomain=accounts |
cloudflare_zero_trust_access_application | account or zone | |
cloudflare_zero_trust_access_custom_page | account | |
cloudflare_zero_trust_access_group | account or zone | |
cloudflare_zero_trust_access_identity_provider | account or zone | |
cloudflare_zero_trust_access_infrastructure_target | account | |
cloudflare_zero_trust_access_key_configuration | account | |
cloudflare_zero_trust_access_mtls_certificate | account or zone | |
cloudflare_zero_trust_access_mtls_hostname_settings | account or zone | |
cloudflare_zero_trust_access_policy | account | |
cloudflare_zero_trust_access_service_token | account or zone | |
cloudflare_zero_trust_access_short_lived_certificate | account or zone | |
cloudflare_zero_trust_access_tag | account | |
cloudflare_zero_trust_device_custom_profile | account | |
cloudflare_zero_trust_device_default_profile | account | |
cloudflare_zero_trust_device_default_profile_certificates | zone | |
cloudflare_zero_trust_device_default_profile_local_domain_fallback | account | |
cloudflare_zero_trust_device_managed_networks | account | |
cloudflare_zero_trust_device_posture_integration | account | |
cloudflare_zero_trust_device_posture_rule | account | |
cloudflare_zero_trust_dex_test | account | |
cloudflare_zero_trust_dlp_custom_profile | account | cloudflare_zero_trust_dlp_custom_profile=38f45ad8-476e-4b56-ad16-42f364250802 |
cloudflare_zero_trust_dlp_dataset | account | |
cloudflare_zero_trust_dlp_predefined_profile | account | cloudflare_zero_trust_dlp_predefined_profile=c8932cc4-3312-4152-8041-f3f257122dc4,56a8c060-01bb-4f89-ba1e-3ad42770a342 |
cloudflare_zero_trust_dns_location | account | |
cloudflare_zero_trust_gateway_certificate | account | |
cloudflare_zero_trust_gateway_policy | account | |
cloudflare_zero_trust_gateway_proxy_endpoint | account | |
cloudflare_zero_trust_gateway_settings | account | |
cloudflare_zero_trust_list | account | |
cloudflare_zero_trust_organization | account or zone | |
cloudflare_zero_trust_risk_behavior | account | |
cloudflare_zero_trust_risk_scoring_integration | account | |
cloudflare_zero_trust_tunnel_cloudflared | account | |
cloudflare_zero_trust_tunnel_cloudflared_config | account | cloudflare_zero_trust_tunnel_cloudflared_config=285f508d-d6ef-4ce4-9293-983d5bdc269e |
cloudflare_zero_trust_tunnel_cloudflared_route | account | |
cloudflare_zero_trust_tunnel_cloudflared_virtual_network | account | |
cloudflare_zone | zone | |
cloudflare_zone_cache_reserve | zone | |
cloudflare_zone_cache_variants | zone | |
cloudflare_zone_dnssec | zone | |
cloudflare_zone_lockdown | zone | |
cloudflare_zone_setting | zone | cloudflare_zone_setting=always_online,cache_level |
Any resources not listed may have known issues or may not yet support import.
Resource Type | Identifier Type | CLI Flags Example |
---|---|---|
cloudflare_account | account | |
cloudflare_account_member | account | |
cloudflare_address_map | account | |
cloudflare_api_shield_operation | zone | |
cloudflare_bot_management | zone | |
cloudflare_certificate_pack | zone | |
cloudflare_custom_hostname | zone | |
cloudflare_custom_hostname_fallback_origin | zone | |
cloudflare_d1_database | account | |
cloudflare_dns_firewall | account | |
cloudflare_dns_record | zone | |
cloudflare_dns_zone_transfers_acl | account | |
cloudflare_dns_zone_transfers_incoming | zone | |
cloudflare_dns_zone_transfers_outgoing | zone | |
cloudflare_dns_zone_transfers_peer | account | |
cloudflare_dns_zone_transfers_tsig | account | |
cloudflare_email_routing_address | account | |
cloudflare_email_routing_catch_all | zone | |
cloudflare_email_routing_dns | zone | |
cloudflare_email_routing_rule | zone | |
cloudflare_email_routing_settings | zone | |
cloudflare_email_security_block_sender | account | |
cloudflare_email_security_impersonation_registry | account | |
cloudflare_email_security_trusted_domains | account | |
cloudflare_filter | zone | |
cloudflare_healthcheck | zone | |
cloudflare_hostname_tls_setting | zone | cloudflare_hostname_tls_setting=ciphers,min_tls_version |
cloudflare_keyless_certificate | zone | |
cloudflare_list | account | |
cloudflare_list_item | account | cloudflare_list_item=2a4b8b2017aa4b3cb9e1151b52c81d22 |
cloudflare_load_balancer | zone | |
cloudflare_load_balancer_monitor | account | |
cloudflare_load_balancer_pool | account | |
cloudflare_logpush_job | account or zone | |
cloudflare_managed_transforms | zone | |
cloudflare_mtls_certificate | account | |
cloudflare_notification_policy | account | |
cloudflare_notification_policy_webhooks | account | |
cloudflare_origin_ca_certificate | zone | |
cloudflare_page_rule | zone | |
cloudflare_page_shield_policy | zone | |
cloudflare_pages_domain | account | cloudflare_pages_domain=ykfjmcgpfs |
cloudflare_pages_project | account | |
cloudflare_queue | account | |
cloudflare_r2_bucket | account | |
cloudflare_r2_custom_domain | account | cloudflare_r2_custom_domain=jb-test-bucket,bnfywlzwpt |
cloudflare_r2_managed_domain | account | cloudflare_r2_managed_domain=jb-test-bucket,bnfywlzwpt |
cloudflare_rate_limit | zone | |
cloudflare_regional_hostname | zone | |
cloudflare_regional_tiered_cache | zone | |
cloudflare_ruleset | account or zone | |
cloudflare_spectrum_application | zone | |
cloudflare_tiered_cache | zone | |
cloudflare_total_tls | zone | |
cloudflare_turnstile_widget | account | |
cloudflare_url_normalization_settings | zone | |
cloudflare_waiting_room | zone | |
cloudflare_waiting_room_event | zone | cloudflare_waiting_room_event=e7f9e4c190ea8d6c66cab32ac110f39a |
cloudflare_waiting_room_rules | zone | cloudflare_waiting_room_rules=8bbd1b13450f6c63ab6ab4e08a63762d |
cloudflare_waiting_room_settings | zone | |
cloudflare_web3_hostname | zone | |
cloudflare_web_analytics_rule | account | cloudflare_web_analytics_rule=2fa89d8f-35f7-49ef-87d3-f24e866a5d5e |
cloudflare_web_analytics_site | account | |
cloudflare_workers_custom_domain | account | |
cloudflare_workers_for_platforms_dispatch_namespace | account | |
cloudflare_workers_kv_namespace | account | |
cloudflare_zero_trust_access_application | account or zone |
Any resources not listed are currently not supported.
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
To clone the cf-terraforming repository from GitHub, run the following command:
git clone https://github.com/cloudflare/cf-terraforming.git
cd 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
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
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:
- 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
- Initialize terraform
terraform init
- 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
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.