Home Automate Cloudflare with Terraform and GitHub Actions! - Terraform Tutorial for Beginners
Post
Cancel

Automate Cloudflare with Terraform and GitHub Actions! - Terraform Tutorial for Beginners

Today, we’re going to set up and configure Terraform on your machine so we can start using Terraform. Then we’ll configure cf-terraforming to import our Cloudflare state and configuration into Terraform. After that we’ll set up a GitHub report and configure GitHub actions so you have CI and CD for deploying your Infrastructure automatically using a Git Flow. If you’re new to Terraform, that’s fine! This is a beginner tutorial for Terraform and by the end of this, you will feel like an expert!

📺 Watch Video

What is Terraform and how does it help?

Terraform is a powerful infrastructure as code tool to help you create and manage infrastructure across multiple public or private clouds. It can help you provision, configure, and manage infrastructure using their simple and human readable configuration language. Using Terraform helps you automate your infrastructure and your DevOps workflow, do it consistently, and allows you to collaborate with teams in Git.

There are 7 key areas where Terraform shines:

  • Automation: Terraform enables automation of infrastructure provisioning, configuration, and management, which reduces human error and saves time.

  • Consistency: Terraform ensures that your infrastructure is consistent across all environments, from development to production.

  • Collaboration: Terraform allows multiple teams to work together on infrastructure changes, using version control systems like Git.

  • Cloud-agnostic: Terraform supports various cloud providers, including AWS, Google Cloud, and Microsoft Azure, allowing you to use the same tool to manage resources across different clouds.

  • Scalability: Terraform is designed to handle large-scale infrastructure deployments and can easily manage thousands of resources.

  • Reusability: Terraform modules enable you to reuse code and infrastructure components across multiple projects, making it easier to manage infrastructure at scale.

  • Flexibility: Terraform is highly flexible and can be extended through plugins to integrate with other tools and services.

Installing Terraform

This will work on Ubuntu and Windows + WSL

Install terraform for other platforms

Install dependencies

1
2
sudo apt update
sudo apt install  software-properties-common gnupg2 curl

Import the gpg key

1
2
curl https://apt.releases.hashicorp.com/gpg | gpg --dearmor > hashicorp.gpg
sudo install -o root -g root -m 644 hashicorp.gpg /etc/apt/trusted.gpg.d/

Add hashicorp repository

1
sudo apt-add-repository "deb [arch=$(dpkg --print-architecture)] https://apt.releases.hashicorp.com $(lsb_release -cs) main"

Install terraform

1
sudo apt install terraform

Check the version

1
2
3
terraform --version
Terraform v1.4.0
on linux_amd64

Create your Terraform Cloudflare config

First create a simple terraform config:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
terraform {
  required_providers {
    cloudflare = {
      source  = "cloudflare/cloudflare"
      version = "~> 3.0"
    }
  }
}

provider "cloudflare" {
  api_token = var.cloudflare_api_token
}

# Create a record
resource "cloudflare_record" "www" {
  # ...
}

# Create a page rule
resource "cloudflare_page_rule" "www" {
  # ...
}

Here is my .editorconfig:

1
2
3
4
5
6
7
8
9
10
11
12
# http://editorconfig.org
root = true

[*]
indent_style = space
indent_size = 2
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true

[*.md]
trim_trailing_whitespace = false

Initialize terraform and Cloudflare

1
terraform init

We should see that it installed plugins, and it should have created a lock file.

Terraform Plan and Apply

Next we’ll want to review our plan, we can see our proposed changes

1
terraform plan

Next we’ll apply our changes.

1
terraform apply

Verify on the dashboard.

After applying we can verify the results.

we can also test with nslookup

1
nslookup yoursite.example.com

Check Cloudflare’s site.

if we run plan again, we can see there’s no work to do

1
terraform apply

Should see that there isn’t any work to do.

Importing Cloudflare State

We will need to import our Cloudflare state into our local Terraform state.

An important point to understand about Terraform is that it can only manage configuration it created or was explicitly told about after the fact. The reason for this limitation is that Terraform expects to be authoritative for the resources it manages. It relies on two types of files to understand what resources it controls and what state they are in. Terraform determines when and how to make changes from the following:

  • A configuration file (ending in .tf) that defines the configuration of resources for Terraform to manage. This is what you worked with in the tutorial steps.
  • A local state file that maps the resource names defined in your configuration file — for example, cloudflare_load_balancer.www-lb — to the resources that exist in Cloudflare.

https://developers.cloudflare.com/terraform/advanced-topics/import-cloudflare-resources/

So this means that we need to sync the remote state of Cloudflare, down to our local state.

This is where cf-terraforming can help

Check for the latest version here: https://github.com/cloudflare/cf-terraforming/tags

Update this command with the latest tag

1
2
3
4
5
6
7
8
9
curl -L https://github.com/cloudflare/cf-terraforming/releases/download/v0.11.0/cf-terraforming_0.11.0_linux_amd64.tar.gz -o cf-terraforming.tar.gz

tar -xzf cf-terraforming.tar.gz

rm cf-terraforming.tar.gz

sudo mv ./cf-terraforming /usr/local/bin

sudo chmod +x /usr/local/bin/cf-terraforming

