Skip to content

Migration

Alex Feel edited this page Aug 5, 2022 · 2 revisions

Who is this guide for?

If you would like to start using the module to manage resources in Cloudflare, this guide will show you how.

Migration options

If you want to start managing your resources with the module, you have several options to do so. However, your next steps will depend on whether you already have resources in Cloudflare and whether you are already using Terraform to manage them.

In general, your approach would be:

  1. If you don't have resources in Cloudflare, you can simply start using the module. See examples and detailed usage information in USAGE.md.
  2. If you have resources in Cloudflare but don't use Terraform to manage them, you can re-create or import your existing resources and start managing them through Terraform using the module.
  3. If you have resources in Cloudflare and are already using Terraform to manage them, you can re-create or move (change resource addresses) your existing resources and start managing them through Terraform using the module.

Note. You can only migrate resources that are supported by the module. You can find the list of supported resources here.

Assumptions

Let's assume you are using the following resources:

Also, in case you are already using Terraform, let's say your configuration looks like this:

terraform {
  required_providers {
    cloudflare = {
      source = "cloudflare/cloudflare"
    }
  }
}

variable "cloudflare_api_token" {
  type      = string
  sensitive = true
}

provider "cloudflare" {
  api_token = var.cloudflare_api_token
}

resource "cloudflare_zone" "acme_com" {
  zone = "acme.com"
}

resource "cloudflare_zone_settings_override" "acme_com" {
  zone_id = cloudflare_zone.acme_com.id

  settings {
    always_online = "off"
    minify {
      css  = "off"
      html = "on"
      js   = "off"
    }
  }
}

resource "cloudflare_zone_dnssec" "acme_com" {
  zone_id = cloudflare_zone.acme_com.id
}

resource "cloudflare_record" "acme_com_a_main" {
  zone_id = cloudflare_zone.acme_com.id

  type    = "A"
  name    = "@"
  value   = "157.131.111.93"
  proxied = true
}

resource "cloudflare_record" "acme_com_a_www" {
  zone_id = cloudflare_zone.acme_com.id

  type    = "A"
  name    = "www"
  value   = "157.131.111.93"
  proxied = true
}

resource "cloudflare_record" "acme_com_mx_1" {
  zone_id = cloudflare_zone.acme_com.id

  type     = "MX"
  name     = "@"
  value    = "mx.acme.com"
  priority = 1
}

resource "cloudflare_record" "acme_com_spf_main" {
  zone_id = cloudflare_zone.acme_com.id

  type  = "TXT"
  name  = "@"
  value = "v=spf1 a mx ip4:192.100.66.0/24 a:mail.sonic.net ip4:64.142.0.0/17 ~all"
  ttl   = 86400
}

resource "cloudflare_page_rule" "forward_naked_domain_to_www" {
  zone_id = cloudflare_zone.acme_com.id

  target = "acme.com/*"
  actions {
    forwarding_url {
      status_code = 301
      url         = "https://www.acme.com/$1"
    }
  }
  priority = 1
}

resource "cloudflare_page_rule" "change_login_page_settings" {
  zone_id = cloudflare_zone.acme_com.id

  target = "acme.com/login"
  actions {
    cache_level         = "bypass"
    disable_performance = true
    security_level      = "high"
  }
  priority = 2
}

The same configuration as above, but using the module would look like this:

terraform {
  required_providers {
    cloudflare = {
      source = "cloudflare/cloudflare"
    }
  }
}

variable "cloudflare_api_token" {
  type      = string
  sensitive = true
}

provider "cloudflare" {
  api_token = var.cloudflare_api_token
}

module "acme_com" {
  source  = "registry.terraform.io/alex-feel/zone/cloudflare"
  # It is recommended to pin a module to a specific version
  version = "x.x.x"

  zone = "acme.com"

  always_online = "off"
  minify = {
    html = "on"
  }

  enable_dnssec = true

  records = [
    {
      record_name = "a_main"
      type        = "A"
      value       = "157.131.111.93"
      proxied     = true
    },
    {
      record_name = "a_www"
      type        = "A"
      name        = "www"
      value       = "157.131.111.93"
      proxied     = true
    },
    {
      record_name = "mx_1"
      type        = "MX"
      value       = "mx.acme.com"
      priority    = 1
    },
    {
      record_name = "spf_main"
      type        = "TXT"
      value       = "v=spf1 a mx ip4:192.100.66.0/24 a:mail.sonic.net ip4:64.142.0.0/17 ~all"
      ttl         = 86400
    }
  ]

  page_rules = [
    {
      page_rule_name = "forward_naked_domain_to_www"
      target         = "acme.com/*"
      actions = {
        forwarding_url = {
          status_code = 301
          url         = "https://www.acme.com/$1"
        }
      }
      priority = 1
    },
    {
      page_rule_name = "change_login_page_settings"
      target         = "acme.com/login"
      actions = {
        cache_level         = "bypass"
        disable_performance = true
        security_level      = "high"
      }
      priority = 2
    }
  ]
}

You have resources in Cloudflare but don't use Terraform to manage them

In this case, you have at least two options:

  1. Deleting and recreating resources. This can potentially result in downtime / unavailability of your infrastructure for some time. This option is the easiest.
  2. Importing resources. This will not affect the availability of your infrastructure or the impact will be minimal. This option is more difficult than the previous one.

There are other options, such as importing only the cloudflare_zone resource, deleting the rest of the resources in the Cloudflare interface and recreating them using the module.

Deleting and recreating resources

This way of migration is quite simple:

  • Delete the resources you want to transfer in the Cloudflare interface. In fact, you will have to delete the entire zone, since this is the main resource. To make sure everything goes well, first try deleting all DNS records and all page rules, and then delete the zone itself.
  • Create configuration for the deleted resources using the module. Of course, you can prepare it ahead of time to minimize potential downtime / unavailability of your infrastructure. Your new configuration may look like the example above.
  • Apply the changes by running the following commands:
$ terraform init
$ terraform plan
$ terraform apply

Your resources will be recreated.

Note. You may find that Terraform wants to apply changes to some resources that were not in your original configuration. This usually refers to the cloudflare_zone_settings_override resource, since the module follows the default values for all settings, as described here.

🎉 All is ready! Your resources are now managed by the module.

Importing resources

  • Create configuration for resources you want to migrate using the module and initialize a working directory containing configuration files by running the following command:
$ terraform init
  • Get resource addresses. You can easily recognize them by running the following command and carefully examining its output:
$ terraform plan

You will get a plan in which Terraform will show that it is going to create new resources. You can easily understand for which resource which address will be assigned. Here is a list of new resource addresses for the above configuration:

New resource address
module.acme_com.cloudflare_page_rule.this["change_login_page_settings"]
module.acme_com.cloudflare_page_rule.this["forward_naked_domain_to_www"]
module.acme_com.cloudflare_record.this["a_main"]
module.acme_com.cloudflare_record.this["a_www"]
module.acme_com.cloudflare_record.this["mx_1"]
module.acme_com.cloudflare_record.this["spf_main"]
module.acme_com.cloudflare_zone.this
module.acme_com.cloudflare_zone_dnssec.this[0]
module.acme_com.cloudflare_zone_settings_override.this

Note. Do not apply changes at this stage, we are not going to make changes right now.

  • Get IDs of all the resources that you have described in the configuration. How you can get IDs of the resources in Cloudflare:
Resource How you can get the ID
Cloudflare page rule Using cf-terraforming or Cloudflare API.
Cloudflare record Using cf-terraforming or Cloudflare API.
Cloudflare Zone On the Overview page of the Cloudflare dashboard, using cf-terraforming or Cloudflare API.
Cloudflare Zone DNSSEC Use the Cloudflare Zone settings ID (zone ID) as the resource ID.
Cloudflare Zone settings The resource does not support import, just describe your current settings in the configuration.
  • Import the resources with the following command (for each resource):
