Skip to content

Terraform module to provision a CloudFlare zone with DNS records

License

Notifications You must be signed in to change notification settings

Infrastrukturait/terraform-cloudflare-zone

Repository files navigation

terraform-cloudflare-zone

WeSupportUkraine

About

Terraform module to provision a CloudFlare zone with DNS record managment

License

License: MIT

The MIT License (MIT)

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

Source: <https://opensource.org/licenses/MIT>

See LICENSE for full details.

Authors

Submodules

Submodules included to root module that can be called independent:

  • single-record - Root module creates an object with a full zone and object with all records, this small sub-module is to facilitate the creation of a single record to an existing zone.

Notes

  • The name and the data.name argument values default to @ (root). This is actually the default behavior, but only for the value of the data.name argument when you are not using the module.
  • The data argument is fully supported. The value argument takes precedence over the data argument to avoid errors if two arguments are accidentally given at the same time, since only one of them can be given at the same time.
  • The ttl argument value defaults to 1 (automatic). This is actually the default behavior.
  • The ttl argument value is forced to 1 (automatic), regardless of explicitly set value, if you set the proxied argument value to true.
  • The proxied argument value defaults to false. This is actually the default behavior. You must explicitly set this argument value to true for the records that you want to proxy through Cloudflare.
  • The proxied argument value is forced to false for unsupported record types, regardless of explicitly set value.
  • The proxied argument value is forced to false for wildcard records for non-enterprise plans, regardless of explicitly set value, because non-enterprise customers can create but not proxy wildcard records.
  • To create only each record without create whole zone, you can just use simple submodule single-record.

Documentation

Requirements

Name Version
terraform >= 0.14
cloudflare >= 3.23

Modules

No modules.

Resources

Name Type
cloudflare_record.this resource
cloudflare_zone.this resource
cloudflare_zone_dnssec.this resource

Inputs

Name Description Type Default Required
account_id Account ID to manage the zone resource in. You can get more information on how to find account_id at this page. string "" no
enable_dnssec Enable or disable DNSSEC. bool false no
jump_start Automatically attempt to fetch existing DNS records on creation. Ignored after zone is created. bool false no
paused Indicates if the zone is only using Cloudflare DNS services. A true value means the zone will not receive security or performance benefits. bool false no
plan The desired plan for the zone. Can be updated once the one is created. Changing this value will create/cancel associated subscriptions.
Possible values: free, partners_free, pro, partners_pro, business, partners_business, enterprise, partners_enterprise."
You can get more information about available plans at this page.
string "free" no
records Zone's DNS records.
Possible values:
* for the type argument: "A", "AAAA", "CAA", "CERT", "CNAME", "DNSKEY", "DS", "HTTPS", "LOC", "MX", "NAPTR", "NS", "PTR", "SMIMEA", "SPF", "SRV", "SSHFP", "SVCB", "TLSA", "TXT", "URI".
*for the priority argument: between 0 and 65535.\nPossible values for the ttl argument: between 60 and 86400, or 1 for automatic."
list(object({
record_name = string
type = string
name = optional(string)
value = optional(string)
data = optional(object({
algorithm = optional(number)
altitude = optional(number)
certificate = optional(string)
content = optional(string)
digest = optional(string)
digest_type = optional(number)
fingerprint = optional(string)
flags = optional(string)
key_tag = optional(number)
lat_degrees = optional(number)
lat_direction = optional(string)
lat_minutes = optional(number)
lat_seconds = optional(number)
long_degrees = optional(number)
long_direction = optional(string)
long_minutes = optional(number)
long_seconds = optional(number)
matching_type = optional(number)
name = optional(string)
order = optional(number)
port = optional(number)
precision_horz = optional(number)
precision_vert = optional(number)
preference = optional(number)
priority = optional(number)
proto = optional(string)
protocol = optional(number)
public_key = optional(string)
regex = optional(string)
replacement = optional(string)
selector = optional(number)
service = optional(string)
size = optional(number)
tag = optional(string)
target = optional(string)
type = optional(number)
usage = optional(number)
value = optional(string)
weight = optional(number)
}))
priority = optional(number)
ttl = optional(number)
proxied = optional(bool)
allow_overwrite = optional(bool)
}))
[] no
type A full zone implies that DNS is hosted with Cloudflare. A partial zone is typically a partner-hosted zone or a CNAME setup. Possible values: full, partial.
To learn more and choose the right configuration for you, see the documentation about full or partial CNAME setups.
string "full" no
zone The DNS zone name which will be added, e.g. example.com. string n/a yes

