Skip to main content

Infrastructure CI Migration to GitHub Actions

This runbook outlines the steps to prepare a typical NVVS Terraform Infrastructure for migration to GitHUb Actions as per ADR 011 - Use GitHub Actions for CI/CD.

Benefits

The procedure for migrating to GitHub Actions for CI will require some housekeeping and minor refactoring is necessary to enable the GitHub Action CI deployments. It will provide the following benefits.

  • Quicker deployments.
  • Simple branch build and deploy.
  • Clearer sequence display and log viewing for engineers to understand the context of deployment processes in action.
  • Terraform version will be defined in one location(instead of Readme, buildspec or latest pulled in).
  • Retrieval of TF_VAR inputs will be scripted and consistent (no need to assemble values form various sources indicated in example files and Readme).
  • The above will enable quicker on-boarding of engineers.
  • Linting and Security tools will enable overview of as yet undefined issues.

Stages

Initially the project repository will be prepared to run terraform plan on GitHub. When this is achieved the AWS CodeBuild pipeline can be disabled; prior to full migration to GitHub. The Terraform state and Dynamo DB state locking AWS resources would be managed via another Terraform Module or the current module would have option to remove the AWS Codepipeline resources.

At the time of writing we can reference the branch of network-access-control-infrastructure for examples to reference.

Overview

  • Makefile update
  • README update
  • Terraform Version
  • Terraform Inputs (TF_VAR)
  • GitHub Permissions & GitHub Secrets
  • Test GitHub Action on branch
  • Test Static Code Analysis

Makefile update

For consistency ensure the Makefile has all the commands here:

#!make
include .env
export

fmt:
    aws-vault exec $$AWS_VAULT_PROFILE -- terraform fmt --recursive

init:
    aws-vault exec $$AWS_VAULT_PROFILE -- terraform init -reconfigure \
    --backend-config="key=terraform.$$ENV.state"

workspace-list:
    aws-vault exec $$AWS_VAULT_PROFILE -- terraform workspace list

workspace-select:
    aws-vault exec $$AWS_VAULT_PROFILE -- terraform workspace select $$ENV || \
    aws-vault exec $$AWS_VAULT_PROFILE -- terraform workspace new $$ENV

validate:
    aws-vault exec $$AWS_VAULT_PROFILE -- terraform validate

plan-out:
    aws-vault exec $$AWS_VAULT_PROFILE -- terraform plan -no-color > $$ENV.tfplan

plan:
    aws-vault exec $$AWS_VAULT_PROFILE -- terraform plan

refresh:
    aws-vault exec $$AWS_VAULT_PROFILE -- terraform refresh

output:
    aws-vault exec $$AWS_VAULT_PROFILE -- terraform output -json

apply:
    aws-vault exec $$AWS_VAULT_PROFILE -- terraform apply

state-list:
    aws-vault exec $$AWS_VAULT_PROFILE -- terraform state list

show:
    aws-vault exec $$AWS_VAULT_PROFILE -- terraform show -no-color

destroy:
    aws-vault exec $$AWS_VAULT_PROFILE -- terraform destroy

clean:
    rm -rf .terraform/ terraform.tfstate*

README update

Find the appropriate place to add the following snippet into the README.md or other documentation file.

