Post

Automating Terraform deployments with Atlantis

I have been using Terraform personally and professionally for years. It is a great IaC tool to simplify management of systems and services.

I redesigned my homelab several years ago when learning terraform to be lightweight, agile, and repeatable. Gone are the loud and power hungry Dell R710s of old. In came several Minisforum Ryzen powered mini pcs running proxmox. When moving to proxmox, I wanted a way to manage my core services through code. Terraform for the infrastructure was the answer. I have found several providers for proxmox that I have iterated through. Currently I am using bgp proxmox for deploying VMs and LXC containers to my proxmox hosts. I also integrated using providers for pihole, cloudflare, aws, and others into my homelab for easy management.

Running this code has always been from my Apple Silicon Macbook air or Mac mini. This has worked more than fine, in fact this is how my work process works for the most part. However, I have always wanted a way of deploying infrastructure in a more automated fashion. I also have wanted to improve our work processes with more terraform automation, pipelines, or something. I had heard of Atlantis from a former job where the DevOps team was using it, though I had no personal experience myself. My extensive use of terraform in my homelab presented the perfect oppourtunity to try it out and learn something new.

Build custom Atlantis container

The purpose built docker container from Atlantis was a good starting point. However, I had a need to integrate some other pieces. I use Terragrunt personally and professionally to DRY out my code. I also use 1Password in my homelab using the Terraform provider for infrastructure secrets. I needed a way to use both of these in Atlantis, so I built a custom container.

Dockerfile for Atlantis Container:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
FROM ghcr.io/runatlantis/atlantis:latest-debian

USER root

# install terragrunt
ENV TERRAGRUNT_VERSION=v0.55.10
RUN apt update && apt full-upgrade -y
ADD https://github.com/gruntwork-io/terragrunt/releases/download/${TERRAGRUNT_VERSION}/terragrunt_linux_amd64 /usr/local/bin/terragrunt
RUN chmod +x /usr/local/bin/terragrunt

#Install 1password
RUN curl -sS https://downloads.1password.com/linux/keys/1password.asc | gpg --dearmor --output /usr/share/keyrings/1password-archive-keyring.gpg
RUN echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/1password-archive-keyring.gpg] https://downloads.1password.com/linux/debian/$(dpkg --print-architecture) stable main" | tee /etc/apt/sources.list.d/1password.list
RUN mkdir -p /etc/debsig/policies/AC2D62742012EA22/
RUN curl -sS https://downloads.1password.com/linux/debian/debsig/1password.pol | tee /etc/debsig/policies/AC2D62742012EA22/1password.pol
RUN mkdir -p /usr/share/debsig/keyrings/AC2D62742012EA22
RUN curl -sS https://downloads.1password.com/linux/keys/1password.asc | gpg --dearmor --output /usr/share/debsig/keyrings/AC2D62742012EA22/debsig.gpg
RUN apt update && apt install 1password-cli -y

This Dockerfile will install Terragrunt and 1Password onto the container. I have a gitlab pipeline that will build this container weekly to update the container.


Setup Atlantis Container

Now that I have the container required in my homelab, I deployed it on my docker host. I use portainer for a bit of a nice GUI but any old docker implementation will work. I store my terraform state in S3 for all projects so I will need access to AWS, as well as the local secrets required for terraform projects.

Environment variables required:

  • ATLANTIS_ATLANTIS_URL = address of my atlantis service: https://atlantis.schenk.tech
  • ATLANTIS_GITLB_HOSTNAME = address of my local gitlab server
  • ATLANTIS_GITLAB_TOKEN = Gitlab Token created in following steps
  • ATLANTIS_GITLAB_USER = Gitlab user
  • ATLANTIS_GITLAB_WEBHOOK_SECRET = Gitlab webhook secret created in following steps
  • ATLANTIS_AUTOMERGE = true (this will automerge Gitlab MR once approved and applied)
  • OP_SERVICE_ACCOUNT_TOKEN = 1Password secret token to access my homelab vault
  • TERRAGRUNT_VERISON = Version of terragrunt currently in use

Integrate with Gitlab

In order to integrate Atlantis, a gitlab token and webhook setup is required.

Create Gitlab Token

  1. Open Gitlab and navigate to user **Access Tokens ** page
  2. Create a Personal Access Token with the following api permissions. Save the token somewhere safe

Create Webhook in each project

  1. For each terraform repository Atlantis will interact with, create a Webhook:
    • URL: Atlantis server URL, such as https://atlantis.schenk.tech
    • Secret Token: Create a shared secret, save somewhere safe.
    • Trigger:
      • Push events
      • Comments
      • Merge request events

Gitlab Webhook Configuration


Deploy Atlantis

Deploy the Atlantis container with the noted environment variables to the docker host of choice, below is an example using Portainer.

Atlantis Environment Variables


Create a Terraform Merge Request

Now we can test Atlantis. Create a Test MR with a terraform repository. Example below creates an S3 bucket in AWS.

Gitlab MR

The webhook integrated with the repository will reach out to the atlantis server to run a plan via an external pipeline on the remote server. Below is the expanded output that has been added as a comment on the MR by the Atlantis server after running a Terraform Plan locally.

Terraform Plan

By approving the MR then typing atlantis apply in a comment, the atlantis server will then be triggered to run a terraform apply locally. The MR will be merge automatically after all plans have been run if ATLANTIS_AUTOMERGE environment variable is set.

Terraform Apply

Atlantis can be configured more as needed. An atlantis.yaml file can be added per repo to override defaults if allowed in the atlantis server’s repos.yaml. Custom workflows can also be added to allow the use of Terragrunt in some repos. Below is an example atlantis.yaml to enable terragrunt:

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
version: 3
projects:
  - dir: jammy-test
    workflow: terragrunt
  - dir: pbs
    workflow: terragrunt
workflows:
  terragrunt:
    plan:
      steps:
        - env:
            name: TERRAGRUNT_TFPATH
            command: 'echo "terraform${ATLANTIS_TERRAFORM_VERSION}"'
        - env:
            # Reduce Terraform suggestion output
            name: TF_IN_AUTOMATION
            value: "true"
        - run:
            command: terragrunt plan -input=false -out=$PLANFILE
            output: strip_refreshing
    apply:
      steps:
        - env:
            name: TERRAGRUNT_TFPATH
            command: 'echo "terraform${ATLANTIS_TERRAFORM_VERSION}"'
        - env:
            # Reduce Terraform suggestion output
            name: TF_IN_AUTOMATION
            value: "true"
        - run: terragrunt apply $PLANFILE

Actions can be restricted even further which is where I will continue experimenting. For now, I can now automate my infrastructure changes a bit more by utilizing a central Atlantis server to deploy my Infrastructure as code.

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