Gatekeep CodeCatalyst Workflow using Approval
Tags: aws, codecatalyst, cicd, terraform
Published: 2024-05-15 17:26:13

In some use cases, our CI/CD pipeline or workflow may need to be validated before code can be deployed.

Refer to this announcement "[Workflow approvals for Amazon CodeCatalyst](https://aws.amazon.com/about-aws/whats-new/2024/04/workflow-approvals-amazon-codecatalyst/)", Amazon CodeCatalyst, now has released feature called approval gates which can be utilized so that workflow can be paused until user with privilege can validate the workflow and whether workflow is approved and can be processed or not.

More details regarding Approval in CodeCatalyst can be read on this documentation: [Approval Gate](https://docs.aws.amazon.com/codecatalyst/latest/userguide/workflows-approval.html).

## Table of Contents

1. [Scenario](#scenario)
2. [Prerequisites](#prerequisites)
3. [Configuration Steps](#configuration-steps)
    - [Directory Structure](#directory-structure)
    - [CodeCatalyst Workflow File Configurations](#codecatalyst-workflow-file-configurations)
    - [Example of Approval Process in CodeCatalyst Workflow](#example-of-approval-process-in-codecatalyst-workflow)
4. [Summary](#summary)

## Scenario

This blog is related with my previous blog "[CI/CD Pipeline for Terraform Workflow Using Amazon CodeCatalyst](https://dev.to/aws-builders/cicd-pipeline-for-terraform-workflow-using-amazon-codecatalyst-3a4c)". In my previous blog, in workflow/pipeline, when terraform plan has finished and will continue to terraform apply, there is a process in between where pipeline will wait for 1 minute to check the terraform plan output and if the plan is not expected, we need to cancel the workflow manually. The difficulty that faced back then is because there was no out of the box solution provided by CodeCatalyst to restrict whether shifting between one action to other action can be cancelled if there is unexpected result.

In this blog, i will make an update which between terraform plan and terraform apply action, there will be an approval action. The goal if the plan is not expected, we can cancel the workflow and terraform apply will not be processed.

Here is the diagram that used in this blog:

![diagram](https://ludesdeveloper.files.wordpress.com/2024/05/diagram.png)

## Prerequisites

Here is the prerequisite that needed to follow configurations discussed in this blog:

- CodeCatalyst space and project that has been connected with AWS account and source repository. In this blog, I use GitHub as source repository. For configuration reference, please check my other blog “[Build and Release Container Image to Amazon Elastic Container Registry (ECR) via Amazon CodeCatalyst](https://aws.plainenglish.io/build-and-release-container-image-to-amazon-elastic-container-registry-ecr-via-amazon-ae7f9f07a135)”

## Configuration Steps

### Directory Structure

Below is directory structure used in this blog:

```
.
├── .codecatalyst/
│   └── workflows/
│       └── tf-sandbox-sbx0-vpc.yml
└── sandbox/
    └── sbx0/
        └── vpc/
            ├── backend.tf  
            ├── main.tf
            ├── terraform.tfvars
            └── variables.tf
```

### CodeCatalyst Workflow File Configurations

Here is the configuration of CodeCatalyst workflow file that written in file **./codecatalyst/workflows/tf-sandbox-sbx0-vpc.yml**

```yaml
Name: tf-sandbox-sbx0-vpc
SchemaVersion: "1.0"
 
Triggers:
  - Type: PUSH
    Branches:
      - master
    FilesChanged:
      - sandbox\/sbx0\/vpc\/.*
 
Actions:
  terraform-plan:
    Identifier: aws/build@v1
    Inputs:
      Sources:
        - WorkflowSource
    Environment:
      Name: sandbox
      Connections:
        - Name: lanandra-sandbox
          Role: codecatalyst-admin
    Configuration:
      Container:
        Registry: DockerHub
        Image: hashicorp/terraform:1.8.2
      Steps:
      - Run: cd sandbox/sbx0/vpc
      - Run: terraform fmt -check -no-color
      - Run: terraform init -no-color
      - Run: terraform validate -no-color
      - Run: terraform plan -no-color -input=false
    Compute:
      Type: EC2
  wait-for-approval:
    Identifier: aws/approval@v1
    DependsOn:
      - terraform-plan
    Configuration:
      ApprovalsRequired: 1
  terraform-apply:
    DependsOn:
      - wait-for-approval
    Identifier: aws/build@v1
    Inputs:
      Sources:
        - WorkflowSource
    Environment:
      Name: sandbox
      Connections:
        - Name: lanandra-sandbox
          Role: codecatalyst-admin
    Configuration:
      Container:
        Registry: DockerHub
        Image: hashicorp/terraform:1.8.2
      Steps:
      - Run: cd sandbox/sbx0/vpc
      - Run: terraform init -no-color
      - Run: terraform apply -auto-approve -no-color -input=false
    Compute:
      Type: EC2
```

Next, I will explain more detail each sections that defined on workflow file above:

1. Define workflow name. Then define how workflow will be ran. Workflow will be ran automatically if there is a push to repository, specifically to directory **sandbox/sbx0/vpc**

    ```yaml
    Name: tf-sandbox-sbx0-vpc
    SchemaVersion: "1.0"
     
    Triggers:
      - Type: PUSH
        Branches:
          - master
        FilesChanged:
          - sandbox\/sbx0\/vpc\/.*
    ```

2. Define action. First action will run workflow `terraform plan`. In this section, action uses identifier `aws/build@v1`. Then action will be ran on sandbox environment that has been connected with AWS account and also has IAM role associated with that account. In `Configuration` section, has been defined that action will be ran on top of container using specific version of terraform public image that pulled from DockerHub. Also has defined working directory and several terraform commands which include `terraform plan`. Lastly, define compute type which is `EC2`

    ```yaml
    Actions:
      terraform-plan:
        Identifier: aws/build@v1
        Inputs:
          Sources:
            - WorkflowSource
        Environment:
          Name: sandbox
          Connections:
            - Name: lanandra-sandbox
              Role: codecatalyst-admin
        Configuration:
          Container:
            Registry: DockerHub
            Image: hashicorp/terraform:1.8.2
          Steps:
          - Run: cd sandbox/sbx0/vpc
          - Run: terraform fmt -check -no-color
          - Run: terraform init -no-color
          - Run: terraform validate -no-color
          - Run: terraform plan -no-color -input=false
        Compute:
          Type: EC2
    ```

3. Next action is `wait-for-approval`. Goal of this action is to verify workflow can be ran to next action if there is an approval from privileged user(s). This action is using identifier `aws/approval@v1` and depends on previous action which is `terraform-plan`. This action need at least 1 privileged user approval so that next action can be ran.

    ```yaml
    wait-for-approval:
        Identifier: aws/approval@v1
        DependsOn:
          - terraform-plan
        Configuration:
          ApprovalsRequired: 1
    ```

4. Next action that defined is action to run `terraform apply`. This action more or less similar with action `terraform-plan`, the difference is just terraform command that declared on configuration. In this section, action will run commands that needed for `terraform apply`. This action is depends on previous action which is `wait-for-approval`

    ```yaml
    terraform-apply:
        DependsOn:
          - wait-for-approval
        Identifier: aws/build@v1
        Inputs:
          Sources:
            - WorkflowSource
        Environment:
          Name: sandbox
          Connections:
            - Name: lanandra-sandbox
              Role: codecatalyst-admin
        Configuration:
          Container:
            Registry: DockerHub
            Image: hashicorp/terraform:1.8.2
          Steps:
          - Run: cd sandbox/sbx0/vpc
          - Run: terraform init -no-color
          - Run: terraform apply -auto-approve -no-color -input=false
        Compute:
          Type: EC2
    ```

### Example of Approval Process in CodeCatalyst Workflow

I have defined Terraform codes that will be used as example in this blog. Files can be found in this GitHub path: [https://github.com/lanandra/mock-aws-infrastructure/tree/master/sandbox/sbx0/vpc](https://github.com/lanandra/mock-aws-infrastructure/tree/master/sandbox/sbx0/vpc)

Existing **main.tf** configuration:

```go
#######
# VPC #
#######
 
 
data "aws_availability_zones" "available" {}
 
locals {
  azs = slice(data.aws_availability_zones.available.names, 0, 3)
 
  tags = {
    "environment" = "${var.env_name}"
    "managedBy"   = "terraform"
  }
}
 
module "sbx0_apse1" {
  source  = "terraform-aws-modules/vpc/aws"
  version = "5.8.1"
 
  name = var.env_name
  cidr = var.vpc_cidr
 
  azs             = local.azs
  private_subnets = [for k, v in local.azs : cidrsubnet(var.vpc_cidr, 8, k)]
  public_subnets  = [for k, v in local.azs : cidrsubnet(var.vpc_cidr, 8, k + 3)]
 
  enable_nat_gateway = false
  single_nat_gateway = true
 
  tags = local.tags
}
```

Here are the configuration steps:

1. Change Terraform configuration. Enable NAT gateway in VPC

    ```go
    enable_nat_gateway = true
    ```

2. As defined in workflow file, all changes inside directory **sandbox/sbx0/vpc** will trigger workflow run. Go to CodeCatalyst web console to verify. Go to space and project where workflow ran. Go to CI/CD menu, then choose Workflows. Verify there is new runs. Go to that runs

    ![find-workflows-and-run](https://ludesdeveloper.files.wordpress.com/2024/05/1.-find-workflows-and-run.png)

3. Go to run details by clicking name or run. Next, we will be redirected to overview page of that run. Click action `terraform-plan`, on the right side will appear tray box that display all steps that configured on that action. If we want to see details of `terraform plan`, go to tab `log`, then click step `terraform plan -no-color -input-false`. Verify action is succeeded

    ![verify-terraform-plan](https://ludesdeveloper.files.wordpress.com/2024/05/2.-verify-terraform-plan.png)

4. Next, action `wait-for-approval` will be ran and waiting input from privileged user, whether workflow run can be approved or not

    ![wait-for-approval](https://ludesdeveloper.files.wordpress.com/2024/05/3.-wait-for-approval.png)

5. Choose approve then click submit

    ![approve-wait-approval](https://ludesdeveloper.files.wordpress.com/2024/05/4.-approve-wait-approval.png)

6. If pop-up approval appeared, click `Confirm` to continue workflow

    ![confirm-approval](https://ludesdeveloper.files.wordpress.com/2024/05/5.-confirm-approval.png)

7. We also can see the status of approval, whether it has been approved or rejected

    ![approval-summary](https://ludesdeveloper.files.wordpress.com/2024/05/6.-approval-summary.png)

8. After approval has been approved, next action will be ran where in this blog example is terraform apply. Verify terraform apply is succeeded

    ![terraform-apply-succeeded](https://ludesdeveloper.files.wordpress.com/2024/05/7.-terraform-apply-succeeded.png)

## Summary

We have reached the last section of this blog. Here are some key takeaways that can be summarized:

- In some use cases, maybe we will find condition where we will need approval before deployment in CI/CD pipeline can be done
- CodeCatalyst have approval feature that can gatekeep deployment in CI/CD

Please comment if you have any suggestions, critiques, or thoughts.

Hope this article will benefit you. Thank you!