Outputs

Name Description
algorithm Zone DNSSEC algorithm.
digest Zone DNSSEC digest.
digest_algorithm Digest algorithm use for Zone DNSSEC.
digest_type Digest Type for Zone DNSSEC.
dnssec_status The status of the Zone DNSSEC.
ds DS for the Zone DNSSEC.
flags Zone DNSSEC flags.
key_tag Key Tag for the Zone DNSSEC.
key_type Key type used for Zone DNSSEC.
meta Map of booleans, indicating some zone statuses or flags.
modified_on Zone DNSSEC updated time.
name_servers Cloudflare-assigned name servers. This is only populated for zones that use Cloudflare DNS.
public_key Public Key for the Zone DNSSEC.
record_created_on The RFC3339 timestamp of when the records were created.
record_hostnames The FQDN of the records.
record_ids The record IDs.
record_metadata A key-value map of string metadata Cloudflare associates with the records.
record_modified_on The RFC3339 timestamp of when the records were last modified.
record_proxiable Shows whether these records can be proxied, must be true if setting proxied=true.
status Status of the zone. Valid values: active, pending, initializing, moved, deleted, deactivated.
vanity_name_servers List of Vanity Nameservers (if set).
verification_key Contains the TXT record value to validate domain ownership. This is only populated for zones of type partial.
zone_id The zone ID.

Examples

module "zone" {
  source = "../.."

  zone = var.zone

  records = [
    {
      name  = "bastion-example"
      value = "1.1.1.1"
      type  = "A"
      ttl   = 1
    },
    {
      name  = "api-example"
      value = "1.1.1.1"
      type  = "A"
      ttl   = 1
    }
  ]
}

Importing existing zone to IaaC

if the domain exists and already has records, don't worry about importing it into terraform using this module.

In the first step you need to create a zone to tfstate:

terraform import cloudflare_zone.example <zone_id>

according to official documentation.

When we have our zone in tfstate, we have 2 steps to done.

  1. As first, we can run our helper bash script to generate json. A bash script dump records from exists zone DNS to json, just copy the generated json to the records variable. Script require CLOUDFLARE_API_TOKEN as a environment variable and domain name as a parameter.

For Example:

CLOUDFLARE_API_TOKEN=1234567890abcdef1234567890abcdef; bin/import_zone.sh "acme.sh"
  1. The second step is more complex because you need to import existing records into your tfstate. To make it as simple as possible, you can use a our hook:
#!/bin/bash

DOMAIN="$1"

zoneid=$(curl -s -X GET "https://api.cloudflare.com/client/v4/zones?name=$DOMAIN" \
  -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \
  -H "Content-Type: application/json" | jq \
  -r '{"result"}[] | .[0] | .id')


records=$(curl -s -L -X GET "https://api.cloudflare.com/client/v4/zones/${zoneid}/dns_records" \
     -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \
     -H "Content-Type:application/json" | jq  -r '.result[] | "\(.name)_\(.type)_\(.id)|\(.id)"')


for i in $records
do
  s=$(echo $i | awk -F'|' '{print $1}')
  t=$(echo $i | awk -F'|' '{print $2}')
  terraform import cloudflare_record.this[\"${s}\"] ${zoneid}/${t}
done

This script is fully compatible with our bin/import_zone.sh script and creates the same style object's names in tfstate. As previously, hook require CLOUDFLARE_API_TOKEN as a environment variable and domain name as a parameter.

To more information about importing cloudflare_record please visit to official documentation.