Skip to content

Instantly share code, notes, and snippets.

@nictrix
Forked from nathanielks/README.md
Created July 20, 2016 20:26
Show Gist options
  • Save nictrix/565ea93978fe8699ab6a0640720e9fad to your computer and use it in GitHub Desktop.
Save nictrix/565ea93978fe8699ab6a0640720e9fad to your computer and use it in GitHub Desktop.
Simple wrapper around terraform to manage multiple environments

This script will pull down an S3 remote configuration before running any terraform actions. Assumes the following structure:

main.tf
terraform.cfg
env/dev/vars
env/staging/vars
env/whatever/vars
env/whatever/somefile.tf

terraform.cfg is required for the remote configuration.

You can use it like so:

./terraform-env.sh dev show
./terraform-env.sh staging plan -out plan
./terraform-env.sh production apply -var="some_var=some_value"

You can use the script just like you'd use terraform, passing whatever arguments you would before. It will only pull the latest environment if the current environment is different from the one you're requesting.

Caveat: this won't work if you've written a plan out to a file and then try to apply that plan as terraform apply doesn't accept the -var-file flag when reading from a plan. For reference:

./terraform-env.sh plan -out plan
./terraform-env.sh apply plan

The above will not work. It's recommend you just run terraform apply plan as the plan file will contain all the necessary bits to run the apply.

This script also allows you to use environment-specific config files. For example, you could have specific dns settings for production in env/production/dns-extra.tf and the script will copy the file into the directory you're calling the script from, run terraform, then delete the copied file.

#!/usr/bin/env bash
set -o pipefail
set -u
function help {
echo "usage: ${0} <environment> <action> [<args>]"
exit 1
}
function contains_element () {
local i
for i in "${@:2}"; do
[[ "$i" == "$1" ]] && return 0
done
return 1
}
function files_exist(){
ls ${1} 1> /dev/null 2>&1
}
#All of the args are mandatory.
if [ $# -lt 1 ]; then
help
fi
# Let's set up our environment
export ENVIRONMENT=$1
export ACTION=$2
ADDTL_PARAMS=${*:3}
CONFIG_FILE=./terraform.cfg
# Let's check the existence of the config file
if [ ! -f $CONFIG_FILE ]; then
echo "Error: $CONFIG_FILE does not exist. You'll need to create a config file so we know where to set up the remote config."
exit 1
fi
source ${CONFIG_FILE}
# Let's set up our variables
ALLOWS_VARFILE=(apply plan push refresh destroy)
ENV_FILE=.terraform/environment
ENV_DIR=env/$ENVIRONMENT
VARS_FILE=${ENV_DIR}/vars
VARS_FILE_FLAG=
BUCKET_KEY=$bucket_prefix/state/$ENVIRONMENT
PREVIOUS_ENVIRONMENT=$([ -f $ENV_FILE ] && echo "$(<$ENV_FILE)" || echo "previous")
EXTRA_ARGS=${extra_args:-''}
PRE_CMD=${pre_command:-''}
POST_CMD=${post_command:-''}
# Let's check to see if a vars file exists for the requested environment before proceeding
if [ ! -f $VARS_FILE ]; then
echo "Error: $VARS_FILE does not exist. You'll need to create a vars file for the requested environment before continuing."
exit 1
fi
# Checks if current action allows a varfile to be passed
contains_element "$ACTION" "${ALLOWS_VARFILE[@]}"
if [ $? -eq 0 ]; then
VARS_FILE_FLAG="-var-file=$VARS_FILE"
fi
# Let's check if the requested environment is different from the previous environment
if [ $PREVIOUS_ENVIRONMENT != $ENVIRONMENT ] || [ ! -f '.terraform/terraform.tfstate' ]
then
# Move current state out of the way to make room for the new state
mv -f .terraform/terraform.tfstate .terraform/terraform.tfstate.$PREVIOUS_ENVIRONMENT > /dev/null 2>&1
mv -f .terraform/terraform.tfstate.backup .terraform/terraform.tfstate.backup.$PREVIOUS_ENVIRONMENT > /dev/null 2>&1
# Let's log the new environment for later
echo $ENVIRONMENT > $ENV_FILE
# Set up remote configuration and pull latest version
terraform remote config -backend S3 -backend-config="bucket=$bucket" -backend-config="key=$BUCKET_KEY" -backend-config="region=$region"
fi
# Let's run the PRE_CMD hook if it's defined
eval ${PRE_CMD}
# let's copy environment specific configuration to the root of the directory
if files_exist ${ENV_DIR}/*.tf; then
cd env/${ENVIRONMENT}
pax -wrs'/\.tf$/\.env\.tf/' *.tf ../../
cd ../../
fi
# Let's do work!
terraform $ACTION $VARS_FILE_FLAG $ADDTL_PARAMS ${EXTRA_ARGS}
# Let's remove those environment-specific configuration files we copied earlier
if files_exist *.env.tf; then
rm *.env.tf
fi
# Let's run the POST_CMD hook if it's defined
eval ${POST_CMD}
bucket=some_secret_bucket
bucket_prefix=example
region=us-east-1
# Both are optional. This allows you to run scripts before and after terraform executes
# (perhaps to decrypt/encrypt files, move files, modify templates, etc)
pre_command='bin/pre.sh'
post_command='bin/post.sh'
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment