topics: cloudflare , terraform (tidbit)

Generate Cloudflare Terraform with cf-terraforming

Cloudflare have developed an open-source tool cf-terraforming which provides a mechanism to export pre-existing Cloudfare service configurations as Terraform HCL. The generated HCL may then be utilised as part of an IaC approach to maintaining usage of Cloudflare services.

At the time of writing there are no precompiled binaries available as a release via the Github repository.

With Go installed, cf-terraform can be fetched and compiled using go get.

# Note this will build the very latest commit of master
go get -u github.com/cloudflare/cf-terraforming/...

The binary cf-terraforming should then be available in your go/bin directory, possibly ~/go/bin/cf-terraforming

Generating Terraform Configuration

cf-terraforming requires credentials for authentication against Cloudflare API’s passed as cli flags:

The ‘all’ command iterates all supported resource types outputting the derived Cloudflare Provider HCL config.

# env.vars
export TF_VAR_cloudflare_email="user@domain.tld"
export TF_VAR_cloudflare_api_key="somesecretkey"
export TF_VAR_cloudflare_account_id="theaccountid"
source env.vars
# run 'all' to generate Terraform configuration for all resources available
# (optionally) add -v to increase verbosity
cf-terraforming \
    -e $TF_VAR_cloudflare_email \
    -k $TF_VAR_cloudflare_api_key \
    -a $TF_VAR_cloudflare_account_id \
    all

Depending on the permissions associated with the credentials and account type limitations, there may be error messages mixed in with the output such as:

ERRO[0005] Insufficient permissions for accessing zone ID=

To tidy up execution, stderr and stdout can be piped seperately:

cf-terraforming \
    -e $TF_VAR_cloudflare_email \
    -k $TF_VAR_cloudflare_api_key \
    -a $TF_VAR_cloudflare_account_id \
    all 2>> error.log > all.tf   

all.tf should now contain Terraform resource blocks with attributes specified as a reflection of any pre-existing configuration.

# Sumarised all.tf content
resource "cloudflare_account_member" "account_member_..." { ... }
resource "cloudflare_page_rule" "page_rule_..." { ... }
resource "cloudflare_record" "A_..." { ... }
resource "cloudflare_zone" "..." { ... }
resource "cloudflare_zone_settings_override" "zone_settings_override_..." { ... }

Verifying the Generated Configuration

Add a new file main.tf and include the pre-requisite configuration required by the Cloudflare Terraform provider. The variable blocks provide a mechanism to dynamically control the values used to configure the provider.

# main.tf
variable "cloudflare_email" {
  description = "email address associated with account"
}
variable "cloudflare_api_key" {
  description = "api key to authenticate with"
}
variable "cloudflare_account_id" {
  description = "api key to authenticate with"
}
provider "cloudflare" {
  version = "~> 2.0"
  email   = var.cloudflare_email
  api_key = var.cloudflare_api_key
  account_id = var.cloudflare_account_id
}

There are a few ways to configure the variables; we picked one earlier with the odd TF_VAR_ prefixed environment variables. Terraform will map a TF_VAR_cloudflare_email environment variable to the cloudflare_email variable for us automatically.

With the configuration in place, initialise the Terraform configuration and have a look at what changes Terraform currently determines are required:

# initialize Terraform configuration
terraform init
# generate plan
terraform plan

Some manual intervention was required when tested. There appears to be an inconsistency between provider vs.1 and vs.2 configuration.

Which provider version is the target ? It looked most like vs. 2 to me.

tls_1_2_only is deprecated in favour of min_tls_version

I noticed an advanced_ddos option generated that was removed from the provider.

Using the Generated Configuration

Without ‘state’ as a reference, Terraform will expect to create resources rather than interact with anything pre-existing. State can be imported using terraform import, or by executing an experimental feature of the cf-terraforming command --tfstate.

cf-terraforming \
    -e $TF_VAR_cloudflare_email \
    -k $TF_VAR_cloudflare_api_key \
    -a $TF_VAR_cloudflare_account_id \
    all --tfstate 2>>error.log > terraform.tfstate

At the time of writing the experimental state generation functionality was too limited to be useful. Not all resources are supported and I found some that had issues with compatibility.

To fully import state I took the manual approach of looking up import syntax per resource, executing it by hand.

To manually import cloudflare_account_member for example:

# Account members can be imported using a composite ID formed of account ID and account member ID, e.g.   
$ terraform import cloudflare_account_member.example_user d41d8cd98f00b204e9800998ecf8427e/b58c6f14d292556214bd64909bcdb118

Curiously, importing a “cloudflare_zone_settings_override” resource was not supported by the Cloudflare Provider.