Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

new features for tf.sh #1

Merged
merged 9 commits into from
Oct 29, 2021
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
111 changes: 79 additions & 32 deletions tf.sh
Original file line number Diff line number Diff line change
@@ -1,36 +1,58 @@
#!/usr/env/bin bash
#!/usr/bin/env bash

set -e


TF_PARALLELISM=${TF_PARALLELISM:-10}
TF_STATE_BUCKET=${TF_STATE_BUCKET:-}
TF_STATE_PATH=${TF_STATE_PATH:-}
TF_STATE_FILE_NAME=${TF_STATE_FILE_NAME:-main.tfstate}
TF_STATE_DYNAMODB_TABLE=${TF_STATE_DYNAMODB_TABLE:-}
TF_TERRAFORM_EXECUTABLE=${TF_TERRAFORM_EXECUTABLE:-terraform}
TF_ENVIRONMENT_ID=${TF_ENVIRONMENT_ID:-}
TF_AUTO_APPLY_SAVED_PLAN=${TF_AUTO_APPLY_SAVED_PLAN:-}
TF_VAR_terraform_state_location=${TF_VAR_terraform_state_location:-}
TF_SKIP_BACKEND_INIT=${TF_SKIP_BACKEND_INIT:-}

# Local tf.sh.env
if [ -f tf.sh.env ]; then
# Load default Environment Variables
echo "Set default from tf.sh.env"
export $(cat tf.sh.env | grep -v '#' | awk '/=/ {print $1}')
fi

if [ -z "${TF_STATE_PATH}" ]; then TF_STATE_PATH=${TF_STATE_PATH:-}; fi
if [ -z "${TF_STATE_FILE_NAME}" ]; then TF_STATE_FILE_NAME=${TF_STATE_FILE_NAME:-main.tfstate}; fi
if [ -z "${TF_DATA_DIR_PER_ENV}" ]; then TF_DATA_DIR_PER_ENV=${TF_DATA_DIR_PER_ENV:-true}; fi
if [ -z "${HASH_COMMAND}" ]; then HASH_COMMAND=${HASH_COMMAND:-sha1sum}; fi

# Set terraform version
[ -e ./.terraform_executable ] && export TF_TERRAFORM_EXECUTABLE="$(cat .terraform_executable)"

if [ "$#" -eq 0 ] || [ "$*" == "-h" ] || [ "$*" == "-h" ]
then
echo "This is a Terraform wrapper to dynamically pick different state files for different environment"
echo "Wrapper will attempt to pick defaults and setup a correct bucket"
echo "All script argumetns will be passed to Terraform"
echo ""
echo "Example:"
echo "tf plan"
echo "tf destroy"
echo ""
echo "tf will indentify your env based on current AWS account id and region"
echo ""
exit 0
if [ "$#" -eq 0 ] || [ "$*" == "-h" ] || [ "$*" == "-h" ]; then
echo "This is a Terraform wrapper to dynamically pick different state files for different environment"
echo "Wrapper will attempt to pick defaults and setup a correct bucket"
echo "All script argumetns will be passed to Terraform"
echo ""
echo "WARNING: If we are applying changes, do not ask for interactive approval"
echo ""
echo "Example:"
echo "tf plan"
echo "tf plan -destroy"
echo "tf apply"
echo "tf apply -destroy"
echo ""
echo "tf will indentify your env based on current AWS account id and region"
echo ""
echo "For apply saved plan please set TF_AUTO_APPLY_SAVED_PLAN variable with any value"
echo "Example:"
echo "TF_AUTO_APPLY_SAVED_PLAN=true ./tf.sh apply plan.tfplan"
echo ""
echo "WARNING: This plan will be applied without any confirmation"
echo ""
exit 0
fi

if [ -z "${AWS_DEFAULT_REGION}" ]
then
echo "Define env variable AWS_DEFAULT_REGION (should be your region name, ex us-east-1) and try again"
exit 1
if [ -z "${AWS_DEFAULT_REGION}" ]; then
echo "Define env variable AWS_DEFAULT_REGION (should be your region name, ex us-east-1) and try again"
exit 1
fi

if [ -z "${TF_ENVIRONMENT_ID}" ]; then
Expand All @@ -50,19 +72,26 @@ else
echo "Using user provided TF_ENVIRONMENT_ID=${TF_ENVIRONMENT_ID}"
fi

# Enable TF_DATA_DIR_PER_ENV
[ "${TF_DATA_DIR_PER_ENV}" == "true" ] && export TF_DATA_DIR=".terraform.${TF_ENVIRONMENT_ID}"