This file is no longer necessary. The necessary TF*VARS that are required from the SSM Parameter Store and used by Terraform are for local development and testing written to the `.env` file that the Makefile sources. The values are exported in the shell's environment as `TF_VAR*{variable_name}`.
Provided the following have been set in your shell's environment
```shell
export AWS_PROFILE=mojo-shared-services-cli
export AWS_VAULT_PROFILE=mojo-shared-services-cli
```
You can run from the root of this project the following script.
```shell
./scripts/generate-env-file.sh [environment_name: development|pre-production|production]
```
An `.env` file will be produced for the environment, if you need to test or check a plan against another environment rerun the script.
```

Terraform Version

Each Terraform project will have a version.tf file as per Hashcorp advised convention in the root of the project. It will have at a minimum a terraform block with the required_version specified. If the version has already been spefified in the terraform block in main.tf remove from there and add as illustrated.

terraform {
  required_version = "1.1.8"
}

This convention enables easy retrieval of the Terraform version by automation scripts and for local use with the tfenv tool. The alias below will install the required Terraform version loaccly and pin it for the project.

alias tfenvuse='tfenv use $(cat versions.tf 2> /dev/null | grep required_version | cut -d "\"" -f 2 | cut -d " " -f 2) && tfenv pin'

Terraform Inputs (TF_VAR)

Getting the correct values for .env files and terraform.tfvars files from the various locations is time consuming and doesn’t allow a new engineer to get familiar with the tooling or project with any confidence. A script will gather the required values and produce an .env for the specified environment which will be exported into the shell’s environment when a command is run via the make target (init, plan, apply etc). The script will use the existing information to inform what is necessary, the env section of the buildspec.yml is the most reliable source of required variables.

Copy the scripts from network-access-control-infrastructure/scripts to the current project’s scripts directory (create if not already exists).

mkdir -p scripts && \
cp ../network-access-control-infrastructure/scripts/aws_ssm_get_parameters.sh scripts/ && \
cp ../network-access-control-infrastructure/scripts/generate-env-file.sh scripts/ && \
cp ../network-access-control-infrastructure/scripts/generate-github-env.sh scripts/

The scripts generate-env-file.sh and generate-github-env.sh are similar. The first is used locally the second within the GitHub Action Workflow file to generate an .env file or inject into the special GITHUB_ENV variable.

Script Use Change
[aws_ssm_get_parameters.sh][get] Retrieve SSM Params most
[generate-env-file.sh][env] locally generate an .env lines 72 -> 88
[generate-github-env.sh][hub] GitHub Action Workflow lines 11 -> 23

With reference to the buildspec.yml up the lines as indicated in the table with the values from the env:variables: yaml block in the generate-* script files.

The aws_ssm_get_parameters.sh needs updating with the values ffrom the env:parameter-store: yaml block. The SSM Parameters need to be retrieved in blocks of ten. Due to variable names sometimes changing from parameter store to their use in Terraform a line for each need to be written in which they’re mapped into an array for use in the other two scripts. Note: start with one param, test then add and test.

./scripts/generate-env-file.sh [environment_name: development|pre-production|production]

Now to run the Terraform the Makefile will only require the .env to provide the variables. Test before proceding.

GitHub Permissions & GitHub Secrets

The GitHub repository will likely need adding to the OIDC Provider and the AWS Role added as a GitHub secret. role-to-assume: ${{ secrets.AWS_ROLE_TO_ASSUME }} Checkout the repo mojo-aws-github-oidc-provider and add the name of the GitHub repository to the locals.tf

The code needs to be run from your local machine (it can’t apply the code to it’s self (yet). See that project’s README file for full details.

Test GitHub Action on branch

Most project already have GitHub Actions Workflows although they don’t require AWS credentials to run such as formatting and Dependabot actions; hence the .github/workflows directory will likely already exist. Add the workflow files from network-access-control-infrastructure/.github/workflows to the current project’s scripts directory (create if not already exists).

mkdir -p .github/workflows && \
cp ../network-access-control-infrastructure/.github/workflows/terraform-apply.yaml .github/workflows/ && \
cp ../network-access-control-infrastructure/.github/workflows/terraform-static-analysis.yml .github/workflows/

Those file don’t need any changes, commit and push them. Currently they will only run terraform plan when there is a PR for the main branch or a push to a branch ending with -GHTEST. To test you branch create a temporary branch from your current one and append -GHTEST

git checkout -b {your_branch_name}-GHTEST
git push --set-upstream origin {your_branch_name}-GHTEST

Now you should see under the Actions tab thes two workflows https://github.com/ministryofjustice/{project_name}/actions. Check the results confirm they run.

Checklist

[get]: https://github.com/ministryofjustice/network-access-control-infrastructure/tree/227-update-makefile-readme-1/scripts/aws_ssm_get_parameters.sh [env]: https://github.com/ministryofjustice/network-access-control-infrastructure/tree/227-update-makefile-readme-1/scripts/generate-env-file.sh [hub]: https://github.com/ministryofjustice/network-access-control-infrastructure/tree/227-update-makefile-readme-1/scripts/generate-github-env.sh

This page was last reviewed on 12 April 2024. It needs to be reviewed again on 12 October 2024 by the page owner #nvvs-devops .
This page was set to be reviewed before 12 October 2024 by the page owner #nvvs-devops. This might mean the content is out of date.