├── README.md └── tf_wrapper.sh /README.md: -------------------------------------------------------------------------------- 1 | # Terraform multi-environment wrapper 2 | 3 | ## Overview 4 | 5 | This project is a small shell wrapper script to handle multi-env and multi-region terraform deployments in the AWS cloud. 6 | 7 | ## Dependencies 8 | 9 | * An S3 bucket for each environment must be in place. 10 | 11 | ### Usage 12 | The environment, AWS region and terraform subcommand must be defined in the order below.. 13 | 14 | ```sh 15 | ./tf_wrapper.sh [env] [region] [plan|apply|destroy|show|taint|untaint] 16 | ``` 17 | 18 | #### Deploy environments 19 | 20 | ##### Example1 - Deploying dev environment in the AWS us-east-1 region 21 | 22 | The 'plan' option will show what changes will be made and validates syntax... 23 | 24 | ```sh 25 | $ ./tf_wrapper.sh dev us-east-1 plan 26 | 27 | ``` 28 | 29 | The 'apply' option will apply the setting shown during the plan 30 | 31 | ```sh 32 | $ ./tf_wrapper.sh dev us-east-1 apply 33 | 34 | ``` 35 | The 'destroy' option will tear down the resources managed by this terraform project. 36 | 37 | ```sh 38 | $ ./tf_wrapper.sh dev us-east-1 destroy 39 | 40 | ``` 41 | ##### Example2 - Deploying test environment in the AWS us-west-1 region 42 | 43 | ```sh 44 | $ ./tf_wrapper.sh test us-west-1 plan 45 | 46 | ``` 47 | 48 | ```sh 49 | $ ./tf_wrapper.sh test us-west-1 apply 50 | 51 | ``` 52 | #### Using *show* subcommand 53 | 54 | The 'show' option reads and outputs a terraform state file in a human-readable form. 55 | 56 | ```sh 57 | $ ./tf_wrapper.sh test us-west-1 show 58 | 59 | ``` 60 | 61 | #### Using *taint/untaint* subcommand 62 | 63 | The 'taint' and 'untaint' options manually marks or unmarks a resource to be destroyed and recreated on the next plan/apply. The resource name can be found from the 'show' subcommand option. 64 | 65 | ```sh 66 | $ ./tf_wrapper.sh test us-west-1 taint 67 | $ ./tf_wrapper.sh test us-west-1 untaint 68 | 69 | ``` 70 | 71 | ## Limitations 72 | * The configuration was developed and tested with Terraform v0.6.16. 73 | 74 | -------------------------------------------------------------------------------- /tf_wrapper.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | #Terraform wrapper script for multi-environment deployments using remote state files 4 | 5 | display_usage() { 6 | echo -e "\e[31m The environment, AWS region and a supported terraform subcommand must all be defined" 7 | echo -e "\nUsage:\n$0 [env] [region] [plan|apply|destroy|taint|untaint|show] \n" 8 | } 9 | if [ $# -lt 3 ] && [ $# -gt 4 ]; then 10 | display_usage 11 | exit 1 12 | fi 13 | if [[ ( $# == "--help") || $# == "-h" ]]; then 14 | display_usage 15 | exit 0 16 | fi 17 | 18 | #Make sure terraform is installed 19 | type terraform >/dev/null 2>&1 || { echo >&2 "Terraform is not installed or in your path, exiting."; exit 1; } 20 | 21 | #Define region 22 | REGION=$2 23 | if [ -z "${REGION}" ]; then 24 | display_usage 25 | exit 1 26 | elif [ $REGION != "us-east-1" ] && [ $REGION != "us-west-1" ]; then 27 | echo -e "\e[31m ERROR: Only AWS us-east-1 and us-west-1 are supported regions" 28 | echo $REGION 29 | 30 | exit 1 31 | fi 32 | 33 | #Define specific AWS resource 34 | RESOURCE=$4 35 | 36 | #set variables based on environment setting 37 | if [ $1 = "dev" ]; then 38 | ENVIRONMENT=dev 39 | STATE_FILE=terraform-${ENVIRONMENT}.tfstate 40 | S3_BUCKET=tsm-${ENVIRONMENT}-tf-state-${REGION} 41 | TFVAR=$ENVIRONMENT/env_${ENVIRONMENT}.tfvars 42 | SECRETS=$ENVIRONMENT/secrets.tfvars 43 | elif [ $1 = "test" ]; then 44 | ENVIRONMENT=test 45 | STATE_FILE=terraform-${ENVIRONMENT}.tfstate 46 | S3_BUCKET=tsm-${ENVIRONMENT}-tf-state-${REGION} 47 | TFVAR=$ENVIRONMENT/env_${ENVIRONMENT}.tfvars 48 | SECRETS=$ENVIRONMENT/secrets.tfvars 49 | elif [ $1 = "prod" ]; then 50 | ENVIRONMENT=prod 51 | STATE_FILE=terraform-${ENVIRONMENT}.tfstate 52 | S3_BUCKET=tsm-${ENVIRONMENT}-tf-state-${REGION} 53 | TFVAR=$ENVIRONMENT/env_${ENVIRONMENT}.tfvars 54 | SECRETS=$ENVIRONMENT/secrets.tfvars 55 | else 56 | echo -e "\e[31m ERROR: Environment must be set to [dev|test|prod]" 57 | display_usage 58 | exit 1 59 | fi 60 | 61 | remote_config() { 62 | echo -e "Configuring and pulling terraform remote state from ${S3_BUCKET}.." 63 | terraform remote config -backend=s3 -backend-config="bucket=${S3_BUCKET}" -backend-config="key=${STATE_FILE}" -backend-config="region=${REGION}" || exit 1 64 | 65 | } 66 | 67 | plan() { 68 | echo -e "Running terraform plan for \e[32m ${ENVIRONMENT} \e[0m environment in AWS ${REGION}" 69 | sleep 2 70 | terraform plan -var-file=$SECRETS -var-file=$TFVAR -var aws_region=${REGION} 71 | } 72 | 73 | 74 | apply() { 75 | start="$(date +%s)" 76 | echo -e "Running terraform apply for \e[32m ${ENVIRONMENT} \e[0m environment in AWS ${REGION}.." 77 | sleep 1 78 | terraform apply -var-file=$SECRETS -var-file=$TFVAR -var aws_region=${REGION} 79 | end="$(date +%s)" 80 | echo "=================================================================" 81 | echo -e "\e[32mTerraform ran for $(($end - $start)) seconds" 82 | } 83 | 84 | show() { 85 | echo -e "Running terraform show for \e[32m ${ENVIRONMENT} \e[0m environment in AWS ${REGION}.." 86 | sleep 2 87 | terraform show 88 | } 89 | 90 | 91 | destroy() { 92 | echo -e "Running terraform destroy for \e[32m ${ENVIRONMENT} \e[0m environment in AWS ${REGION}.." 93 | terraform destroy -var-file=$SECRETS -var-file=$TFVAR -var aws_region=${REGION} 94 | } 95 | 96 | taint() { 97 | if [ -z "${RESOURCE}" ]; then 98 | echo "ERROR: the [taint] subcommand requires the AWS resource to be defined" 99 | terraform --help taint 100 | exit 1 101 | fi 102 | echo -e "Marking terraform resource as tainted for \e[32m ${ENVIRONMENT} \e[0m environment in AWS ${REGION}.." 103 | 104 | terraform taint -var-file=$SECRETS -var-file=$TFVAR -var aws_region=${REGION} $RESOURCE 105 | } 106 | 107 | untaint() { 108 | if [ -z "${RESOURCE}" ]; then 109 | echo "ERROR: the [untaint] subcommand requires the AWS resource to be defined" 110 | terraform --help untaint 111 | exit 1 112 | fi 113 | echo -e "Marking terraform resource as tainted for \e[32m ${ENVIRONMENT} \e[0m environment in AWS ${REGION}.." 114 | 115 | terraform untaint -var-file=$SECRETS -var-file=$TFVAR -var aws_region=${REGION} $RESOURCE 116 | } 117 | 118 | #Check current env and region in local state, if it exists 119 | LOCAL_STATE=.terraform/terraform.tfstate 120 | if [ -e $LOCAL_STATE ]; then 121 | CURRENT_ENV=`grep Environment ${LOCAL_STATE} |head -1 | cut -d '"' -f 4` 122 | CURRENT_REGION=`grep region ${LOCAL_STATE} |head -1 | cut -d '"' -f 4` 123 | 124 | echo "Local $LOCAL_STATE file exists.." 125 | if [ $CURRENT_ENV = $1 ] && [ $CURRENT_REGION = $2 ]; then 126 | echo -e "Local $LOCAL_STATE file is set to the \e[32m ${CURRENT_ENV} \e[0m environment and \e[32m ${CURRENT_REGION} \e[0m, proceeding.." 127 | else 128 | echo -e "Local $LOCAL_STATE file is not set to the environment or region specified, purging & pulling remote state.." 129 | rm -f ${LOCAL_STATE} 130 | fi 131 | fi 132 | 133 | #Always set/pull tf remote state 134 | remote_config 135 | 136 | if [ $3 = "plan" ]; then 137 | plan 138 | elif [ $3 = "apply" ]; then 139 | apply 140 | elif [ $3 = "taint" ]; then 141 | taint 142 | elif [ $3 = "untaint" ]; then 143 | untaint 144 | elif [ $3 = "show" ]; then 145 | show 146 | elif [ $3 = "destroy" ]; then 147 | destroy 148 | else 149 | echo -e "\e[31m ERROR: [plan|apply|destroy|taint|untaint|show] are the only subcommands supported" 150 | display_usage 151 | exit 1 152 | fi 153 | 154 | --------------------------------------------------------------------------------