$ terraform import RESOURCE_ADDRESS RESOURCE_ID

Note. To import some resources, you must use a RESOURCE_ID, which is a combination of a zone ID and a resource ID in the following form:

ZONE_ID/RESOURCE_ID

Note. For resources created by the module with the count or for_each meta-arguments (there are a few), in some shells you may need to quote or escape some characters in the address to literally pass the address of such resources to Terraform. More information with examples for different command shells can be found here.

Thus, the complete list of commands will be as follows (character escaping is used for PowerShell, be sure to check the information for your shell in the official help, starting from here):

$ terraform import 'module.acme_com.cloudflare_page_rule.this[\"change_login_page_settings\"]' ZONE_ID/RESOURCE_ID
$ terraform import 'module.acme_com.cloudflare_page_rule.this[\"forward_naked_domain_to_www\"]' ZONE_ID/RESOURCE_ID
$ terraform import 'module.acme_com.cloudflare_record.this[\"a_main\"]' ZONE_ID/RESOURCE_ID
$ terraform import 'module.acme_com.cloudflare_record.this[\"a_www\"]' ZONE_ID/RESOURCE_ID
$ terraform import 'module.acme_com.cloudflare_record.this[\"mx_1\"]' ZONE_ID/RESOURCE_ID
$ terraform import 'module.acme_com.cloudflare_record.this[\"spf_main\"]' ZONE_ID/RESOURCE_ID
$ terraform import module.acme_com.cloudflare_zone.this RESOURCE_ID
$ terraform import 'module.acme_com.cloudflare_zone_dnssec.this[0]' RESOURCE_ID
  • Run the following command to make sure everything is in order and Terraform is not planning to create or destroy new resources:
$ terraform plan

Note. You may find that Terraform wants to replace or update some resources. Resource replacement may be required, e.g. for cloudflare_record, you can safely apply such changes. The cloudflare_zone_settings_override resource needs to be created because it was not imported, you must approve the creation of this resource in order to manage it with the module.

  • Apply the changes if they suit you. You may want to update your configuration before doing so. Once you are satisfied with the output of the terraform plan command, run the following command:
$ terraform apply

🎉 All is ready! Your resources are now managed by the module.

You have resources in Cloudflare and are already using Terraform to manage them

In this case, you have at least two options:

  1. Destroying and recreating resources. This can potentially result in downtime / unavailability of your infrastructure for some time. This option is the easiest.
  2. Moving resources to new resource addresses. This will not affect the availability of your infrastructure, since no actions related to destroying or creation will be performed. This option is more difficult than the previous one.

There are other options, such as moving only the cloudflare_zone resource, deleting the rest of the resources in the Cloudflare interface and recreating them using the module.

Destroying and recreating resources

This way of migration is quite simple:

  • Remove the resources you want to migrate from your configuration.
  • Apply the changes by running the following commands:
$ terraform plan
$ terraform apply
  • Add the destroyed resources to your configuration, but now using the module. Of course, you can prepare it ahead of time to minimize potential downtime / unavailability of your infrastructure. For the original configuration from the example above, your new configuration will look like the example above.
  • Apply the changes by running the following commands:
$ terraform init
$ terraform plan
$ terraform apply

Your resources will be recreated.

Note. You may find that Terraform wants to apply changes to some resources that were not in your original configuration. This usually refers to the cloudflare_zone_settings_override resource, since the module follows the default values for all settings, as described here.

🎉 All is ready! Your resources are now managed by the module.

Moving resources to new resource addresses

  • Add to your configuration resources you want to migrate using the module.
  • Delete the old configuration, you won't need it anymore.

Note. If you have used priorities for page rules, specify the same priorities for those rules when moving them to the module, despite the issue documented here.

  • Get the current addresses of the resources and temporarily save those addresses that are related to the resources you want to migrate. To get a list of all resources, run the following command:
$ terraform state list

For the original config above, you will get the following result:

