Pipeline de IaC com GitLab, Terraform e GitLab-managed Terraform state
Tags: community, terraform, gitlab, cicd
Published: 2024-01-29 21:26:08

![](https://cdn-images-1.medium.com/max/2000/1*95y8xXsUas0tuF7-hyYtMw.png)

[Repositório GitLab.](https://gitlab.com/paulofponciano/iac-terraform-aws-demo-sqs-s3.git)

### Pré-requisitos

* Access Keys AWS

* GitLab

### Serviços AWS

* SQS

* S3

Usamos SQS e S3 a título de exemplo nesse laboratório.

### Construção

- Os passos abaixo são basicamente para preparar o repositório no GitLab, criando a *branch* 'development' e fazendo *commit* e *push* dos arquivos para ela:

![](https://cdn-images-1.medium.com/max/5748/1*mBeeutD_5EReMHjbW1IQaA.png)

![](https://cdn-images-1.medium.com/max/5740/1*zqCjAJz5O2wxCwo5LKDEeA.png)

- Criamos também a *branch* 'staging':

![](https://cdn-images-1.medium.com/max/5740/1*AoyaZpwDneQHUGcrkY3Kcg.png)

- No GitLab, crie o *environment* 'default', também 'development' e 'staging' caso ainda não tenha sido criados após o primero *push:*

![Environments](https://cdn-images-1.medium.com/max/3292/1*1cyNDDo66Bzq2z-pOoE89Q.png)

- Crie as variáveis no GitLab, onde cada 'AWS_ACCESS_KEY_ID' e 'AWS_SECRET_ACCESS_KEY' correspondem as contas de destino do provisionamento de acordo com *environment*:

![Variables](https://cdn-images-1.medium.com/max/3352/1*NLNjR-cC6-zKjWOPE2GM6g.png)*Variables*
> Cuidado com essas informações de chave!

- Execute o *pipeline* para a *branch* 'development'. Abaixo executamos manualmente, porém esse processo é automático assim que enviamos atualizações para *branch*:

![Run pipeline](https://cdn-images-1.medium.com/max/5716/1*TtFfr9YuaNIvNDlswABSUA.png)

![Pipeline](https://cdn-images-1.medium.com/max/5728/1*hVNMIeydKuJxL4lxICBSyA.png)

![](https://cdn-images-1.medium.com/max/5720/1*zDgUWlf4XjfaHNSyxdJlrQ.png)

- Execute o *pipeline* para a *branch* ‘staging’:

![Pipeline](https://cdn-images-1.medium.com/max/3810/1*HSqyb29X5dcqADzy9y79sw.png)

- *Merge request* de 'staging' para 'main', esse é o *deploy* em produção:

![New merge request](https://cdn-images-1.medium.com/max/5732/1*O4_-MSsSt5_2KE2zPg3TTA.png)

![New merge request](https://cdn-images-1.medium.com/max/3178/1*YQvRpFyS21Fid5vyLIUdLQ.png)

![Merge approval](https://cdn-images-1.medium.com/max/3184/1*g_XBnvm6jXZmDrBpgODenw.png)

![Pipeline](https://cdn-images-1.medium.com/max/3494/1*xij72nYAkUlnh8_V9sIUmA.png)

- Verifique os recursos provisionados em cada conta AWS. Note que, os recursos de *development* e *staging* estão na mesma conta, é porque utilizamos a mesma chave para os dois ambientes. Já os recursos de produção, estão em outra conta AWS:

![SQS Development and Staging](https://cdn-images-1.medium.com/max/3596/1*T8WzM77o3vXNRFskFEeQ2w.png)

![SQS Production](https://cdn-images-1.medium.com/max/3588/1*uNGX-h4W_Afzv9RY79h57g.png)

![S3 Production](https://cdn-images-1.medium.com/max/3074/1*aRb1B_DpDpgEu7ZGTQIwgw.png)

- Podemos também acionar manualmente o *stage cleanup* que realiza o *terraform destroy*:

![Pipeline](https://cdn-images-1.medium.com/max/3826/1*0aXvZiQ9V0EBleD53P-rDg.png)*Pipeline*

- Veja que o GitLab está cuidando dos *Terraform states* para nós:

![Terraform states](https://cdn-images-1.medium.com/max/3452/1*4uFmtO34FTIxU8KvxNA_yQ.png)*Terraform states*
> O arquivo **gitlab-ci.yml** é responsável por toda orquestração do pipeline:

```yaml
image: registry.gitlab.com/gitlab-org/terraform-images/stable:latest
    
workflow:
  rules:
    - if: $CI_COMMIT_BRANCH == "main"
      variables:
        ENV: "production"
    - if: $CI_COMMIT_BRANCH == "development"
      variables:
        ENV: "development"
    - if: $CI_COMMIT_BRANCH == "staging"
      variables:
        ENV: "staging"

variables:
  TF_ROOT: ${CI_PROJECT_DIR}
  TF_ADDRESS: ${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/terraform/state/${ENV}_tf_state

cache:
  key: ${ENV}_tf_state
  paths:
    - ${TF_ROOT}/.terraform

before_script:
  - cd ${TF_ROOT}

stages:
  - init
  - validate
  - build
  - deploy
  - cleanup

init_dev:
  stage: init
  script:
    - gitlab-terraform init
  environment:
    name: development
  only:
    - development 

validate_dev:
  stage: validate
  script:
    - gitlab-terraform validate
  environment:
    name: development
  only:
    - development

plan_dev:
  stage: build 
  script:
    - gitlab-terraform plan -var-file=${ENV}.tfvars 
    - gitlab-terraform plan-json -var-file=${ENV}.tfvars
  artifacts:
    name: plan
    paths:
      - ${TF_ROOT}/plan.cache
    reports:
      terraform:  ${TF_ROOT}/plan.json
  environment:
    name: development
  only:
    - development

apply_dev:
  stage: deploy
  script:
    - gitlab-terraform apply
  dependencies:
    - plan_dev
  environment:
    name: development
  only:
    - development

destroy_dev:
  stage: cleanup
  script:
    - gitlab-terraform destroy -var-file=${ENV}.tfvars
  dependencies:
    - plan_dev
    - apply_dev
  when: manual
  environment:
    name: development
  only:
    - development

init_qas:
  stage: init
  script:
    - gitlab-terraform init
  environment:
    name: staging
  only:
    - staging 

validate_qas:
  stage: validate
  script:
    - gitlab-terraform validate
  environment:
    name: staging
  only:
    - staging

plan_qas:
  stage: build 
  script:
    - gitlab-terraform plan -var-file=${ENV}.tfvars 
    - gitlab-terraform plan-json -var-file=${ENV}.tfvars
  artifacts:
    name: plan
    paths:
      - ${TF_ROOT}/plan.cache
    reports:
      terraform:  ${TF_ROOT}/plan.json
  environment:
    name: staging
  only:
    - staging

apply_qas:
  stage: deploy
  script:
    - gitlab-terraform apply
  dependencies:
    - plan_qas
  environment:
    name: staging
  only:
    - staging

destroy_qas:
  stage: cleanup
  script:
    - gitlab-terraform destroy -var-file=${ENV}.tfvars
  dependencies:
    - plan_qas
    - apply_qas
  when: manual
  environment:
    name: staging
  only:
    - staging

init_prd:
  stage: init
  script:
    - gitlab-terraform init
  environment:
    name: default
  only:
    - main 

validate_prd:
  stage: validate
  script:
    - gitlab-terraform validate
  environment:
    name: default
  only:
    - main

plan_prd:
  stage: build
  script:
    - gitlab-terraform plan -var-file=${ENV}.tfvars 
    - gitlab-terraform plan-json -var-file=${ENV}.tfvars
  artifacts:
    name: plan
    paths:
      - ${TF_ROOT}/plan.cache
    reports:
      terraform:  ${TF_ROOT}/plan.json
  environment:
    name: default
  only:
    - main    

apply_prd:
  stage: deploy
  script:
    - gitlab-terraform apply
  dependencies:
    - plan_prd
  environment:
    name: default
  only:
    - main

destroy_prd:
  stage: cleanup
  script:
    - gitlab-terraform destroy -var-file=${ENV}.tfvars
  dependencies:
    - plan_prd
    - apply_prd
  when: manual
  environment:
    name: default
  only:
    - main
```
> Este bloco definido como **workflow**, é responsável por identificar os ambientes com base na branch onde foi realizado o commit, inserindo essa informação na variável ENV:

```yaml
workflow:
  rules:
    - if: $CI_COMMIT_BRANCH == "main"
      variables:
        ENV: "production"
    - if: $CI_COMMIT_BRANCH == "development"
      variables:
        ENV: "development"
    - if: $CI_COMMIT_BRANCH == "staging"
      variables:
        ENV: "staging"
```

> Nos scripts de cada stage, utilizamos a variável ENV para determinar qual arquivo **tfvars** será utilizado, de acordo com cada environment (development.tfvars, staging.tfvars, production.tfvars):

```yaml
script:
  - gitlab-terraform plan -var-file=${ENV}.tfvars 
  - gitlab-terraform plan-json -var-file=${ENV}.tfvars
```

---

Happy building!