if [ -z "${TF_STATE_BUCKET}" ]; then
# Use hashed environment id to avoid account id/region disclosure via S3 DNS name
# in this way it is hard to predict the bucket name and attacker won't be able to
# setup buckets in advance to capture your state file
HASHED_ENVIRONMENT_ID=$(echo -n ${TF_ENVIRONMENT_ID} | sha1sum | awk '{print $1}')
HASHED_ENVIRONMENT_ID=$(echo -n ${TF_ENVIRONMENT_ID} | "${HASH_COMMAND}" | awk '{print $1}')
export TF_STATE_BUCKET="terraform-state-${HASHED_ENVIRONMENT_ID}"
fi

if [ -z "${TF_STATE_DYNAMODB_TABLE}" ]; then
HASHED_ENVIRONMENT_ID=$(echo -n ${TF_ENVIRONMENT_ID} | "${HASH_COMMAND}" | awk '{print $1}')
export TF_STATE_DYNAMODB_TABLE="terraform-state-${HASHED_ENVIRONMENT_ID}"
fi

if [ -z "${TF_STATE_PATH}" ]; then
# Check if we are in git repo
GIT_REPO_TEST=$(git rev-parse --git-dir 2> /dev/null || true)
if [ -z "${GIT_REPO_TEST}" ]
then
GIT_REPO_TEST=$(git rev-parse --git-dir 2>/dev/null || true)
if [ -z "${GIT_REPO_TEST}" ]; then
echo "tf expects you to run inside git repo since it will be using git repo name as part of the state"
exit 1
fi
Expand All @@ -71,8 +100,7 @@ if [ -z "${TF_STATE_PATH}" ]; then
# we can't use just local repo name because jenkins pipelines
# clone repos to directories with abracadabra names which are not the same
# as actual repo name
if [ ! -z "$(git config --get remote.origin.url)" ]
then
if [ ! -z "$(git config --get remote.origin.url)" ]; then
REPO_NAME=$(basename -s .git $(git config --get remote.origin.url))
echo "Using remote repo name \"${REPO_NAME}\" as a part of Terraform state path"
else
Expand All @@ -83,15 +111,34 @@ if [ -z "${TF_STATE_PATH}" ]; then
export TF_STATE_PATH="terraform/${REPO_NAME}/${TF_STATE_FILE_NAME}"
fi

if [ -z "${TF_VAR_terraform_state_location}" ]; then
# Set terraform variable terraform_state_location for tags
export TF_VAR_terraform_state_location="s3://${TF_STATE_BUCKET}/${TF_STATE_PATH}"
fi

echo "Using remote state s3://${TF_STATE_BUCKET}/${TF_STATE_PATH}"
echo "Using lock table ${TF_STATE_DYNAMODB_TABLE}"

set -x

${TF_TERRAFORM_EXECUTABLE} init -backend-config "key=${TF_STATE_PATH}" -backend-config "bucket=${TF_STATE_BUCKET}" -backend-config "region=${AWS_DEFAULT_REGION}"
# Allow to skip terraform init with new backend
if [ -z "${TF_SKIP_BACKEND_INIT}" ]; then
${TF_TERRAFORM_EXECUTABLE} init -backend-config "key=${TF_STATE_PATH}" -backend-config "bucket=${TF_STATE_BUCKET}" -backend-config "region=${AWS_DEFAULT_REGION}" -backend-config "dynamodb_table=${TF_STATE_DYNAMODB_TABLE}" -backend-config "encrypt=true"
else
echo "Skipping terraform backend initialization.."
fi

# If we are applying changes, do not ask for interactive approval
[ $1 == "apply" ] && export OPTIONS="-auto-approve"
# figure out which env file to use
[ -e ./${TF_ENVIRONMENT_ID}.tfvars ] && export VAR_FILE="-var-file=./${TF_ENVIRONMENT_ID}.tfvars"
if [ -e ./${TF_ENVIRONMENT_ID}.tfvars ]; then
# If we are not applying saving plan, add -var-file
if [ -z "${TF_AUTO_APPLY_SAVED_PLAN}" ]; then
export TF_CLI_ARGS_plan="-var-file=./${TF_ENVIRONMENT_ID}.tfvars"
export TF_CLI_ARGS_import="-var-file=./${TF_ENVIRONMENT_ID}.tfvars"
export TF_CLI_ARGS_destroy="-var-file=./${TF_ENVIRONMENT_ID}.tfvars"
export TF_CLI_ARGS_refresh="-var-file=./${TF_ENVIRONMENT_ID}.tfvars"
# If we are applying changes, do not ask for interactive approval. Work for any apply commands (-destroy, -refresh-only and etc)
export TF_CLI_ARGS_apply="-var-file=./${TF_ENVIRONMENT_ID}.tfvars -auto-approve"
fi
fi

${TF_TERRAFORM_EXECUTABLE} $* ${VAR_FILE} ${OPTIONS}
${TF_TERRAFORM_EXECUTABLE} $*