Discord Bot Pipeline - Terraform Setup

3 minute read

This post is part 4 of a 10 part series:

Previously we set up a GitHub project for hosting Terraform configs, so let’s add some! Create a folder named terraform in your git project folder and add the following files:

  1. backend.tf
  2. outputs.tf
  3. project.tf
  4. provider.tf
  5. terraform.tfvars
  6. variables.tf

This will form the foundation of our Terraform configuration and will be applied to every project we create. Therefore, this should contain the bare minimum configuration we would expect to use across any project, even those beyond Discord bots.

Below we’ll discuss the purpose of each file.

variables.tf and terraform.tfvars

The variables.tf file unsurprisingly defines variables to be used by other Terraform configs. Its contents are below:

variable "project_name" {}
variable "billing_account" {}
variable "org_id" {}
variable "region" {}
variable "zone" {}

These are basically the variables that define the environment the project will use to operate. Some of these, like billing_account and org_id, will be essentially global across all of your projects, since they are tied to your GCP account. Others, like project_name, are project-specific and will be different for each project you create.

The terraform.tfvars file contains the values for these variables:

project_name = ""
billing_account = ""
org_id = ""
region = ""
zone = ""

Note that these are still parameterized and will be populated by our workflow that creates a new project.

On the next post, we’ll talk about how we can leverage GitHub Actions to perform Terraform actions for us.

backend.tf

The backend.tf file will be configured to tell Terraform where to find the persistent Terraform state. We created this as a GCS bucket earlier in this series, and each project will use its own state file, as shown in the file below:

terraform {
  backend "gcs" {
    bucket = "-tf-controller"
    prefix = "/terraform/state"
  }
}

provider.tf

We are using GCP, so add GCP providers here:

provider "google" {
  region = var.region
}

provider "google-beta" {
  region = var.region
}

outputs.tf

This file contains exposed values that are essentially the results of applying the Terraform config. This is mainly for other Terraform configs to use, and is essentially the GCP project ID of the project that will be created:

output "project_id" {
  value = google_project.project.project_id
}

project.tf

This file consists of the core set of Terraform objects that will be part of every project you create. This is a bit subjective, as not all projects may use every resource you define here, but I typically like to keep this as the set of resources that are pretty commonly used. In my case, I set this up as:

resource "random_id" "id" {
  byte_length = 4
  prefix      = var.project_name
}

resource "google_project" "project" {
  name            = var.project_name
  project_id      = random_id.id.hex
  billing_account = var.billing_account
  org_id          = var.org_id
}

resource "google_project_service" "service" {
  provider = google-beta

  for_each = toset([
    "artifactregistry.googleapis.com",
    "cloudbilling.googleapis.com",
    "cloudresourcemanager.googleapis.com",
    "compute.googleapis.com",
    "firebase.googleapis.com",
    "firestore.googleapis.com",
    "iam.googleapis.com",
    "secretmanager.googleapis.com",
    "serviceusage.googleapis.com"
  ])

  service = each.key

  project            = google_project.project.project_id
  disable_on_destroy = false
}

resource "time_sleep" "artifact_registry_api_enabling" {
  depends_on = [
    google_project_service.service["artifactregistry.googleapis.com"]
  ]

  create_duration = "2m"
}

resource "google_artifact_registry_repository" "artifact-repo" {
  provider      = google-beta
  location      = var.zone
  project       = google_project.project.project_id
  repository_id = "services"
  description   = "Docker images for services"
  format        = "DOCKER"

  depends_on = [
    time_sleep.artifact_registry_api_enabling
  ]
}

This adds a GCP service resource for many common services, like Firebase, Secret Manager, and Artifact Registry. it also creates a default Artifact Registry Repository for Docker images, as it’s fairly common for projects to need to push Docker images to some private repository (and this one is no exception).