cloudflare_page_rule.change_login_page_settings
cloudflare_page_rule.forward_naked_domain_to_www
cloudflare_record.acme_com_a_main
cloudflare_record.acme_com_a_www
cloudflare_record.acme_com_mx_1
cloudflare_record.acme_com_spf_main
cloudflare_zone.acme_com
cloudflare_zone_dnssec.acme_com
cloudflare_zone_settings_override.acme_com

You can migrate all of these resources as they are all supported by the module.

  • Get new resource addresses. You can easily recognize them by running the following command and carefully examining its output:
$ terraform plan

You will get a plan in which Terraform will show that it is going to create new resources. You can easily understand for which resource which address will be assigned. Here is a list of old and new resource addresses for the above configurations:

Old resource address New resource address
cloudflare_page_rule.change_login_page_settings module.acme_com.cloudflare_page_rule.this["change_login_page_settings"]
cloudflare_page_rule.forward_naked_domain_to_www module.acme_com.cloudflare_page_rule.this["forward_naked_domain_to_www"]
cloudflare_record.acme_com_a_main module.acme_com.cloudflare_record.this["a_main"]
cloudflare_record.acme_com_a_www module.acme_com.cloudflare_record.this["a_www"]
cloudflare_record.acme_com_mx_1 module.acme_com.cloudflare_record.this["mx_1"]
cloudflare_record.acme_com_spf_main module.acme_com.cloudflare_record.this["spf_main"]
cloudflare_zone.acme_com module.acme_com.cloudflare_zone.this
cloudflare_zone_dnssec.acme_com module.acme_com.cloudflare_zone_dnssec.this[0]
cloudflare_zone_settings_override.acme_com module.acme_com.cloudflare_zone_settings_override.this

Note. Do not apply changes at this stage, we are not going to make changes right now.

  • Migrate the resources, changing their current addresses to the new ones, with the following command (for each resource):
$ terraform state mv OLD_RESOURCE_ADDRESS NEW_RESOURCE_ADDRESS

Note. For resources created by the module with the count or for_each meta-arguments (there are a few), in some shells you may need to quote or escape some characters in the address to literally pass the address of such resources to Terraform. More information with examples for different command shells can be found here.

Thus, the complete list of commands will be as follows (character escaping is used for PowerShell, be sure to check the information for your shell in the official help, starting from here):

$ terraform state mv cloudflare_page_rule.change_login_page_settings 'module.acme_com.cloudflare_page_rule.this[\"change_login_page_settings\"]'
$ terraform state mv cloudflare_page_rule.forward_naked_domain_to_www 'module.acme_com.cloudflare_page_rule.this[\"forward_naked_domain_to_www\"]'
$ terraform state mv cloudflare_record.acme_com_a_main 'module.acme_com.cloudflare_record.this[\"a_main\"]'
$ terraform state mv cloudflare_record.acme_com_a_www 'module.acme_com.cloudflare_record.this[\"a_www\"]'
$ terraform state mv cloudflare_record.acme_com_mx_1 'module.acme_com.cloudflare_record.this[\"mx_1\"]'
$ terraform state mv cloudflare_record.acme_com_spf_main 'module.acme_com.cloudflare_record.this[\"spf_main\"]'
$ terraform state mv cloudflare_zone.acme_com module.acme_com.cloudflare_zone.this
$ terraform state mv cloudflare_zone_dnssec.acme_com 'module.acme_com.cloudflare_zone_dnssec.this[0]'
$ terraform state mv cloudflare_zone_settings_override.acme_com module.acme_com.cloudflare_zone_settings_override.this
  • Run the following command to make sure everything is in order and Terraform is not planning to create or destroy new resources:
$ terraform plan

Note. You may find that Terraform wants to update some resources. This usually refers to the cloudflare_zone_settings_override resource, since the module follows the default values for all settings, as described here.

  • Apply the changes if they suit you. You may want to update your configuration before doing so. Once you are satisfied with the output of the terraform plan command, run the following command:
$ terraform apply

🎉 All is ready! Your resources are now managed by the module.