Terraform is a platform-agnostic tool which can orchestrate AWS infrastructure as code (IaC). This reusable workflow enables deployment of multiple environments via GitHub Actions. Use-cases include directory-based environment isolation and management of multiple backends/workspaces from a single repository.
- Provision a Terraform backend to store configuration state files.
- Store AWS credentials as Actions secrets to reference as environment variables.
-
Reference the reusable Terraform workflow and pass in environment variables as secrets.
uses: o11y-top/aws-terraform-multiple-environments/.github/workflows/terraform.yml@3 secrets: env: | AWS_ACCESS_KEY_ID=${{ secrets.AWS_ACCESS_KEY_ID }} AWS_SECRET_ACCESS_KEY=${{ secrets.AWS_SECRET_ACCESS_KEY }}
-
Add workflow triggers, as shown in terraform-runner.yml.
-
Add IaC configurations to a nested folder within environments directory.
-
Configure the backend.tf which is shared between all environments.
PRs which modify IaC will automatically add a label corresponding to the directory name. For example, changes within "environments/demo" would be labelled with tf:demo
.
We can override these labels before merging the PR to apply the generated plans. Read on for more usage details and features.
Environment isolation is achieved by nesting directories under environments with their own providers.tf in addition to the shared backend.tf.
Reusable, stateless components can be placed in the modules directory to be imported into each environment. For example:
module "vpc" {
source = "../../modules/network"
The label-driven workflow lets us opt-in/out of deploying our IaC to multiple environments and review each of their planned outputs in a single PR.
While state locking is recommended on the backend, the workflow features built-in concurrency control to prevent overlapping runs.
For local use with Terraform CLI, copy ".env.sample" to ".env" and run source .env
to load these variables into the current shell environment.
To initialize in "environments/demo" directory, run:
terraform -chdir="environments/demo" init \
-backend-config="../backend.tfvars" \
-backend-config="key=environments/demo/terraform.tfstate"
To plan/apply in "environments/demo" directory, run:
terraform -chdir="environments/demo" apply
To replicate Terraform’s plan/apply -destroy
, we can prefix with tf_destroy
to generate a plan to destroy all resources. The plan will be carried out on merge.
To replicate Terraform’s apply -auto-approve
, we can prefix with tf_auto_approve
to generate and apply the plan immediately.
Terraform workspaces allow us to associate multiple states with a single configuration. In conjunction with parameter interpolation, we can vary our configuration state dynamically based on the workspace name. For example:
locals {
instance_types = {
default = "t2.micro"
staging = "t2.medium"
}
}
resource "aws_instance" "demo" {
instance_type = local.instance_types[terraform.workspace]
tags = { Name = "demo-${terraform.workspace}" }
To deploy a workspace called "staging" in "environments/demo" directory, add tf:demo--staging
as a label to the PR. Note the --
delimiter between the environment directory and the workspace name.
For local use, we initialize as before then select the workspace for plan/apply:
terraform -chdir="environments/demo" workspace select staging
terraform -chdir="environments/demo" apply
PRs are welcome and appreciated. Please open a discussion if you'd like to get in touch.
Major props to dflook/terraform-github-actions for making this CI/CD possible with ease.
- Automated drift detection.
- Ephemeral test environments.
Neither myself nor this project are associated with AWS or Terraform.
All works herein are my own and shared of my own volition.
Copyleft @ All wrongs reserved.