Examine the evolution of virtualization technologies from bare metal, virtual machines, and containers and the tradeoffs between them.
Install terraform and configure it to work with AWS
Learn the common terraform commands and how to use them
•Terraform Plan, Apply, Destroy
Use Terraform variables and outputs to improve make our configurations more flexible
Explore HCL language features in Terraform to create more expressive and modular infrastructure code.
Learn to break your code into modules to make it flexible and reuseable
Overview of two primary methods for managing multiple Terraform environments
Techniques for testing and validating Terraform code
Covers how teams generally work with Terraform, including automated deployment with CI/CD
In this lesson, we will learn how to automate the deployment of a Terraform configuration using GitHub Actions based on GitHub events.
We will set up a workflow that deploys our infrastructure to different environments (staging and production) and runs tests to ensure that the configuration is working as expected.
Create a new file named terraform.yml
in the .github/workflows
directory of your GitHub repository.
Add three different triggers for the workflow:
on:
push:
branches:
- main
release:
types:
- created
pull_request:
These are the major steps we want the workflow to handle.
These can be translated into the workflow yaml
as follows:
name: "Terraform"
on:
push:
branches:
- main
release:
types: [published]
pull_request:
jobs:
terraform:
name: "Terraform"
runs-on: ubuntu-latest
env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
defaults:
run:
working-directory: 07-managing-multiple-environments/file-structure/staging
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Setup Terraform
uses: hashicorp/setup-terraform@v1
with:
terraform_version: 1.0.1
terraform_wrapper: false
- name: Terraform Format
id: fmt
run: terraform fmt -check
- name: Terraform Init
id: init
run: terraform init
- name: Terraform Plan
id: plan
if: github.event_name == 'pull_request'
# Route 53 zone must already exist for this to succeed!
run: terraform plan -var db_pass=${{secrets.DB_PASS }} -no-color
continue-on-error: true
- uses: actions/github-script@0.9.0
if: github.event_name == 'pull_request'
env:
PLAN: "terraform\n${{ steps.plan.outputs.stdout }}"
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const output = `#### Terraform Format and Style 🖌\`${{ steps.fmt.outcome }}\`
#### Terraform Initialization ⚙️\`${{ steps.init.outcome }}\`
#### Terraform Plan 📖\`${{ steps.plan.outcome }}\`
<details><summary>Show Plan</summary>
\`\`\`${process.env.PLAN}\`\`\`
</details>
*Pusher: @${{ github.actor }}, Action: \`${{ github.event_name }}\`*`;
github.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: output
})
- name: Terraform Plan Status
if: steps.plan.outcome == 'failure'
run: exit 1
- uses: actions/setup-go@v2
with:
go-version: "^1.15.5"
- name: Terratest Execution
if: github.event_name == 'pull_request'
working-directory: 08-testing/tests/terratest
run: |
go test . -v timeout 10m
- name: Check tag
id: check-tag
run: |
if [[ ${{ github.ref }} =~ ^refs\/tags\/v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then echo ::set-output name=environment::production
elif [[ ${{ github.ref }} == 'refs/heads/main' ]]; then echo ::set-output name=environment::staging
else echo ::set-output name=environment::unknown
fi
- name: Terraform Apply Global
if: github.event_name == 'push' || github.event_name == 'release'
working-directory: 07-managing-multiple-environments/file-structure/global
run: |
terraform init
terraform apply -auto-approve
- name: Terraform Apply Staging
if: steps.check-tag.outputs.environment == 'staging' && github.event_name == 'push'
run: terraform apply -var db_pass=${{secrets.DB_PASS }} -auto-approve
- name: Terraform Apply Production
if: steps.check-tag.outputs.environment == 'production' && github.event_name == 'release'
working-directory: 07-managing-multiple-environments/file-structure/production
run: |
terraform init
terraform apply -var db_pass=${{secrets.DB_PASS }} -auto-approve
You will notice that the workflow references secrets.AWS_ACCESS_KEY_ID
and secrets.AWS_SECRET_ACCESS_KEY
. These must be created within GitHub and correspond to an AWS IAM user with sufficient permissions to deploy the corresponding infrastructure.
You can use the same IAM user you have been utilizing so for for local deploys.
Note: This example uses a long lived AWS_SECRET_ACCESS_KEY
. It is better practice to use OIDC to generate short lived credentials instead. For more information see: Configuring OpenID Connect in Amazon Web Services
With this configuration in place we can then test it by:
Commit and push the changes to the main
branch to trigger the GitHub Action workflow.
Issuing a new release to deploy to the production environment
Creating a pull request to run tests
git checkout -b your-feature-branch
git commit --allow-empty -m "Testing on PR"
git push origin your-feature-branch
After these github action workflows complete, you should have a staging
and production
copy of your infrastructure, deployed from the latest commit on main
as well as the latest repo release.
To avoid incurring additional costs, run terraform destroy
for each environment when you are finished testing.