Post

Deploy and Manage your site on cloudflare with Terraform

previously I’ve published guide on deploying your site on vercel with terraform. Now let’s deploy it on cloudflare with terraform. I’m running this blog with Hugo static site generator and manage my DNS with Cloudflare. Managing it on GUI may not be that challenging but it’s annoying and what the state of the site is always unknown. So, like any other DevOps engineer do, I’m managing my blog with terraform. With the terraform state will always be same across all the devices and it’ll be easy to manage. I’m also using terraform cloud for this but let’s talk about that on another day.

Installing Terraform

Follow the instructions based on the your operating system.

Windows

1
winget install --id Hashicorp.Terraform

Debian

1
2
3
wget -O- https://apt.releases.hashicorp.com/gpg | gpg --dearmor | sudo tee /usr/share/keyrings/hashicorp-archive-keyring.gpg
echo "deb [signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] https://apt.releases.hashicorp.com $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/hashicorp.list
sudo apt update && sudo apt install terraform

RHEL

1
2
3
sudo yum install -y yum-utils
sudo yum-config-manager --add-repo https://rpm.releases.hashicorp.com/RHEL/hashicorp.repo
sudo yum -y install terraform

MacOS

1
2
brew tap hashicorp/tap
brew install hashicorp/tap/terraform

open terminal and test it with terraform version

you should see something like this.

1
2
Terraform v1.4.6
on linux_amd64

Providers

Providers are interfaces that interact with their API and maintain the architecture.

Here we are using Vercel and Cloudflare providers. We can keep all our terraform configuration in single file or keep them separate.

1
2
3
4
5
6
7
8
9
terraform {
  required_version= ">= 1.3.0"
  required_providers {
    cloudflare= {
      source  = "cloudflare/cloudflare"
      version = "4.6.0"
    }
  }
}

To communicate with the Cloudflare, you can either create a variable and pass on the Auth tokens or You can use environment variable.

Authenticate with variables

1
2
3
4
5
6
7
8
provider "cloudflare" {
  api_token= var.cloudflare_api_token
}

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

Account ID

Cloudflare provider requires account id to be added to every config. We can use cloudflare_accounts data sources.

1
2
3
data "cloudflare_accounts" "cloudflare_account_data" {
  name= "KD Puvvadi"
}

Cloudflare Pages Project

First create a project on vercel with cloudflare_pages_project,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
esource "cloudflare_pages_project" "blog_pages_project" {
  account_id       = data.cloudflare_accounts.cloudflare_account_data.accounts[0].id
  name             = "blog"
  production_branch= "main"

  source {
    type= "github"
    config {
      owner                        = "kdpuvvadi"
      repo_name                    = "blog"
      production_branch            = "main"
      pr_comments_enabled          = true
      deployments_enabled          = true
      production_deployment_enabled= true
      preview_deployment_setting   = "all"
      preview_branch_includes      = ["*"]
      preview_branch_excludes      = ["main", "prod"]
    }
  }

  build_config {
    build_command      = "hugo --gc --minify"
    destination_dir    = "public"
    root_dir           = ""
  }

  deployment_configs {
    preview {
      environment_variables= {
        HUGO_VERSION= "0.111.0"
        NODE_VERSION= "16.20.0"
      }
      fail_open= true
    }
    production {
      environment_variables= {
        HUGO_VERSION= "0.111.0"
        NODE_VERSION= "16.20.0"
      }
      fail_open= true
    }
  }
}

Here I’m using Hugo for testing. Any framework can be used from NextJs to just a single page html page. Also, git repo should be provided. GitHub, Gitlab or Bitbucket can be used.

Add Domain to the project

Add custom domain to the project with cloudflare_pages_domain

1
2
3
4
5
resource "cloudflare_pages_domain" "cloudflare_blog_domain" {
  account_id  = data.cloudflare_accounts.cloudflare_account_data.accounts[0].id
  project_name= cloudflare_pages_project.blog_pages_project.name
  domain      = "blog.puvvadi.me"
}

Add record on Cloudflare

Limitation of the cloudflare_pages_domain is it does not add CNAME record to your zone.

To add a record to the Cloudflare, Zone ID is required. Zone ID can be obtained cloudflare_zones data source.

1
2
3
4
5
data "cloudflare_zones" "zone_puvvadi_me" {
  filter {
    name= "puvvadi.me"
  }
}

Zone ID is available at data.cloudflare_zones.get_zone_data.zones[0].id.

Add DNS record on Cloudflare to point to the site with cloudflare_record. Deployment URL is available from deployment config at cloudflare_pages_project.blog_pages_project.subdomain.

1
2
3
4
5
6
7
8
9
resource "cloudflare_record" "cloudflare_blog_record" {
  zone_id        = data.cloudflare_zones.zone_puvvadi_me.zones[0].id
  name           = "blog"
  value          = cloudflare_pages_project.blog_pages_project.subdomain
  type           = "CNAME"
  proxied        = true
  ttl            = 1
  allow_overwrite= true
}

Initialize

To Initialize the terraform and install the providers, run

1
terraform init

Terraform will install the providers and required module.

Validation

Before running the configuration, you should validate it to check everything is sound and good.

1
terraform validate

Output should looks like this

1
2
3
$ terraform validate
Success! The configuration is valid.
Plan and Apply

Planning

Before deploying plan can be created with plan argument and can be saved.

1
terraform plan

Output should be something like

1
2
3
4
5
6
$ terraform plan

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the
.
.
.

Apply

To make changes run

1
terraform apply

And terraform will prompt for confirmation and only accepts yes to apply changes.

1
2
3
4
5
Do you want to perform these actions?
Terraform will perform the actions described above.
Only 'yes' will be accepted to approve.

Enter a value:

Enter yes to continue.

After making any changes make sure validate and apply again. To refresh the state of the site run terraform refresh

Project will be deployed and DNS record will be added to the Cloudflare.

Conclusion

With the terraform, making changes are very easy and state can be maintained. Au Revoir.

This post is licensed under MIT by the author.