Terraform -Automate CI/CD Workflows via GitHub Actions
Tags: terraform, aws, githubactions, github
Published: 2021-07-18 14:49:55

---
title: Terraform -Automate CI/CD Workflows via GitHub Actions
published: true
date: 2021-07-18 14:45:39 UTC
tags: Terraform,AWS,githubactions,GitHub
canonical_url: https://blog.g33kzone.com/terraform-github-actions
cover_image: https://cdn.hashnode.com/res/hashnode/image/upload/v1626416343859/DKeRlVrPX.png
series: terraform
---

This article will set up a CI/CD pipeline for our Terraform source code ( [refer post](https://dev.to/g33kzone/terraform-code-structure-3nhj) ) to spin AWS EC2 instance. The aim is to automate our development workflow by building the DevOps pipeline using GitHub Actions.

#### GitHub Actions

Before we proceed further let's understand GitHub Actions. It is a platform to automate tasks within the software development lifecycle. It's an `event-driven` framework, which means we can carry series of commands for a given event or can be scheduled for one-off or repetitive tasks. (e.g. Execute a Test Suite on Pull Request creation, Adding labels to issues, Lint checks, etc.)

It is fully integrated into GitHub. It also gives added advantage to store the source code and the CI/CD pipeline execution on the same platform. CI/CD pipeline is one of the automation workflow offerings to streamline the overall software development and delivery process.

![GitHub Flow.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1626437267724/igD_xUWLM.png)We can apply GitHub Actions, essentially to any stage of GitHub flow.

- Configure CI/CD
- Execute a specific automated task when an issue is opened
- Generate automate reminders for Pull Requests based on owners or reviewers

#### Workflow Strategy

Github allows us to create workflows in the following ways:

1. Create a YAML config (file name - \*.yml or \*.yaml) within the GitHub repository.
2. Create the workflow via Actions Tab on Github Repository's Web Interface.

![Github Actions Snapshot.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1626432916028/rEbYq59JZ.png)Clicking on `Set up this workflow` will pre-fill the required Terraform workflow.

For this article, we will focus on the first approach.

#### Prerequisites

-  [Fork ](https://docs.github.com/en/get-started/quickstart/fork-a-repo) the Github  Repository -  [tf-aws-ec2](https://github.com/g33kzone/tf-aws-ec2) 
- AWS User with Programmatic access
  - AWS Access Key ID
  - AWS Secret Access Key
- AWS IAM Privileges to create EC2 Instance

Recommended to create a feature branch and checkout this branch
```bash
git checkout -b github-actions-demo
``` 
Create a new file in the `.github/workflows` directory named `github-actions-demo.yml`
![Folder Structure.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1626438445661/Ia6w_M--s.png)

Now, let's start writing the configuration in the yaml file.

`name` - The name of your workflow. GitHub displays the names of your workflows on your repository's actions page - "Terraform Build Demo"

```yaml
name: 'Terraform Build Demo'

```

`on` - (_Required_) The name of the GitHub event that triggers the workflow. We have configured to trigger the workflow on `Pull Request` and `Push` events to the `main` branch.

- `Pull Request` event- Triggered when the Pull request will be raised for the new feature branch
- `Push` event - Triggered when the Pull Request is merged into the `main` branch.

```yaml
on:
  push:
    branches:
      - "main"
  pull_request:
    branches:
      - "main"

```

`jobs` - A workflow run is made up of one or more jobs. These jobs can run in parallel or sequentially. Each job executes in a runner environment specified by `runs-on`.

`job name` - The name of the job displayed on GitHub.

`runs-on` - (_Required_) Determines the type of machine to run the job on. The machine can be either a GitHub-hosted runner or a self-hosted runner. Available GitHub-hosted runner types are: windows-latest / windows-2019 / windows-2016 / ubuntu-latest / ubuntu-20.04 etc.

`environment` - The environment that the job references. All environment protection rules must pass before a job referencing the environment is sent to a runner.

```yaml
jobs:
  terraform:
    name: 'TF GitHub Actions Demo'
    runs-on: ubuntu-latest
    environment: production

```

`defaults.run` - Helps define default shell and working-directory options for all run steps in a workflow.

```yaml
    defaults:
      run:
        shell: bash

```

`steps` - Sequence of tasks called steps within a Job. They can execute commands, set up tasks, or run actions in your repository, a public repository, or action published in a Docker registry.

The first step is to check out the source code in the runner environment.

`Checkout V2`- This action checks out your repository under $GITHUB\_WORKSPACE, so your workflow can access it.

```yaml
    - name: Checkout
      uses: actions/checkout@v2

```

The second step is to set up Terraform CLI in the runner environment.

`setup-terraform` - is a JavaScript action that sets up Terraform CLI

```yaml
    - name: Setup Terraform
      uses: hashicorp/setup-terraform@v1
      with:
        terraform_version: 1.0.0

```

To ensure access to the AWS Cloud environment we need to configure `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY` in the runner environment. The values for these variables will be configured as GitHub Secrets in the below section.

`Configure AWS Credentials` - This action configures AWS credential and region environment variables for use in other GitHub Actions.

```yaml
    - name: Configure AWS Credentials
      uses: aws-actions/configure-aws-credentials@v1
      with:
        aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
        aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
        # aws-session-token: ${{ secrets.AWS_SESSION_TOKEN }} 
        # if you have/need it
        aws-region: us-east-1

```

The runner environment is now configured. We can now configure Terraform commands.

`run` - Runs command-line programs using the operating system's shell.

`Terraform Init` initializes the configuration used in the GitHub action workflow.

```yaml
- name: Terraform Init
  id: init
  run: terraform init

```

`Terraform Format` checks whether the configuration has been properly formatted. It will throw an error if the configuration isn't properly formatted.

```yaml
    - name: Terraform Format
      id: fmt
      run: terraform fmt -check
      env:
        TF_ACTION_WORKING_DIR: .
      continue-on-error: true

```

`Terraform Validate` validates the configuration used in the GitHub action workflow.

```yaml
- name: Terraform Validate
  id: validate
  run: terraform validate -no-color

```

`Terraform Plan` generates a Terraform plan.

- This step only runs on pull requests. The PR generates a plan. When the PR is merged, that plan will be applied.
- This step will continue even when it errors. This allows the next step to display the plan error message even if this step fails

`Terraform Plan Status` returns whether a plan was successfully generated or not.

```yaml
- name: Terraform Plan Status
  if: steps.plan.outcome == 'failure'
  run: exit 1

```

`Terraform Apply` applies the configuration. This step will only run when a commit is pushed to `main`

```yaml
- name: Terraform Apply
  if: github.ref == 'refs/heads/main' && github.event_name == 'push'
  run: terraform apply -auto-approve

```

The complete file `github-actions-demo.yml` will look as below.

```yaml
name: 'Terraform Build Demo'

on:
  push:
    branches:
      - "main"
  pull_request:
    branches:
      - "main"

jobs:
  terraform:
    name: 'TF GitHub Actions Demo'
    runs-on: ubuntu-latest
    environment: production

    defaults:
      run:
        shell: bash

    steps:
    - name: Checkout
      uses: actions/checkout@v2

    - name: Setup Terraform
      uses: hashicorp/setup-terraform@v1
      with:
        terraform_version: 1.0.0

    - name: Configure AWS Credentials
      uses: aws-actions/configure-aws-credentials@v1
      with:
        aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
        aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
        # aws-session-token: ${{ secrets.AWS_SESSION_TOKEN }} 
        # if you have/need it
        aws-region: us-east-1

    - name: Terraform Init
      id: init
      run: terraform init

    - name: Terraform Format
      id: fmt
      run: terraform fmt -check
      env:
        TF_ACTION_WORKING_DIR: .
      continue-on-error: true

    - name: Terraform Validate
      id: validate
      run: terraform validate -no-color

    - name: Terraform Plan
      id: plan
      if: github.event_name == 'pull_request'
      run: terraform plan -no-color
      continue-on-error: true

    - name: Terraform Plan Status
      if: steps.plan.outcome == 'failure'
      run: exit 1

    - name: Terraform Apply
      if: github.ref == 'refs/heads/main' && github.event_name == 'push'
      run: terraform apply -auto-approve

```

We need to also configure AWS credentials such that they are accessible to the GitHub Actions YAML script. The best way is to configure them as [GitHub Secrets](https://docs.github.com/en/actions/reference/encrypted-secrets#creating-encrypted-secrets-for-a-repository) against the repository.

Navigate to your GitHub repository on Web Console --> `Settings` --> `Secrets` (Left Nav Bar) --> Click `New Repository Secret`

Configure values for the following variables as GitHub Secrets:

- AWS\_ACCESS\_KEY\_ID
- AWS\_SECRET\_ACCESS\_KEY
- PERSONAL\_ACCESS\_TOKEN

For Personal Access Token (PAT) generation, refer to the following [GitHub docs](https://docs.github.com/en/github/authenticating-to-github/keeping-your-account-and-data-secure/creating-a-personal-access-token).

![GitHub Secrets.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1626614780733/CqPjncbCa.png)

Raise Pull Request for the new branch via Web Console. Refer following [GitHub docs](https://docs.github.com/en/github/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/about-pull-requests) for more information. ![GitHub PR.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1626614933973/uYfDorqFq.png)

Once the PR is raised, the GitHub Actions Job is triggered for the Pull Request event.

![Triggered.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1626615338537/fy_40p2M_.png)

On Clicking the `Details` link, we can see all the executed `Steps` and their corresponding logs.

![GitHub Actions Steps.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1626615503172/HPlT6lihq.png)

##### Observations:

- `Terraform Plan` run was successful, hence the `Terraform Plan Status` run execution was skipped due to the `failure` filter condition.
- The `Terraform Apply` run execution was also skipped - configured to be executed on the `PUSH` event.

#### Merge Pull Requests (PUSH event)

On merging the Pull request into the `main` branch. The configured GitHub actions workflow will be triggered again for the `PUSH` event.

![Github Push.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1626617708503/1lDuLpZcl.png)Click on the second workflow run of `GitHub Actions Demo`.

![Github Push Job Success.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1626618186801/bgH9SLVGl.png)Click on `TF GitHub Actions Demo`.

![GitHub TF Apply.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1626618296600/JxEWNRdKV.png)

##### Observations:

- `Terraform Plan` run was skipped as it will be triggered only on `PULL Request`.
- The `Terraform Apply` run was successfully executed - configured to be executed on the `PUSH` event. As a result, an AWS EC2 instance was created.

![AWS Console.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1626618925260/j1vCcOvTp.png)The existing setup is now capable of handling any changes (new/updates). The Terraform scripts will be automatically deployed to AWS Cloud once source code is merged into the `main` branch as demonstrated above.

#### Destroy resources
Remember to destroy the resources (i.e AWS EC2 instance) you created for this tutorial to avoid any costs.

Refer to the GitHub  [Repo](https://github.com/g33kzone/tf-aws-ec2-github-actions) for the source code demonstrated in the above post.