Then we need to updated our .zshrc or .bashrc with our variables

1
nano ~/.zshrc
1
2
export CLOUDFLARE_API_TOKEN='12345'
export CLOUDFLARE_ZONE_ID='abcde'

The source your shell

1
source ~/.zshrc

Now let’s export Cloudflare state

(Be sure you have copied your variables into your shell, or ran the export commands above )

1
2
3
cf-terraforming generate \
  --resource-type "cloudflare_record" \
  --zone $CLOUDFLARE_ZONE_ID > imported.tf

Look at the file and copy the contents into your cloudflare.tf

then run

1
terraform plan

Terraform thinks that we need to apply all of these resources, even though they exist.

We need to import them into our local state.

1
2
3
cf-terraforming import \
  --resource-type "cloudflare_record" \
  --zone $CLOUDFLARE_ZONE_ID

This will export a lot of commands, we now need to run them to import them into our state.

All you need to do it copy and paste the commands into your terminal.

This will import your local state, you can see it in terraform.tfstate

If we run terraform plan now, we can see that there aren’t any changes.

Remote State with Terraform Cloud

Terraform Cloud

Be sure to sign up for an account and then get add your CLOUDFLARE_API_TOKEN and an ENV variable in Terraform Cloud. Mark it as seneitive.

Then you’ll want to updated your cloudflare.tf

It should look like this

1
2
3
4
5
6
7
8
  cloud {
    hostname = "app.terraform.io"
    organization = "your org"

    workspaces {
      name = "Cloudflare"
    }
  }

Your cloudflare.tf file should now look like:

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
terraform {
  cloud {
    hostname = "app.terraform.io"
    organization = "your org"

    workspaces {
      name = "Cloudflare"
    }
  }
  required_providers {
    cloudflare = {
      source  = "cloudflare/cloudflare"
      version = "~> 3.0"
    }
  }
}

provider "cloudflare" {
  api_token = var.cloudflare_api_token
}

# Create a record
resource "cloudflare_record" "www" {
  # ...
}

# Create a page rule
resource "cloudflare_page_rule" "www" {
  # ...
}

Then run

1
terraform init

This will prompt you to sign in and then import your local state into Terraform cloud.

CI / CD with GitHub Actions

If you want to create a CI / CD pipeline with GitHub actions, you’ll need to create a new repo at GitHub

Here is my .gitignore

.terraform/
terraform.tfstate*

Convert your local folder into a git repo:

first, cd into your folder

Note: Be sure not to commit any of your secrets to git! This includes API tokens, terraform state, and any other files that might include sensitive information

1
2
3
4
5
git init
git commit -m "first commit"
git branch -M main
git remote add origin [email protected]:username/your-repo-name.git
git push -u origin main

To create a branch, add files, commit, and push

1
2
3
4
git checkout -b my-new-branch
git add .
git commit -m "fix(terraform): made some changes"
git push --set-upstream origin my-new-branch

Be sure you have created a secret TF_API_TOKEN with your Terraform API token.

For reference, here is the terraform GitHub Action (with my bug fix)

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
44
45
46
47
48
49
name: 'Terraform'

on:
  push:
    branches: [ "main" ]
  pull_request:

permissions:
  contents: read

jobs:
  terraform:
    name: 'Terraform'
    runs-on: ubuntu-latest
    environment: production

    # Use the Bash shell regardless whether the GitHub Actions runner is ubuntu-latest, macos-latest, or windows-latest
    defaults:
      run:
        shell: bash

    steps:
    # Checkout the repository to the GitHub Actions runner
    - name: Checkout
      uses: actions/[email protected]

    # Install the latest version of Terraform CLI and configure the Terraform CLI configuration file with a Terraform Cloud user API token
    - name: Setup Terraform
      uses: hashicorp/[email protected]
      with:
        cli_config_credentials_token: $

    # Initialize a new or existing Terraform working directory by creating initial files, loading any remote state, downloading modules, etc.
    - name: Terraform Init
      run: terraform init

    # Checks that all Terraform configuration files adhere to a canonical format
    - name: Terraform Format
      run: terraform fmt -check

    # Generates an execution plan for Terraform
    - name: Terraform Plan
      run: terraform plan -input=false

      # On push to "main", build or change infrastructure according to Terraform configuration files
      # Note: It is recommended to set up a required "strict" status check in your repository for "Terraform Cloud". See the documentation on "strict" required status checks for more information: https://help.github.com/en/github/administering-a-repository/types-of-required-status-checks
    - name: Terraform Apply
      if: github.ref == 'refs/heads/main' && github.event_name == 'push'
      run: terraform apply -auto-approve -input=false

Wrapping up

At this point you should be able to run terraform and have your Cloudflare state sync’d with Terraform Cloud and GitHub actions running in CI / CD so you can start deploying your infrastructure using code!

Join the conversation

⚙️ See all the hardware I recommend at https://l.technotim.live/gear

🚀 Don’t forget to check out the 🚀Launchpad repo with all of the quick start source files

This post is licensed under CC BY 4.0 by the author.

Rotating your Encryption Keys and Updating your Secrets with SOPS

Building a Low Power, All-in-One, Silent Server