In this guide, we’ll walk through the process of securely deploying infrastructure to AWS using Terraform and GitHub Actions. This pipeline demonstrates how to create a reusable template for planning and applying Terraform configurations, while maintaining security and efficiency.
Prerequisites
Before proceeding, ensure you have the following:
- AWS Access Key and Secret Key: You’ll need an AWS Access Key and Secret Key to authenticate with AWS services. Follow this guide to create them.
- GitHub Secrets: Add your AWS Access Key and Secret Key as secrets in your GitHub repository. These secrets will be used by GitHub Actions to authenticate with AWS.
Pipeline Overview
The pipeline consists of two main stages: planning (terraform-plan.yml
) and applying (terraform-apply.yml
). Each stage utilizes reusable templates and GitHub Actions to automate the Terraform deployment process.
Code Explanation
terraform-plan.yml
This YAML file defines the workflow for planning Terraform configurations. It accepts input parameters such as the Terraform root path, version, TFVARS file, AWS backend settings, and AWS credentials as secrets.
name: "TF_Plan"
on:
workflow_call:
inputs:
path:
description: "Terraform Root Path"
required: true
type: string
tf_version:
description: 'Terraform Version. e.g: 1.3.0 Default=latest.'
required: false
type: string
default: latest
tf_vars_file:
description: 'Terraform TFVARS file name.'
required: true
type: string
aws_backend_bucket_name:
description: 'AWS S3 bucket name to store terraform state file.'
required: true
type: string
aws_backend_bucket_prefix:
description: 'AWS S3 bucket folder name to store terraform state file.'
required: true
type: string
aws_backend_region:
description: 'AWS S3 bucket region'
required: true
type: string
aws_backend_dynamodb_table:
description: 'AWS dynamodb table name'
required: true
type: string
aws_backend_encrypt:
description: 'AWS storage encryption'
required: true
type: boolean
# environment:
# description: 'manual approvals in GitHub Actions with the Environments.'
# required: true
# type: string
secrets:
AWS_ACCESS_KEY_ID:
description: 'AWS Access key ID'
required: true
AWS_SECRET_ACCESS_KEY:
description: 'AWS Secret Access Key'
required: true
jobs:
build-apply:
runs-on: ubuntu-latest
# environment: ${{ inputs.environment }}
defaults:
run:
shell: bash
working-directory: ${{ inputs.path }}
env:
aws_backend_bucket_name: ${{ inputs.aws_backend_bucket_name }}
aws_backend_bucket_prefix: ${{ inputs.aws_backend_bucket_prefix }}
aws_backend_region: ${{ inputs.aws_backend_region }}
aws_backend_dynamodb_table: ${{ inputs.aws_backend_dynamodb_table }}
aws_backend_encrypt: ${{ inputs.aws_backend_encrypt }}
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
TF_VARS: ${{ inputs.tf_vars_file }}
steps:
- name: Checkout
uses: actions/checkout@v4
- name: tfsec
uses: aquasecurity/[email protected]
with:
version: latest
soft_fail: true
- name: Setup Terraform
uses: hashicorp/setup-terraform@v3
with:
terraform_version: ${{ inputs.tf_version }}
- name: Terraform Init
run: terraform init --backend-config="bucket=$aws_backend_bucket_name" --backend-config="key=$aws_backend_bucket_prefix" -backend-config="region=$aws_backend_region" -backend-config="dynamodb_table=$aws_backend_dynamodb_table" -backend-config="encrypt=$aws_backend_encrypt"
- name: Terraform Validate
run: terraform validate
- name: Terraform Plan
id: plan
run: terraform plan --var-file=$TF_VARS --out=plan.tfplan
continue-on-error: true
- name: Terraform Plan Status
if: steps.plan.outcome == 'failure'
run: exit 1
- name: Compress TF Plan artifact
run: zip -r ${{ inputs.aws_backend_bucket_name }}.zip ./*
- name: Upload Artifact
uses: actions/[email protected]
with:
name: "${{ inputs.aws_backend_bucket_name }}"
path: "${{ inputs.path }}/${{ inputs.aws_backend_bucket_name }}.zip"
retention-days: 5
terraform-apply.yml
Similar to the previous file, this YAML file handles the application of Terraform configurations. It downloads the artifact generated during the planning stage and applies the changes to the infrastructure.
name: "TF_Apply"
on:
workflow_call:
inputs:
path:
description: "Terraform Root Path"
required: true
type: string
tf_version:
description: 'Terraform Version. e.g: 1.3.0 Default=latest.'
required: false
type: string
default: latest
tf_vars_file:
description: 'Terraform TFVARS file name.'
required: true
type: string
aws_backend_bucket_name:
description: 'GCP Storage bucket name to store terraform state file.'
required: true
type: string
aws_backend_bucket_prefix:
description: 'GCP Storage bucket folder name to store terraform state file.'
required: true
type: string
aws_backend_region:
description: 'GCP Storage bucket folder name to store terraform state file.'
required: true
type: string
aws_backend_dynamodb_table:
description: 'GCP Storage bucket folder name to store terraform state file.'
required: true
type: string
aws_backend_encrypt:
description: 'AWS storage encryption'
required: true
type: boolean
default: false
# environment:
# description: 'manual approvals in GitHub Actions with the Environments.'
# required: true
# type: string
secrets:
AWS_ACCESS_KEY_ID:
description: 'AWS Access key ID'
required: true
AWS_SECRET_ACCESS_KEY:
description: 'AWS Secret Access Key'
required: true
jobs:
build-apply:
runs-on: ubuntu-latest
# environment: ${{ inputs.environment }}
defaults:
run:
shell: bash
working-directory: ${{ inputs.path }}
env:
aws_backend_bucket_name: ${{ inputs.aws_backend_bucket_name }}
aws_backend_bucket_prefix: ${{ inputs.aws_backend_bucket_prefix }}
aws_backend_region: ${{ inputs.aws_backend_region }}
aws_backend_dynamodb_table: ${{ inputs.aws_backend_dynamodb_table }}
aws_backend_encrypt: ${{ inputs.aws_backend_encrypt }}
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
TF_VARS: ${{ inputs.tf_vars_file }}
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Download Artifact
uses: actions/[email protected]
with:
name: ${{ inputs.aws_backend_bucket_name }}
path: ${{ inputs.path }}
- name: Decompress TF Plan artifact
run: echo "y" | unzip -o ${{ inputs.aws_backend_bucket_name }}.zip
- name: Setup Terraform
uses: hashicorp/setup-terraform@v3
with:
terraform_version: ${{ inputs.tf_version }}
- name: Terraform Init
run: terraform init --backend-config="bucket=$aws_backend_bucket_name" --backend-config="key=$aws_backend_bucket_prefix" -backend-config="region=$aws_backend_region" -backend-config="dynamodb_table=$aws_backend_dynamodb_table" -backend-config="encrypt=$aws_backend_encrypt"
- name: Terraform Apply
run: terraform apply plan.tfplan
pipeline.yml
The main pipeline file (pipeline.yml
) orchestrates the entire process. It calls the planning and applying stages sequentially, passing the necessary parameters and secrets.
name: 'Infra_build'
on:
push:
branches:
- main
pull_request:
permissions:
contents: read
jobs:
Dev_Plan:
uses: littleworks-inc/aws_terraform_demo/.github/workflows/terraform-pan.yml@main
with:
path: .
tf_version: latest
aws_backend_bucket_name: devopsdemobucket01
aws_backend_bucket_prefix: state/terraform.tfstate
aws_backend_region: ca-central-1
aws_backend_dynamodb_table: devtoolhub
aws_backend_encrypt: true
tf_vars_file: dev.tfvars
# environment: dev
secrets:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
Dev_Deploy:
needs: Dev_Plan
uses: littleworks-inc/aws_terraform_demo/.github/workflows/terraform-apply.yml@main
with:
path: .
tf_version: latest
aws_backend_bucket_name: devopsdemobucket01
aws_backend_bucket_prefix: state/terraform.tfstate
aws_backend_region: ca-central-1
aws_backend_dynamodb_table: devtoolhub
aws_backend_encrypt: true
tf_vars_file: dev.tfvars
# environment: dev
secrets:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
Demo
To see this pipeline in action, visit the GitHub repository. Fork the repository and explore the code to understand how Terraform and GitHub Actions are integrated to deploy infrastructure securely on AWS.
Conclusion
Deploying infrastructure to AWS using Terraform and GitHub Actions offers a robust and automated approach. By following the steps outlined in this guide, you can streamline your deployment process and maintain a high level of security.
Start deploying your infrastructure efficiently with Terraform and GitHub Actions today!