Important
This reusable workflow enables you to plan and apply changes to Terraform configurations in bulk with pull request (PR) comments: for a CLI-like experience on the web UI. It's powered by GitHub Actions to maximize compatibility and minimize maintenance for DIY deployments. It's ready-made for AWS as a functional example, and can easily be extended to support other cloud providers.
Overview · Usage [Workflow · Examples · Parameters · AWS] · Security · Roadmap · Contributions · License
Terraform is a platform-agnostic tool for managing cloud and on-prem resources by provisioning infrastructure as code (IaC).
- It enables you to define resources in human-readable configuration files that can be version controlled and shared for consistent state management.
GitHub Actions is a continuous integration and continuous deployment (CI/CD) platform that enables you to automate your project's pipelines with custom workflows.
- This repository hosts a reusable workflow that parses PR comments for Terraform commands and runs them in a remote environment.
- Also supports GitHub Codespaces dev container, which offers a tailored Terraform development environment, complete with tools and runtimes to lower the barrier to entry for contributors.
Best suited for DevOps and Platform engineers who want to empower their teams to self-service Terraform without the overhead of self-hosting runners, containers or VMs like Atlantis.
- Environment deployment protection rules mitigate the risk of erroneous changes along with standardized approval requirements.
- Each PR and associated workflow run holds a complete log of infrastructure changes for ease of collaborative debugging as well as audit compliance.
Copy the following snippet into ".github/workflows/terraform.yml" file in your repository. Replace the contents of env_vars
with environment variables required by your Terraform configuration.
on:
issue_comment:
types: [created, edited]
pull_request:
types: [synchronize]
jobs:
terraform:
uses: devsectop/tf-via-pr/.github/workflows/tf.yml@main
secrets:
env_vars: |
AWS_ACCESS_KEY_ID=${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY=${{ secrets.AWS_SECRET_ACCESS_KEY }}
Note
- Pin your workflow version to a specific release tag or SHA to harden your CI/CD pipeline security against supply chain attacks.
- The optional
env_vars
input lets you pass in environment variables as key-value pairs while masking sensitive values from logs.- Each entry must be on a new line and separated by an equals sign (
=
). - Entries prefixed with
BASE64_
will be decoded from Base64 twice. E.g., for passing in temporary/OIDC credentials output from a previous job.
- Each entry must be on a new line and separated by an equals sign (
Use-case scenario: Provision resources in multiple workspaces with different input variables, followed by targeted destruction. View PR in situ.
#1 PR Comment: Plan configuration in a workspace with input variable file.
-terraform=plan -chdir=stacks/sample_instance -workspace=dev -var-file=env/dev.tfvars
#2 PR Comment: Apply configuration in a workspace with input variable file.
-terraform=apply -chdir=stacks/sample_instance -workspace=dev -var-file=env/dev.tfvars
#3 PR Comment: Plan destruction of targeted resources in a workspace with input variable file.
-terraform=plan -destroy -target=aws_instance.sample,data.aws_ami.ubuntu -chdir=stacks/sample_instance -workspace=dev -var-file=env/dev.tfvars
#4 PR Comment: Apply destruction of targeted resources in a workspace with input variable file.
-terraform=apply -destroy -target=aws_instance.sample,data.aws_ami.ubuntu -chdir=stacks/sample_instance -workspace=dev -var-file=env/dev.tfvars
Use-case scenario: Provision resources with multiple different backends in bulk, simultaneously, followed by destruction without confirmation. View PR in situ.
#1 PR Comment: Plan multiple configurations with different backends.
-terraform=plan -chdir=stacks/sample_bucket -backend-config=backend/dev.tfvars
-terraform=plan -chdir=stacks/sample_bucket -backend-config=backend/stg.tfvars
#2 PR Comment: Apply multiple configurations with different backends.
-terraform=apply -chdir=stacks/sample_bucket -backend-config=backend/dev.tfvars
-terraform=apply -chdir=stacks/sample_bucket -backend-config=backend/stg.tfvars
#3 PR Comment: Destroy multiple configurations with different backends without confirmation.
-terraform=apply -destroy -auto-approve -chdir=stacks/sample_bucket -backend-config=backend/dev.tfvars
-terraform=apply -destroy -auto-approve -chdir=stacks/sample_bucket -backend-config=backend/stg.tfvars
Name | Description | Default | Example |
---|---|---|---|
CONFIG_TF_CHDIR_PREFIX |
String prefix for Terraform -chdir argument. This is a global option that switches to a different directory. |
stacks/ | |
CONFIG_TF_REQUIRE_APPROVAL |
Boolean flag to require PR review approval for Terraform apply commands. Consider deployment protection rules for specific environments. | false | true |
CONFIG_TF_VAR_FILE_PREFIX |
String prefix for Terraform -var-file argument, if -var-file (or -workspace and CONFIG_TF_WORKSPACE_AS_VAR_FILE ) is supplied. |
../vars/ | |
CONFIG_TF_VAR_FILE_SUFFIX |
String suffix for Terraform -var-file argument, if -var-file (or -workspace and CONFIG_TF_WORKSPACE_AS_VAR_FILE ) is supplied. |
.tfvars | |
CONFIG_TF_WORKSPACE_AS_VAR_FILE |
Boolean flag to re-use Terraform -workspace as -var-file argument, if either of them are supplied. |
false | true |
TF_CLI_HOSTNAME |
Hostname of Terraform cloud/enterprise instance to place within the credentials block of Terraform CLI configuration. | app.terraform.io | tf.acme.com |
TF_CLI_TOKEN |
API token for Terraform cloud/enterprise instance to place within the credentials block of Terraform CLI configuration. | ||
TF_CLI_VERSION |
Version of Terraform CLI to install, supporting semver ranges. | latest | >=1.5.1 |
Name | Description | Example |
---|---|---|
COMMENT_SHA |
SHA of the PR comment that triggered the workflow. | 1234567… |
PARSED_COMMENT |
JSON object of the parsed PR comment. | [{"terraform":"plan", "chdir":"stacks/sample_bucket"}] |
PROMPT_MATRIX |
Matrix strategy of the last successfully completed job. | {"terraform":"plan", "chdir":"stacks/sample_bucket"} |
TF_PLAN_ID |
Unique identifier of the Terraform plan file, used for artifact upload/download and bot comment update. | 42stacks-sample-bucket-tfplan |
WORKING_DIRECTORY |
Working directory of the Terraform configuration, used in -chdir argument. |
stacks/sample_bucket |
Environment isolation is achieved by nesting directories (e.g., stacks/), each with their own providers, to enable management of multiple: backends, workspaces and variable files from a single repository.
Reusable, stateless components can be placed within a dedicated directory (e.g., "stacks/modules/") to be imported into each environment like so.
module "sample_bucket" {
source = "../modules/s3_bucket"
Note
- For OIDC authentication, the aws-actions/configure-aws-credentials action can be used to pass short-lived credentials.
- An example of such a workflow is given in caller_aws.yml.
Integrating security in your CI/CD pipeline is critical to practicing DevSecOps. This reusable workflow is designed to be secure by default, and it should be complemented with your own review to ensure it meets your (organization's) security requirements.
- All associated GitHub Actions used in this workflow are pinned to a specific SHA to prevent supply chain attacks from third-party upstream dependencies.
- Restrict changes to certain environments with deployment protection rules so that approval is required from authorized users/teams before changes to the infrastructure can be applied.
- Pass environment variables to the workflow as secrets to prevent sensitive values from being exposed in logs.
- The
secrets.env_vars
input allows any number of key-value pairs to be passed to the workflow for use as masked environment variables, enabling you to customize the workflow to your Terraform configuration. - GitHub Actions has no official support for passing secrets from a prior job in the caller workflow to a reusable workflow. Tracked in discussion#13082, discussion#17554 and discussion#26671.
- The
- Parse PR comments as input commands to interface with Terraform CLI, returning the output as bot comments.
- Opted to use hashicorp/setup-terraform with a custom wrapper script to enable parsing of PR comments as input commands with CLI arguments.
- Use GitHub's reusable workflow or composite actions for CI/CD of Terraform configuration changes.
- Opted for reusable workflow due to more granular control over workflow execution: from managing
concurrency
of queued workflows to running jobs in parallel withstrategy.matrix
. - Adapted ternary operator-like behavior to enable if-else logic within GitHub Actions expressions.
- Unlike
pull_request
,issue_comment
events can only be triggered to run on the default branch, which complicates testing changes to the reusable workflow. - When the workflow is run with matrix strategy, the output is set by the last successfully completed job rather than a combination of all jobs.
- Opted for reusable workflow due to more granular control over workflow execution: from managing
All forms of contribution are very welcome and deeply appreciated for fostering open-source software.
- Please create a PR to contribute changes you'd like to see.
- Please raise an issue to discuss proposed changes or report unexpected behavior.
- Please open a discussion to share ideas about where you'd like to see this project go.
- Please consider becoming a stargazer if you find this project useful.
- This project is licensed under the permissive Apache License 2.0.
- All works herein are my own and shared of my own volition.
- Copyright 2023 Rishav Dhar — All wrongs reserved.