Gitlab hero border pattern left svg Gitlab hero border pattern right svg

How teams use GitLab and Terraform for infrastructure as code: A demo

When multiple teams use a Git repository as the single source of truth for all infrastructure and application deployment code, they’re performing a good GitOps procedure. Infrastructure teams can collaborate and deploy code to multiple cloud services using Terraform for automation. This article demonstrates how teams can create a Kubernetes cluster by collaborating with teammates within GitLab.

Learn how GitLab enables infrastructure as code

This demo demonstrates how to follow good GitOps procedure to deploy infrastructure as code using Terraform for automation and GitLab as the single source of truth (and CI).

Building your infrastructure as code in GitLab

Getting started

This gitops-demo group illustrates the steps infra teams can follow.

Begin by logging into the group where the project lives within GitLab. The next step is to open the README.md file, which shows the underlying structure of the gitops-demo group. There are a few individual projects and two subgroups: infrastructure and applications.

Inside the infrastructure subgroup

There is a separate repository for each cloud: Azure, GCP, and AWS, and a repository for templates.

Infrastructure subgroup

Similar files can be found in all three cloud repositories. All of the files are written in Terraform to automate the deployment process, while a gitlab-ci.yml file is also stored in the repository to provide instructions for automation.

The backend file

Using HashiCorp’s Terraform Cloud Service as a remote location for the state file keeps the state file safe and in a central location so it can be accessed by any process. One advantage of using Terraform Cloud is it has the ability to lock the state to ensure only one job can run at a time, preventing multiple jobs from making conflicting changes. The code stores the state files in the Terraform Cloud in an organization called gitops-demo in a workspace called aws. This keeps the running state in the cloud provider, so any team member has access at any time.

terraform {
  backend "remote" {
    hostname     = "app.terraform.io"
    organization = "gitops-demo"
    workspaces {
      name = "aws"
    }
  }
}

EKS.tf file

The EKS is another Terraform file that leverages the EKS module for the Terraform cluster. Teams can define parameters such as the type of subnets and the number of nodes in the EKS terraform file.

module "eks" {
  source           = "terraform-aws-modules/eks/aws"
  cluster_name     = "gitops-demo-eks"
  subnets          = "${module.vpc.public_subnets}"
  write_kubeconfig = "false"
  tags = {
    Terraform   = "true"
    Environment = "dev"
  }
  vpc_id = "${module.vpc.vpc_id}"
  worker_groups = [
    {
      instance_type = "m4.large"
      asg_max_size  = 5
      tags = [{
        key                 = "Terraform"
        value               = "true"
        propagate_at_launch = true
      }]
    }
  ]
}

Define the GitLab admin

The Kubernetes provider can be used to create a GitLab admin user and set up automatically as code and managed by Terraform.

Register the cluster with GitLab

Now that a Kubernetes cluster has been created, it’s time to register it with GitLab in order to deploy more code to the cluster in the future. The first step is to use the GitLab provider to create a group cluster named AWS cluster.

data "gitlab_group" "gitops-demo-apps" {
  full_path = "gitops-demo/apps"
}
provider "gitlab" {
  alias   = "use-pre-release-plugin"
  version = "v2.99.0"
}
resource "gitlab_group_cluster" "aws_cluster" {
  provider           = "gitlab.use-pre-release-plugin"
  group              = "${data.gitlab_group.gitops-demo-apps.id}"
  name               = "${module.eks.cluster_id}"
  domain             = "eks.gitops-demo.com"
  environment_scope  = "eks/*"
  kubernetes_api_url = "${module.eks.cluster_endpoint}"
  kubernetes_token   = "${data.kubernetes_secret.gitlab-admin-token.data.token}"
  kubernetes_ca_cert = "${trimspace(base64decode(module.eks.cluster_certificate_authority_data))}"
}

The code contains the domain name, environment scope, and Kubernetes credentials.

After this runs, the cluster will be created in AWS and automatically registered to the gitops-demo/apps group.

Deploying code using GitLab CI

Terraform template

Return to the infrastructure group and open the Templates folder. When looking at the terraform.gitlab-ci.yml file, it’s possible to see how the CI works to deploy infrastructure code to the cloud using Terraform.

Inside the CI file, teams can see a few different stages: validate, plan, apply, and destroy. Using Hashicorp’s Terraform base image, users can run different tasks.

The first step is to initialize Terraform.

before_script:
  - terraform --version
  - terraform init
  - apk add --update curl
  - curl -o kubectl https://amazon-eks.s3-us-west-2.amazonaws.com/1.13.7/2019-06-11/bin/linux/amd64/kubectl
  - install kubectl /usr/local/bin/ && rm kubectl
  - curl -o aws-iam-authenticator https://amazon-eks.s3-us-west-2.amazonaws.com/1.13.7/2019-06-11/bin/linux/amd64/aws-iam-authenticator
  - install aws-iam-authenticator /usr/local/bin/ && rm aws-iam-authenticator

The next step is to validate that everything is correct.

validate:
  stage: validate
  script:
    - terraform validate
    - terraform fmt -check=true
  only:
    - branches

It’s important to remember that good GitOps workflows incorporate creating a merge request for the changes.

merge review:
  stage: plan
  script:
    - terraform plan -out=$PLAN
    - echo \`\`\`diff > plan.txt
    - terraform show -no-color ${PLAN} | tee -a plan.txt
    - echo \`\`\` >> plan.txt
    - sed -i -e 's/  +/+/g' plan.txt
    - sed -i -e 's/  ~/~/g' plan.txt
    - sed -i -e 's/  -/-/g' plan.txt
    - MESSAGE=$(cat plan.txt)
    - >-
      curl -X POST -g -H "PRIVATE-TOKEN: ${GITLAB_TOKEN}"
      --data-urlencode "body=${MESSAGE}"
      "${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/merge_requests/${CI_MERGE_REQUEST_IID}/discussions"
  artifacts:
    name: plan
    paths:
      - $PLAN
  only:
    - merge_requests

The merge request

The merge request (MR) is the most important step in GitOps. This is the process to review all changes and see the impact of those changes. The MR is also a collaboration tool where team members can discuss changes and stakeholders can approve changes before the final merge into the main branch.

The merge request defines what will happen when running the infrastructure as code. After the MR is created, the Terraform plan is uploaded to the MR. After all changes have been reviewed and approved, the code can be merged to the main branch. Once the code changes are merged, all the changes will be deployed into production.

Ready to learn more about GitOps?

Git is a trademark of Software Freedom Conservancy and our use of 'GitLab' is under license