[Terragrunt GitOps - Part 2] Prerequisites & GCP set-up

·

5 min read

Introduction

Welcome back - I hope you've become acquainted with the first article in the series. In this one, we'll continue our journey and focus on performing the initial GCP set-up. We'll create the "orange" resources (as noted in the previous article) and set the grounds for GitOps to operate smoothly.

However, the "meta" trigger will be the topic of the next article.

Set-up steps

Projects and folders

First of all, we need GCP projects. Recall from the design article that we need 3 projects on the SP perimeter. Then we need 2 projects in the "customer" perimeter. Of course, I'll simulate the latter part and create those projects under the same organization (in real life, however, those would be customer-managed projects belonging to another GCP org).

I'll delete my folders and projects before the publication of this article, so I'll be sharing their names and IDs.

To create them, follow publically available documentation (e.g. for folders), it's very simple.

Enable APIs

We need to enable APIs in the projects. First, let's handle the SP part.

list_of_apis=(cloudbuild.googleapis.com cloudresourcemanager.googleapis.com storage-component.googleapis.com storage-api.googleapis.com iam.googleapis.com secretmanager.googleapis.com)

list_of_projects=(prj-customer1-dedicated prj-customer2-dedicated prj-terragrunt-common)

for project_id in "${list_of_projects[@]}"
do
    for api in "${list_of_apis[@]}"
    do
        gcloud services enable $api --project=$project_id
    done
done

Then let's do the same for the customer projects:

list_of_apis=(cloudresourcemanager.googleapis.com storage-component.googleapis.com storage-api.googleapis.com iam.googleapis.com)
list_of_projects=(prj-customer1-outside-org prj-customer2-outside-org)

for project_id in "${list_of_projects[@]}"
do
    for api in "${list_of_apis[@]}"
    do
        gcloud services enable $api --project=$project_id
    done
done

Create Dockerfile, build, and push to Artifact Registry

When writing the Dockerfile for the Terragrunt runner, I got inspiration from Google's example foundation. I enhanced and adjusted to my needs a little, and that's the result:

ARG GCLOUD_VERSION=453.0.0-slim

# base image
FROM google/cloud-sdk:${GCLOUD_VERSION}

# Use ARG so that values can be overridden by user/cloudbuild
ARG TERRAFORM_VERSION="1.6.3"
ARG TERRAGRUNT_VERSION="0.53.2"

# install common tools
RUN apt-get update && \
    apt-get -y install curl jq unzip git ca-certificates gnupg

# intall terraform
RUN curl https://releases.hashicorp.com/terraform/${TERRAFORM_VERSION}/terraform_${TERRAFORM_VERSION}_linux_amd64.zip --output terraform_${TERRAFORM_VERSION}_linux_amd64.zip && \
    curl https://releases.hashicorp.com/terraform/${TERRAFORM_VERSION}/terraform_${TERRAFORM_VERSION}_SHA256SUMS.sig --output terraform_SHA256SUMS.sig  && \
    curl https://releases.hashicorp.com/terraform/${TERRAFORM_VERSION}/terraform_${TERRAFORM_VERSION}_SHA256SUMS --output terraform_SHA256SUMS && \
    curl https://keybase.io/hashicorp/pgp_keys.asc --output pgp_keys.asc && \
    gpg --import pgp_keys.asc && \
    gpg --verify terraform_SHA256SUMS.sig terraform_SHA256SUMS && \
    grep terraform_${TERRAFORM_VERSION}_linux_amd64.zip terraform_SHA256SUMS | shasum --algorithm 256 --check  && \
    mkdir -p /builder/terraform && \
    unzip terraform_${TERRAFORM_VERSION}_linux_amd64.zip -d /builder/terraform && \
    rm -f terraform_${TERRAFORM_VERSION}_linux_amd64.zip terraform_SHA256SUMS

# install terragrunt
RUN curl -L https://github.com/gruntwork-io/terragrunt/releases/download/v${TERRAGRUNT_VERSION}/terragrunt_linux_amd64 \
    -o /usr/local/bin/terragrunt && \
    chmod +x /usr/local/bin/terragrunt

# install Github CLI
RUN curl -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg | dd of=/usr/share/keyrings/githubcli-archive-keyring.gpg && \
    chmod go+r /usr/share/keyrings/githubcli-archive-keyring.gpg && \
    echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" | tee /etc/apt/sources.list.d/github-cli.list > /dev/null && \
    apt update && \
    apt install gh -y

# purge and remove
RUN apt-get --purge -y autoremove && \
    apt-get clean && \
    rm -rf /var/lib/apt/lists/*

ENV PATH=/builder/terraform/:$PATH

ENTRYPOINT ["/bin/bash"]

Comments for that Dockerfile:

  • It's based on gcloud-sdk image.

  • It contains a lot of tools, so not the slimmest you can use (as per Docker best practices). However, it's general enough for me to justify its usage - in the Cloud Builds steps I often run gcloud commands, curl, git so it comes in handy.

  • You can choose Terraform and Terragrunt versions. For Terraform download, you can see a lengthy part whose purpose is verification of the SHA sums of the package.

  • I install GitHub CLI tool - I'll explain why exactly a bit later. In short, it's necessary for the way I've chosen to authenticate when pulling from private repositories.

The procedure is as follows:

  • Create an Artifact Registry repo by following this article

  • Build & push the Docker image to the repository by following this article

Note. I advise to use of the regions listed here. Egress to the same region is free (so Artifact Registry -> Cloud Build in the same region). And we're a bit limited in terms of Cloud Build trigger location if we use the GCP free trial.

Prepare repositories

In the design article, I mentioned we'd be using 4 repositories. Of course, I share the "public" versions of what I used. But when you try to replicate the setup, you will need those repositories to be private (as they will be in an enterprise environment). In order not to delve into the code right now, I suggest you create 4 private repositories and name them similarly to what I used:

  • terraform-random-sample-module

  • terraform-storage-sample-module

  • terragrunt-runner-module

  • terragrunt-example-envs

They could be empty as of now.

Create a fine-grained token in GitHub

I've chosen GH fine-grained tokens as an authentication mechanism to pull private repositories. Your next step is to create a private access token and add read permission for the 4 repos in its scope.

Follow the guide in the official documentation to achieve it. I have something like:

And grant read access for contents, commit statuses, discussions, issues, merge queues, metadata, pull requests, and variables.

Note. It's a bit excessive, but those read-access permissions may come in handy if you want to expand the solution. Feel free to limit it, though.

Store the token in the Secret Manager

The next step is to create a new secret object in a common project and add a secret version containing GitHub's fine-grained PAT.

Follow the docs to complete it.

Add a Cloud Build Connection in each of the SP projects

For the Cloud Build triggers to respond to events in the terragrunt-example-envs repository, we need to create something that's called a connection. We'll use the "2nd" gen. It's much easier to do it via console than CLI/Terraform and also you don't have to manage the sensitive string in your code, so that part is even preferred to do in GUI in my opinion.

The result is that in each of your SP projects you have something like this:

Conclusion

This article explained how to perform some one-time operations needed for the solution. We didn't deal with "meta" triggers and customer-side set-up, which will be the topic of the next article.