Using Github and Atlantis for Infrastructure as Code (IaC) on AWS

Have you heard of it already? Atlassian Cloud October 2022 pricing adjustments

Using Github and Atlantis for Infrastructure as Code (IaC) on AWS

aws
Jörg Herzinger

Jörg Herzinger

10 minutes read

At Bytesource we ♥ doing DevOps at Github with Atlantis/Terraform and AWS. Atlantis (https://runatlantis.io) is a powerful GitOps Terraform automation tool that is fully open source and allows developers to directly interact with AWS Infrastructure components via Terraform.

Our initial goal was to provide Developers a setup that allows them to build their software and deploy it to AWS environments without any obstacles. Do achieve this we decided to deploy Atlantis in ECS Fargate and allow the InstanceProfile to assume dedicated deployment roles in separate development and production accounts.

atlantis_gh_aws_architecture.png

And to interact with Atlantis which runs Terraform for us we use Github. There the developers can follow the well established pull request based workflow to update configuration and then deploy it first into the dedicated development environment and then into the dedicated production environment.

Terraform

Most projects in real life don’t start with a setup that is fully automated, beautiful and documented. But nowadays we see that many developers start even in the earliest project phases already with automating the infrastructure they need. Terraform has proven to be a powerful tool in that respect and so we start every new project with at least a git repository for Terraform and a dedicated development environment.

Developers can start using those environments right away and we allow them to deploy to development manually from their machines to keep response times as low as possible. Especially during early development these fast response times guarantee quick results.

Once the project matures we introduce an additional production environment and Atlantis into mix. Terraform projects are usually structured in a similar way:

.
├── atlantis.yaml
├── configs
│   ├── production.tfvars
│   └── development.tfvars
├── main.tf
├── variables.tf
├── providers.tf
└── outputs.tf

A structure like this deploys comparable infrastructure to all environments which different parameters given by the variables in configs/production.tfvars and configs/development.tfvars. This way manual deployment into the development environments is still directly possible and Atlantis as a workflow tool can jump in.

Atlantis

Lets have a look at how Atlantis works.

ght_atlantis_pr_bot.png

For a developer Atlantis is directly integrated into Github and can be interacted with by simple comments on pull requests. By carefully managing the permissions on the repository the developer will be able to produce a terrafrom plan with Atlantis and once the request is approved it can be applied and the pull request can be merged into the main branch.

So how does the Atlantis side look like? In general it consists of two parts: A server side configuration and a repository configuration.

The Server side configuration is autogenerated in our environments and defines which workflows are available.

{
  "repos": [
    {
      "allow_custom_workflows": false,
      "allowed_workflows": [
        "development",
        "production"
      ],
      "apply_requirements": [
        "approved",
        "mergeable"
      ],
      "branch": "/.*/",
      "id": "/.*/"
    }
  ],
  "workflows": {
    "development": {
      "apply": {
        "steps": [
          {
            "env": {
              "name": "TF_VAR_provider_role_arn",
              "value": "arn:aws:iam::<development_account_id>:role/external/DeploymentRole"
            }
          },
          {
            "apply": {
              "extra_args": []
            }
          }
        ]
      },
      "plan": {
        "steps": [
          {
            "env": {
              "name": "TF_VAR_provider_role_arn",
              "value": "arn:aws:iam::<development_account_id>:role/external/DeploymentRole"
            }
          },
          {
            "plan": {
              "extra_args": [
                "-var-file=configs/development.tfvars"
              ]
            }
          }
        ]
      }
    },
    "production": { ... equal to development except for the aws account id and the config file loaded ... }
  }
}

This configuration is quite basic and defines the two workflows development and production which are different only by the terraform varialbe provider_role_arn that they set. So to switch between environments aka. AWS accounts in Terraform a single line is required:

provider "aws" {
  assume_role {
    role_arn = var.provider_role_arn
  }
}

This makes the workflow transparent to any viewer.

The repository configuration atlantis.yaml is in turn very simple:

version: 3
projects:
- name: production
  dir: .
  autoplan:
    enabled: false
  terraform_version: v1.3.3
  workflow: provisioning
- name: development
  dir: .
  autoplan:
    enabled: false
  terraform_version: v1.3.3
  workflow: development

Sumup

While the overall setup looks simple, each part can move quite a lot and thus they have to be carefully configured to fit together. The central piece that is connecting all environments is the AWS Role that is assumed when deploying. Setting this Role via the variable provider_role_arn in the workflow on the server gives a simple governance over which environments can be used. The user also can not override these workflows as that is forbidden by the Atlantis server and since all changes in git are reviewed a certain amount of governance is given here too while still allowing the developer to have short development cycles.

Learn more about our Cloud Services

Cookies help us deliver our services. By using our services, you agree to our use of cookies.