Post

Deploy and Manage your site on Vercel with Terraform

Deploy and Manage your site on Vercel with Terraform

I’m running this blog on vercel with Hugo static site generator and manage my DNS with Cloudflare. Managing it on two separate dashboards 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.3.9
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
10
11
12
13
terraform {
  required_version= ">= 1.3.0"
  required_providers {
    vercel= {
      source = "vercel/vercel"
      version= "~> 0.11.4"
    }
    cloudflare= {
      source = "cloudflare/cloudflare"
      version= "4.0.0"
    }
  }
}

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

Vercel

With variable

1
2
3
4
5
6
7
8
provider "vercel" {
  api_token: var.vercel_api_token
}

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

To use Environment variable

1
export VERCEL_API_TOKEN='token'

Cloudflare

with variable

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

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

Vercel Project

First create a project on vercel with vercel_project,

1
2
3
4
5
6
7
8
9
resource "vercel_project" "test_project" {
  name     = "test_blog"
  framework= "hugo"

  git_repository: {
    type= "github"
    repo= "kdpuvvadi/vercel_hugo_blog"
  }
}

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

Environment Variables

Environment variables can also be set here such as which version of NodeJS to be used, which version of Hugo to be used and etc.

1
2
3
4
5
6
resource "vercel_project_environment_variable" "test_env" {
  project_id= vercel_project.test_project.id
  key       = "HUGO_VERSION"
  value     = "0.110.0"
  target    = ["production"]
}

Deploy the site

Deploy the project with vercel_deployment

1
2
3
4
5
6
7
8
9
10
11
resource "vercel_deployment" "test_deploy" {
  project_id= vercel_project.test_project.id
  ref       = "main"
  production= "true"

  project_settings: {
    build_command   = "hugo --gc --minify"
    output_directory= "/public"
    root_directory  = "/"
  }
}

Add Domain to the project

Add custom domain to the project with vercel_project_domain

1
2
3
4
resource "vercel_project_domain" "test_domain" {
  project_id= vercel_project.test_project.id
  domain    = "example.com"
}

Cloudflare zone

To add a CNAME record or A record to the Cloudflare, we need zone ID. To get the zone ID, get the record data with cloudflare_zones and filter the data

1
2
3
4
5
data "cloudflare_zones" "get_zone_data" {
  filter {
    name: "example.com"
  }
}

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

Add record on Cloudflare

Add DNS record on Cloudflare to point to the vercel site with cloudflare_record. Vercel deployment URL is available from deployment config at vercel_deployment.test_deploy.domains[0].

1
2
3
4
5
6
7
8
resource "cloudflare_record" "zone_blog_record" {
  zone_id= data.cloudflare_zones.get_zone_data.zones[0].id
  name   = "@"
  value  = vercel_deployment.test_deploy.domains[0]
  type   = "CNAME"
  proxied= true
  ttl    = 1
}

Deployment

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.