Introduction
Recently I have been playing with Terraform. It’s a lot of fun.
I had a little project that was perfect for it, but ran into a problem. Most examples of Terraform usage assume that your environments are static. So layouts like this are not uncommon:
terraform_folder/ modules/ myproject/main.tf myproject/vars.tf live/ main.tf stage/ main.tf dev/ main.tf
Problem
All well and good, but in my project I needed to create environments on the fly, and perhaps many in existence at the same time. There was no ‘live’, just potentially hundreds of envs in use at once for a short period of time.
I also needed to keep a record of environments created and destroyed.
I researched and asked around, but couldn’t find any best practice for this, so came up with a pattern that may be useful to others.
Nothing a Shell Script Can’t Handle
In one sentence, this scheme creates a new folder on demand with a unique value which is destroyed when time is up.
The original code is elsewhere and somewhat more complex, so I put together this simple example code to illustrate the flow.
Here’s a video of it in action:
In addition to the standard main and vars files in the module, there are two scripts involved:
- create_dynamic_environment.sh
- destroy_dynamic_environment.sh
create_dynamic_environment.sh
- Create a directory with a unique (well, probably) ID
- Set up the main.tf file
- Terraform the environment
- (Git) add, commit and push the new directory
This script can be triggered when a new environment is required.
#!/bin/bash # Ensure we are in the right folder pushd $(dirname ${BASH_SOURCE[0]}) # Create a (probably) unique ID by concatenating two random # values (RANDOM is a variable inherent to bash), with the day of year # as a suffix. ID="dynamic_environment_${RANDOM}${RANDOM}_$(date +%j)" # Create the terraform folder. mkdir -p ${ID} pushd ${ID} cat > main.tf << END module "dynamicenv" { source = "../modules/dynamicenv" dynamic_env_id = "${ID}" } END # Terraform ahoy! terraform get terraform plan terraform apply popd # Record the creation in git and push. Assumes keys set up. git add ${ID} git commit -am "${ID} environment added" git push popd
destroy_dynamic_environments.sh
- After 7 days, retire the environment
- (Git) remove, commit and push the removal
This script can be run regularly in a cron.
In the ‘real’ aws environment I get the EC2 instance to self-destruct after a few hours, but for belt and braces we destroy the environment and remove it from git.
#!/bin/bash # We need extended glob capabilities. shopt -s extglob # Ensure we are in the right folder pushd $(dirname ${BASH_SOURCE[0]}) # Default to destroying environments over 7 days old. # If you want to destroy all of them, pass in '-1' as an argument. DAYS=${1:-7} # Get today's 'day of year' TODAY=$(date +%j) # Remove leading zeroes from the date. TODAY=${TODAY##+(0)} # Go through all the environment folders, and terraform destroy, # git remove and remove the folder. for dir in $(find dynamic_environment_* -type d -maxdepth 0) do # Remove the folder prefix. dir_day=${dir##*_} # Remove any leading zeroes from the day of year. dir_day=${dir_day##+(0)} # If over 7 days old... if [[ $(( ${TODAY} - ${dir_day})) -gt ${DAYS} ]] then pushd "${dir}" # Destroy the environment. terraform destroy -force popd # Remove from git. git rm -rf "${dir}" git commit -am "destroyed ${dir}" git push # Remove left-over backup files. rm -rf "${dir}" fi done
My book Docker in Practice
Get 39% off with the code: 39miell
fyi, I think they recommend using json for scripting – see https://github.com/hashicorp/terraform/issues/12074#issuecomment-280865885