Atlantis is an infrastructure as code (IaC) tool to automate Terraform interactions within your GitLab/GitHub MR/PR workflow. This post uses the setup explained in an older post, where we used GitLab as Terraform backend.
Instead of running terraform init/apply/plan
on your local dev machine, you can now run it automagically when opening a merge request. This works by interacting via comments with Atlantis.
Setup
1. Preparations
First generate a personal access token for the gitlab user with scope api
. As this user will comment on MRs you should create a new atlantisbot
user or reuse an already existing bot user.
Then generate a secret which will be shared between GitLab and Atlantis. For example with pwgen -s 64 1
. This is not necessary but helps against random request against your Atlantis instance, as it will only accept requests with this secret in the header.
2. Setup Atlantis deployment.
Most of my services are running as containers and as Atlantis provides an upstream image there is no good reason to not use it. All the TF_*
variables must match the configured Terraform backend. HCLOUD_TOKEN
and HETZNER_DNS_API_TOKEN
are specific token needed by the Hetzner Cloud and Hetzner Cloud DNS Terraform provider.
version: "3"
services:
atlantis:
image: runatlantis/atlantis
container_name: atlantis
restart: always
ports:
- 127.0.0.1:4141:4141
environment:
ATLANTIS_GITLAB_TOKEN: "wasd"
ATLANTIS_GITLAB_WEBHOOK_SECRET: "wasd"
HCLOUD_TOKEN: "wasd"
HETZNER_DNS_API_TOKEN: "wasd"
TF_HTTP_ADDRESS: https://gitlab.mydomain.com/api/v4/projects/<ID>/terraform/state/project
TF_HTTP_LOCK_ADDRESS: https://gitlab.mydomain.com/api/v4/projects/<ID>/terraform/state/project/lock
TF_HTTP_UNLOCK_ADDRESS: https://gitlab.mydomain.com/api/v4/projects/<ID>/terraform/state/project/lock
TF_HTTP_USERNAME: me
TF_HTTP_PASSWORD: password
TF_HTTP_LOCK_METHOD: POST
TF_HTTP_UNLOCK_METHOD: DELETE
command:
- server
- --atlantis-url=https://atlantis.mydomain.com
- --gitlab-user=my-bot
- --repo-allowlist=gitlab.mydomain.com/my-project/iac
- --gitlab-hostname=gitlab.mydomain.com
Add repositories in the repo-allowlist
parameters to enable Atlantis for these specific repositories.
3. Register a GitLab webhook in your project.
Add a webhook to all projects previously added in the repo-allowlist
option. Select the trigger push events
, comments
and merge request events
. Also check that the URL ends with /events
.
4. Setup Caddy as reverse proxy to expose Atlantis.
Setup caddy as a revsere proxy to handle TLS certificates and disable all log outputs.
atlantis.mydomain.com {
reverse_proxy http://localhost:4141 {
header_up Host {http.reverse_proxy.upstream.hostport}
}
log {
output discard
}
}
Secure access to the dashboard
Atlantis provides a dashboard that displays current locks on running deployments. The lock locks the Terraform state to mitigate multiple apply
operations. This is a neat feature as it prevents an inconsistent Terraform state. But it also leaks the name of your repos/projects if the dashboard is available on the public internet (and anyone with access to it can remove any state lock). Also the /events
endpoint must be reachable from the public internet as it will be called by the earlier registered webhook(s).
This is an task for the basicauth Caddy module. The password must be hashed as Caddy will not accept plain text passwords. You can generate a password with caddy hash-password.
root@server:~# caddy hash-password --plaintext my-secure-password
JDJhJDE0JGVsMXhGeDI0UTZ4SkhhSi9yWlZ5Uk93Q08uU0twa05vOXNQVDY0MFpkVGdZMjZUT09Ha2hH
atlantis.mydomain.com {
reverse_proxy http://localhost:4141 {
header_up Host {http.reverse_proxy.upstream.hostport}
}
basicauth / {
my-user JDJhJDE0JGVsMXhGeDI0UTZ4SkhhSi9yWlZ5Uk93Q08uU0twa05vOXNQVDY0MFpkVGdZMjZUT09Ha2hH
}
log {
output discard
}
}
Conclusion
Atlantis provides an easy way to let teams work together without problems like locking the state because two members are running Terraform on their local machine at the same time. The interaction within MRs/PRs is simple and it also makes changes visible within an MR/PR. With that a reviewer can directly see what Terraform will do and doesn't have to check out the branch locally and run a terraform plan
.