Neste artigo, gostaria de mostrar uma configuração de ponta a ponta para criar pipelines do Gitlab CI para Terraform, usando GCP como Armazenamento Remoto, passo a passo.

Configuração de Conta GCP

A configuração da conta pode ser feita de várias maneiras diferentes. Minha escolha é ter um projeto para DevOps, e um projeto por ambiente, conforme ilustrado na imagem abaixo.

Untitled

No projeto DevOps, precisamos configurar uma Conta de Serviço com Permissão de Editor que será usada pelos pipelines para implantar os recursos. Para fazer isso:

  1. Vá para o IAM no projeto DevOps e crie uma Conta de Serviço.

Untitled

  1. Escolha um nome Untitled

  2. Conceda Permissão de Editor e crie (você pode pular a etapa 3) Untitled

  3. Agora, vamos criar uma chave para a Conta de Serviço. Por favor, selecione a conta de serviço, vá para a seção Chave e adicione uma chave no formato JSON. Salve-a para usarmos no Gitlab CI. Untitled

  4. Agora, precisamos conceder acesso a essa conta de serviço para poder criar recursos nos projetos de desenvolvimento, estágio e produção. Para isso, vá a cada projeto de ambiente, na página IAM e clique em Conceder Acesso.

Untitled

  1. Adicione o e-mail da conta de serviço criada no projeto DevOps e conceda também Permissão de Editor (repita esse processo em cada projeto de ambiente). Untitled

  2. Infelizmente, diferente de Terragrunt, o código Terraform puro não cria automaticamente o bucket para o estado remoto. Então, vá a cada projeto de ambiente e crie um bucket para ser usado para armazenar o estado remoto de cada ambiente.

    Aviso: É altamente recomendado habilitar a versionamento do bucket.

Untitled

E isso é tudo para o GCP.

Configuração do Gitlab

No Gitlab, precisamos apenas configurar nossa Conta de Serviço como uma Variável.

  1. Na sua Página de Configurações do Gitlab, na Subseção CICD, crie uma variável chamada “SA” do tipo Arquivo.
  2. Cole o valor do JSON SA que você baixou nas etapas anteriores. Untitled

Estrutura de Código

Neste exemplo, estou usando uma estrutura de pastas como abaixo, mas você pode adaptá-la de acordo com suas necessidades.

terraform-infra-live/
├─ infra/
  ├─ stage/
    ├─ main.tf
  ├─ prod/
    ├─ main.tf
  ├─ dev/
    ├─ main.tf
├─ modules/
  ├─ gcp/
    ├─ gce/
    ├─ firewall-rule/
    ├─ gcs/

O código principal contém um código como abaixo, que você precisa alterar em cada arquivo main.tf os valores do bucket e seu ID de projeto para cada ambiente.

terraform {
  required_providers {
    google = "4.10.0"
  }

  backend "gcs" {
    bucket = "REPLACE-WITH-YOUR-BUCKET-NAME-dev"
    prefix = "terraform/state"
  }
}

provider "google" {
  project = "REPLACE-WITH-YOUR-PROJECT-ID-dev"
  region  = "us"
}

locals {
  project_id  = "REPLACE-WITH-YOUR-PROJECT-ID-dev"
  environment = "dev"
}
...

## Adicione seus módulos e o código restante...

Pipelines

Os pipelines são muito simples e criados para fluxos de trabalho baseados em recursos

  1. Um pipeline para Merge Request, onde as validações serão feitas. Untitled

  2. Um pipeline para a branch principal, onde o código será implantado automaticamente em cada ambiente não produtivo em paralelo, e o ambiente de produção manualmente. Untitled

O código

image:
  name: hashicorp/terraform:1.3.2
  entrypoint:
    - "/usr/bin/env"
    - "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"

stages:
- test
- deploy-non-prod
- deploy-prod

before_script:
- cp $SA /tmp/credentials.json
- export GOOGLE_APPLICATION_CREDENTIALS="/tmp/credentials.json"

.deploy: &deploy
  script:
    - terraform init
    - terraform plan -lock=false -out plan.json
    - terraform apply -auto-approve plan.json

test:
  stage: test
  script: 
    - terraform fmt --recursive -check
    - cd infra/$ENV_NAME
    - terraform init
    - terraform validate
    - terraform plan -lock=false
  parallel:
    matrix: 
      - ENV_NAME: [ dev, stage, prod ] 
  only:
    - main
    - merge_requests

deploy-non-prod:
  stage: deploy-non-prod
  script:
    - cd infra/$ENV_NAME
    - !reference [.deploy, script]
  parallel:
    matrix:
      - ENV_NAME: [ dev, stage ]
  only:
    - main
  dependencies:
    - test

deploy-prod:
  stage: deploy-prod
  script:
    - cd infra/$ENV_NAME
    - !reference [.deploy, script]
  parallel:
    matrix:
      - ENV_NAME: [ prod ]
  only:
    - main
  when: manual
  dependencies:
    - deploy-non-prod

after_script:
- rm /tmp/credentials.json

Outras Considerações

Como o pipeline realizará uma verificação de lint, é uma boa ideia executar um “terraform fmt -recursive” antes de enviar seu código.

Você pode verificar o exemplo completo de código no meu Github: https://github.com/andersondario/terraform-infra-gitlab-example


Suporte

Se você achar meus posts úteis e gostaria de me apoiar, por favor, me compre um café:

Anderson Dario é blog pessoal e blog de tecnologia

Isso é tudo. Obrigado.