From d7c6b2c776de79291bded1d32e3e99668dd85146 Mon Sep 17 00:00:00 2001 From: Dave Arnold Date: Wed, 6 May 2026 13:41:05 -0400 Subject: [PATCH] ading scripts and data --- data/tf-run.data | 20 + scripts/tf-control.sh | 392 +++++++++++++++ scripts/tf-directory-setup.py | 225 +++++++++ scripts/tf-run | 921 ++++++++++++++++++++++++++++++++++ scripts/tf-run.data | 20 + 5 files changed, 1578 insertions(+) create mode 100644 data/tf-run.data create mode 100755 scripts/tf-control.sh create mode 100755 scripts/tf-directory-setup.py create mode 100755 scripts/tf-run create mode 100644 scripts/tf-run.data diff --git a/data/tf-run.data b/data/tf-run.data new file mode 100644 index 0000000..ea54249 --- /dev/null +++ b/data/tf-run.data @@ -0,0 +1,20 @@ +VERSION 1.3.1 +REMOTE-STATE +COMMAND tf-directory-setup.py -l none -f +COMMAND setup-new-directory.sh +COMMAND tf-init -upgrade + +LINKTOP includes.d/variables.account_tags.tf +LINKTOP includes.d/variables.account_tags.auto.tfvars +LINKTOP includes.d/variables.infrastructure_tags.tf +LINKTOP includes.d/variables.infrastructure_tags.auto.tfvars +LINKTOP includes.d/variables.application_tags.tf +LINKTOP includes.d/variables.application_tags.auto.tfvars +LINKTOP common/remote_state.common.tf +LINKTOP infrastructure/%%SHORT_REGION%%/remote_state.infrastructure_%%SHORT_REGION%%.tf + +# module.subnet_tags +# module.sg_web module.base-security-groups + +ALL +COMMAND tf-directory-setup.py -l s3 diff --git a/scripts/tf-control.sh b/scripts/tf-control.sh new file mode 100755 index 0000000..ef1b36f --- /dev/null +++ b/scripts/tf-control.sh @@ -0,0 +1,392 @@ +#!/bin/bash + +get_git_root() +{ + TOP=$(git rev-parse --show-toplevel 2>/dev/null) + if [ -z "$TOP" ] + then + TOP=$HOME + fi +} + +do_help() +{ + local ACTIONS=$@ + echo "* help: $THIS $VERSION" + echo " tf-{action}: runs command 'terraform {action}' with specific arguments" + echo " tf-{action} less [arguments]: shows the latest log file for the specific {action}" + echo " TFLESS=1 tf-{action} [arguments]: shows the latest log file for the specific {action}" + echo " tf-log {string} [list|{filename}: tails the last log matching the patern logs/{string}*, or provides a list of file, or uses a specific file" + echo " tf-cli {string}: runs the approriate terraform binary with whaterver {string} as arguments" + echo "" + echo "* environment variables" + echo " TFCONTROL: point to alternate .tf-control; default looks lin git-root, then \$HOME" + echo " TF_CLI_CONFIG_FILE: point to alternate .tf-control.tfrc; default looks lin git-root, then \$HOME, and default \$HOME/.terraformrc" + echo " TFARGS: extra args to pass to terraform command. Only applies to actions apply and destroy" + echo " TFNOCOLOR: color disabled by default -no-color, to enable, set this any value" + echo " TFNOLOG: setting this at all will disable logging through 'tee' to a file. Needed to pull tf-state properly" + echo " TFNOPROXY: do not auto-set proxy. With changes to the firewalls, proxy is needed, so we set it for init options" + echo "" + echo "* Available Actions:" + for a in $ACTIONS + do + echo " tf-${a}" + done + echo "" + echo "* Special Actions:" + echo " tf-plan summary: produces a list of items to create, destroy, replace, or update. Requires having run 'tf-plan' first" + echo " tf-apply summary: produces a list of items like plan but under an apply action. Requires having run 'tf-apply' first and then answering 'no'" + echo " tf-destroy summary: produces a list of items to destroy. Requires having run 'tf-destroy' first and then answering 'no'" + return 0 +} + +# pass things like -target= +# make aliases +# ln -s $BINDIR/tf-control.sh $BINDIR/tf-init +# ln -s $BINDIR/tf-control.sh $BINDIR/tf-plan +# ln -s $BINDIR/tf-control.sh $BINDIR/tf-apply +# ln -s $BINDIR/tf-control.sh $BINDIR/tf-destroy +# ln -s $BINDIR/tf-control.sh $BINDIR/tf-refresh +# ln -s $BINDIR/tf-control.sh $BINDIR/tf-output +# ln -s $BINDIR/tf-control.sh $BINDIR/tf-validate +# ln -s $BINDIR/tf-control.sh $BINDIR/tf-import +# ln -s $BINDIR/tf-control.sh $BINDIR/tf-state +# ln -s $BINDIR/tf-control.sh $BINDIR/tf-fmt +# ln -s $BINDIR/tf-control.sh $BINDIR/tf-taint +# ln -s $BINDIR/tf-control.sh $BINDIR/tf-console +# ln -s $BINDIR/tf-control.sh $BINDIR/tf-log +# ln -s $BINDIR/tf-control.sh $BINDIR/tf-cli + +THIS=$(basename $0) +VERSION="1.11.0" +ACTION=$(basename $THIS .sh | sed -e 's/^tf-//') +LOGDIR="logs" + +umask 002 + +# path or name of terraform binary +# get from top of git repo or $HOME/.tf-control +CURRENTDIR=$(pwd) +get_git_root +if [ -z "$TFCONTROL" ] +then + if [ -r $TOP/.tf-control ] + then + TFCONTROL=$TOP/.tf-control + elif [ -r $CURRENTDIR/.tf-control ] + then + TFCONTROL=$CURRENTDIR/.tf-control + elif [ -r $HOME/.tf-control ] + then + TFCONTROL=$HOME/.tf-control + fi +fi +# if .tf-control in git-root, override it with a local .tf-control.override +if [ -r $CURRENTDIR/.tf-control.override ] +then + TFCONTROL=$CURRENTDIR/.tf-control.override +fi +if [ ! -z "$TFCONTROL" ] +then + source $TFCONTROL +fi + +if [ -z $TFCOMMAND ] +then + TFCOMMAND="terraform" +fi + +# look for config file +if [ -z "$TF_CLI_CONFIG_FILE" ] +then + if [ -r $TOP/.tf-control.tfrc ] + then + export TF_CLI_CONFIG_FILE=$TOP/.tf-control.tfrc + elif [ -r $CURRENTDIR/.tf-control.tfrc ] + then + export TF_CLI_CONFIG_FILE=$CURRENTDIR/.tf-control.tfrc + elif [ -r $HOME/.tf-control.tfrc ] + then + export TF_CLI_CONFIG_FILE=$HOME/.tf-control.tfrc + else + unset TF_CLI_CONFIG_FILE + fi +fi + +# based on issue https://github.com/hashicorp/terraform/issues/32901 +# where shared provider cache doesn't work right for multiple users in 1.4. This is needed to get beyond 1.3. +# it really does belong in the .tf-control file but that's a lot of files to change +export TF_PLUGIN_CACHE_MAY_BREAK_DEPENDENCY_LOCK_FILE=1 + +ACTIONS="init plan apply destroy refresh output validate import state fmt taint console log cli" +declare -A actions +for action in $ACTIONS +do + actions["$action"]=$action +done + +if [[ ! -z "$1" ]] && [[ "$1" == "help" ]] +then + do_help $ACTIONS + exit 0 +fi + +# set soem TF_VAR variables ifnot already set +if [ -z "$TF_VAR_os_environment" ] +then + export TF_VAR_os_environment="{\"pwd\":\"$(pwd)\"}" +fi +if [ -z "$TF_VAR_os_username" ] +then + export TF_VAR_os_username=$USER +fi + +# calling the script directly installs into $BINDIR | /apps/terraform/bin +if [ $ACTION == "control" ] +then + if [ -z $BINDIR ] + then + location=$(which $THIS 2> /dev/null) + if [ -z $location ] + then + BINDIR="/apps/terraform/bin" + else + BINDIR=$(dirname $location) + fi + fi + umask 022 + echo "* installing $THIS v$VERSION in $BINDIR" + cp $THIS $BINDIR/ + chmod 755 $BINDIR/$THIS + for action in $ACTIONS + do + echo "+ enabling tf-$action to $BINDIR/$THIS" + ln -sf $BINDIR/$THIS $BINDIR/tf-$action + done + exit 0 +fi + +# pass TFCOLOR=-color if wanting colorized output +if [ -z $TFCOLOR ] +then + if [ ! -z "$TFNOCOLOR" ] + then + TFCOLOR="" + else + TFCOLOR="-no-color" + fi +fi + +if [ ! -d $LOGDIR ] +then + mkdir -p $LOGDIR +fi +YMDSTAMP=$(date +%Y%m%d) +start=$(date +%s) +STAMP="$YMDSTAMP.$start" +LOGFILE="$LOGDIR/$ACTION.$STAMP.log" + +if [[ ! -z $1 ]] && [[ $1 == "less" ]] || [[ ! -z "$TFLESS" ]] +then + file=$(ls $LOGDIR/$ACTION*.log 2> /dev/null | tail -n 1) + if [ -z "$file" ] + then + echo "* No log file for action=$ACTION. Please run 'tf-$ACTION' first" + exit 1 + else + echo "# results from file $file" + less $file + exit 0 + fi +fi +if ( [[ $ACTION == "plan" ]] || [[ $ACTION == "destroy" ]] || [[ $ACTION == "apply" ]] ) && [[ ! -z $1 ]] && [[ $1 == "summary" ]] +then + file=$(ls $LOGDIR/$ACTION*.log 2> /dev/null | tail -n 1) + if [ -z "$file" ] + then + echo "* Previous $ACTION file does not exist. Please run 'tf-$ACTION'" + exit 1 + fi + echo "* tf-$ACTION summary from log $file" + for op in created updated replaced destroyed + do + cc=$(grep -c " $op" $file) + echo "> to-be $op ($cc)" + grep " $op" $file + echo "" + done + for op in changed moved + do + cc=$(grep -cE "has \b$op\b" $file) + echo "> has $op ($cc)" + grep -E " has \b$op\b" $file + echo "" + done + grep ^Plan $file + exit 0 +fi +if [ $ACTION == "log" ] +then + logtype="$1" + logaction="$2" + if [[ ! -z "$logaction" ]] && [[ $logaction == "list" ]] + then + echo "* available files for pattern '${logtype}*':" + ls logs/${logtype}* -C1 | sed -e 's/^/ /' + echo "" + exit 0 + elif [[ ! -z "$logaction" ]] + then + TFLOGFILE=$logaction + else + TFLOGFILE=$(ls logs/${logtype}* 2> /dev/null | tail -n 1) + fi + if [ ! -z "$TFLOGFILE" ] + then + if [[ -z "$logaction" ]] + then + echo "* available files for pattern '${logtype}*':" + ls logs/${logtype}* -C1 | sed -e 's/^/ /' + echo "" + fi + echo "* showing logfile=$TFLOGFILE" + less $TFLOGFILE + exit $? + else + echo "* No log files exist matching pattern '${logtype}*'" + exit 1 + fi +fi + +if [ ${actions[$ACTION]} == $ACTION ] +then + ( echo "# starting v$VERSION action $ACTION file $LOGFILE stamp $STAMP time $start"; \ + echo "# current_directory=$(pwd)"; \ + echo "# git_repository=$(git remote -v show | grep fetch | awk '{print $2}')"; \ + echo "# git_current_branch=$(git branch | grep ^* | awk '{print $2}')"; \ + echo "# terraform_version=$($TFCOMMAND -v|grep ^Terraform)"; \ + echo "# TFCONTROL=$TFCONTROL"; \ + echo "# TF_CLI_CONFIG_FILE=$TF_CLI_CONFIG_FILE"; \ + echo "# TFARGS=\"$TFARGS\" TFNOCLOR=$TFNOCOLOR TFNOLOG=$TFNOLOG TFNOPROXY=$TFNOPROXY"; \ + echo "# env TF_VAR_ variables"; \ + printenv | grep TF_VAR_ | sed -e 's/^/# /'; \ + echo "" ) |& tee $LOGFILE +else + echo "* action:${actions[$ACTION]}" + echo "* invalid action $ACTION, exiting" + exit 1 +fi + +# TFARGS="" +r=0 +if [ $ACTION == "init" ] +then +# $TFCOMMAND init $TFARGS $TFCOLOR $@ |& tee -a $LOGFILE + if [[ -z "$TFNOPROXY" ]] && [[ -z "$HTTPS_PROXY" ]] && [[ -r "/apps/terraform/etc/set-proxy.sh" ]] + then + source /apps/terraform/etc/set-proxy.sh + fi + $TFCOMMAND init $TFCOLOR $@ |& tee -a $LOGFILE + r=$? +fi + +if [ $ACTION == "plan" ] +then +# $TFCOMMAND plan $TFARGS $TFCOLOR $@ |& tee -a $LOGFILE + $TFCOMMAND plan $TFCOLOR $@ |& tee -a $LOGFILE + r=$? +fi + +if [ $ACTION == "apply" ] +then + $TFCOMMAND apply $TFARGS $TFCOLOR $@ |& tee -a $LOGFILE + r=$? +fi + +if [ $ACTION == "destroy" ] +then + $TFCOMMAND destroy $TFARGS $TFCOLOR $@ |& tee -a $LOGFILE + r=$? +fi + +if [ $ACTION == "refresh" ] +then +# $TFCOMMAND refresh $TFARGS $TFCOLOR $@ |& tee -a $LOGFILE + $TFCOMMAND refresh $TFCOLOR $@ |& tee -a $LOGFILE + r=$? +fi + +if [ $ACTION == "validate" ] +then +# $TFCOMMAND validate $TFARGS $TFCOLOR $@ |& tee -a $LOGFILE + $TFCOMMAND validate $TFCOLOR $@ |& tee -a $LOGFILE + r=$? +fi + +if [ $ACTION == "output" ] +then +# $TFCOMMAND output $TFARGS $@ +# $TFCOMMAND output $TFARGS $TFCOLOR $@ |& tee -a $LOGFILE + $TFCOMMAND output $TFCOLOR $@ |& tee -a $LOGFILE + r=$? + exit $r +fi + +if [ $ACTION == "import" ] +then +# $TFCOMMAND import $TFARGS $@ |& tee -a $LOGFILE + $TFCOMMAND import $@ |& tee -a $LOGFILE + r=$? +fi + +# not recommended for pull +if [ $ACTION == "state" ] +then +# $TFCOMMAND state $TFARGS $@ |& tee -a $LOGFILE + if [ ! -z "$TFNOLOG" ] + then + $TFCOMMAND state $@ + else + $TFCOMMAND state $@ |& tee -a $LOGFILE + fi + r=$? +fi + +if [ $ACTION == "fmt" ] +then +# $TFCOMMAND fmt $TFARGS $@ |& tee -a $LOGFILE + $TFCOMMAND fmt $@ |& tee -a $LOGFILE + r=$? +fi + +if [ $ACTION == "taint" ] +then +# $TFCOMMAND taint $TFARGS $@ |& tee -a $LOGFILE + $TFCOMMAND taint $@ |& tee -a $LOGFILE + r=$? +fi + +# This doesnt work to 'tee' because we can't leave stdin on the terminal. +if [ $ACTION == "console" ] +then +# $TFCOMMAND console $TFARGS $@ |& tee -a $LOGFILE +# $TFCOMMAND console $@ |& tee -a $LOGFILE + echo "* TFNOLOG in effect for $ACTION" + $TFCOMMAND console $@ + r=$? +fi + +if [ $ACTION == "cli" ] +then +# $TFCOMMAND $TFARGS $@ |& tee -a $LOGFILE + $TFCOMMAND $@ |& tee -a $LOGFILE + r=$? +fi + +end=$(date +%s) +elapsed=$(( $end - $start )) +(echo "# ending v$VERSION action $ACTION file $LOGFILE stamp $STAMP start $start end $end elapsed $elapsed"; echo "") |& tee -a $LOGFILE + +echo "" +echo "# results in file $LOGFILE stamp $STAMP status=$r" +# echo $r diff --git a/scripts/tf-directory-setup.py b/scripts/tf-directory-setup.py new file mode 100755 index 0000000..7c7b5e1 --- /dev/null +++ b/scripts/tf-directory-setup.py @@ -0,0 +1,225 @@ +#!/apps/terraform/python/bin/python +# /bin/env python + +from jinja2 import Environment,FileSystemLoader +import os +#import csv +#import re +import sys +from pprint import pprint +from datetime import datetime,date,time +from dateutil import tz +from dateutil.parser import parse as date_parse +import yaml +import hashlib +import argparse +from pathlib import Path + +def parse_arguments(version): + parser = argparse.ArgumentParser(description="Setup directory for Terraform (remote state, links)",add_help=True) + parser.add_argument('filename', action='store', help="Configuration filename to read (remote_state.yml)", default='remote_state.yml', nargs='?') + parser.add_argument('--version', action='version', version='%(prog)s '+version) + parser.add_argument("-n","--dry-run", action="store_true", dest="dry_run", help="Dry run, do not create links or remote state configuration", default=False) + parser.add_argument("-d","--debug", action="store_true", dest="debug", help="debugging", default=False) + parser.add_argument("-v","--verbose", action="store_true", dest="verbose", help="verbose output", default=False) + parser.add_argument("-f","--force", action="store_true", dest="force", help="Force", default=False) + parser.add_argument("-l","--link", action="store", dest="link", help="Make link to .tf", choices=['none', 'local', 's3']) + args = parser.parse_args() + return args + +def touch_file(file): + if os.path.exists(file): + os.utime(file,None) + else: + open(file,'a').close() + +def read_yaml(file): + data={} + with open(file, 'r') as stream: + try: + data=yaml.full_load(stream) + except yaml.YAMLError as e: + print(e) + return None + return data + +def create_backend(args,version): + data=read_yaml(args.filename) +# initialize missing fields + data['make_links']=data.get('make_links',True) + + if args.debug: + print('* data =') + pprint(data) + print('* args =',args) + print("") + + dry_s="[dry-run] " if args.dry_run else "" + this_dir=os.getcwd() + +# print('args',args) +# sys.exit(0) + + file_loader=FileSystemLoader('/apps/terraform/template') + env=Environment( + loader=file_loader, + trim_blocks=True, + lstrip_blocks=True + ) + + data['directory']=data.get('directory','') + if data['directory'] == "": + print("* error, 'directory' cannot be empty") + sys.exit(1) + + tf_backend=env.get_template('remote_state.backend.tf.j2') + tf_backend_data_local=env.get_template('remote_state.data.tf.local.j2') + tf_backend_data_s3=env.get_template('remote_state.data.tf.s3.j2') + + tf_output=tf_backend.render(data=data) + tf_filename='remote_state.backend.tf' + if os.path.exists(tf_filename): + do_create=args.force + else: + do_create=True + if do_create: + print("* {}creating file {}".format(dry_s,tf_filename)) + if not args.dry_run: + with open(tf_filename, 'w') as tf_file: + tf_file.write(tf_output) + else: + if args.debug or args.verbose: + print("* {}not creating file {}".format(dry_s,tf_filename)) + + d=data['directory'].replace('/','_').replace('.','_') + data['directory_replaced']=d + + base_dir=this_dir.replace(data['directory'],'') + dir_paths=data['directory'].split(os.path.sep) + rp=['..'] * len(dir_paths) + rel_path=os.path.join(*rp) + if args.debug: + print("* this_dir={}\n base_dir={}\n directory={}".format(this_dir,base_dir,data['directory'])) + print(" path_length={}\n relative_path_to_top={}/".format(len(dir_paths),rel_path)) + + + tf_output=tf_backend_data_s3.render(data=data) + tf_filename='remote_state.%s.tf.s3' % d + if do_create: + print("* {}creating file {}".format(dry_s,tf_filename)) + if not args.dry_run: + with open(tf_filename, 'w') as tf_file: + tf_file.write(tf_output) + else: + if args.debug or args.verbose: + print("* {}not creating file {}".format(dry_s,tf_filename)) + + tf_output=tf_backend_data_local.render(data=data) + tf_filename='remote_state.%s.tf.local' % d + if do_create: + print("* {}creating file {}".format(dry_s,tf_filename)) + if not args.dry_run: + with open(tf_filename, 'w') as tf_file: + tf_file.write(tf_output) + else: + if args.debug or args.verbose: + print("* {}not creating file {}".format(dry_s,tf_filename)) + + tf_filename='remote_state.%s.tf.none' % d + if do_create: + print("* {}touching file {}".format(dry_s,tf_filename)) + if not args.dry_run: + touch_file(tf_filename) + else: + if args.debug or args.verbose: + print("* {}not touching file {}".format(dry_s,tf_filename)) + + tf_filename='remote_state.%s.tf' % d + if args.link is not None: + source_file='{}.{}'.format(tf_filename,args.link) + if os.path.exists(tf_filename): + if not os.path.islink(tf_filename): + print("* {}target file {} is not a link, fixing".format(dry_s,tf_filename)) + if not args.dry_run: + os.remove(tf_filename) + if args.verbose: + print("* {}removing file {}".format(dry_s,tf_filename)) + if not args.dry_run: + os.symlink(source_file, tf_filename) + print("* {}link {} to {}".format(dry_s,source_file, tf_filename)) + else: + if do_create: + print("* sample ln commands to run\n") + print("# ln -sf {}.none {}".format(tf_filename,tf_filename)) + print("# ln -sf {}.local {}".format(tf_filename,tf_filename)) + print("# ln -sf {}.s3 {}".format(tf_filename,tf_filename)) + + return + +#--- +# main +#--- +def main(): + version='2.2.2' + args=parse_arguments(version) + + create_backend(args,version) + return + +#--- +# main +#--- +if __name__ == '__main__': + main() + +# if make_links, then read all links in the parent directory and make a link to them +# if link_files [] exists, then make only links to the parent directory for those files +# first time through (or --init, --step1), remote state doesn't exist), make the link to -> none +# after remote state has stuff (--step2), or remote state link does exist, change the link -> s3 +# optional use remote_state as local (local: true | false) +# bring template files into this script (like with rotate-keys.py) +# add logging + +## directory: "common" +## profile: "123456789012-mycloud" +## bucket: "inf-tfstate-123456789012" +## bucket_region: "us-gov-west-1" +## region: "us-gov-west-1" +## regions: ["us-gov-west-1"] +## account_id: "123456789012" +## account_alias: "mycloud" +## aws_environment: "govcloud" +# +# provider_configs: +# - provider.ldap # gets link to ../provider_configs.d/provider.ldap.* ./ +# - provider.dns # gets link to ../provider_configs.d/provider.dns.* ./ +# +# parent_links: +# - (file) # makes link files in the parent directory. Expect this not to be needed with this new setup +# - random_parent_file.tf +# - random_parent_link.tf +# +# consider making TOP/remote_state.d and then link all to that, and link remote_state.d to each directory +# remote_state: +# - (component-directory) # finds and links remote state with the appropriate component. Examples: +# - infrastructure +# - infrastructure/west-1 +# - common/apps/myapp1 + + + +## cwd=Path.cwd() +## top=None +## for p in cwd.parents: +## if (p / "TOP").exists() or ( (p / "init").exists() and (p / "init").is_dir() ): +## top=p +## +## if top: +## rel=cwd.relative_to(top) +## rel_top=['..'] * len(rel.parts) +## rel_top_s=os.path.join(*rel_top) +## else: +## rel=None +## rel_top_s='' +## +## print('cwd={}\ntop={}\nrel={}\nrel_to_top={}'.format(cwd,top,rel,rel_top_s)) diff --git a/scripts/tf-run b/scripts/tf-run new file mode 100755 index 0000000..3c0bc01 --- /dev/null +++ b/scripts/tf-run @@ -0,0 +1,921 @@ +#!/bin/bash + +get_git_root() +{ + TOP=$(git rev-parse --show-toplevel 2>/dev/null) +} + +get_relative_to_git_root() +{ +# TOP=$(git rev-parse --show-toplevel 2>/dev/null) + get_git_root + TOPBASE=$(basename $TOP) + CWD=$(pwd) + RELATIVE_PATH=$(echo $CWD | sed -e "s/^.*$TOPBASE//" | tr -cd / | sed -e 's#/#../#g') +# return 0 +} + +get_relative_directory() +{ + local FILE=$1 + if [ -z "$FILE" ] + then + echo "* get_relative_up(): error, missing FILE" + return 1 + fi + CWD=$(pwd) + RELATIVE_PATH="" + local c=0 + while [[ ! -r "$CWD/$FILE" ]] || [[ -L "$CWD/$FILE" ]] + do +## echo "[debug] checking $CWD/$FILE" +# stop if at top of repo (.git directory) or at / + if [[ ! -r "$CWD/.git" ]] && [[ "$CWD" != "/" ]] + then + CWD=$(dirname $CWD) + RELATIVE_PATH+="../" + c=$(( c + 1 )) +## echo "[debug] going up cwd=$CWD c=$c path=$RELATIVE_PATH" + else +## echo "[debug] hit git root or / cwd=$CWD" + RELATIVE_PATH="" + c=-1 + break + fi + done + if [[ $c -ge 0 ]] && [[ -z "$RELATIVE_PATH" ]] + then +## echo "[debug] found in current directory" + RELATIVE_PATH="./" + fi +} + +get_profile() +{ + if [ -z $ERROR_GET_PROFILE ] + then + ERROR_GET_PROFILE=0 + fi + local FILES=$(ls *tfvars 2>/dev/null) + if [ -z "$AWS_PROFILE" ] + then + if [ -z "$FILES" ] + then + [ $ERROR_GET_PROFILE -gt 0 ] && echo "* [WARNING] cannot determine profile from *.tfvars" + ERROR_GET_PROFILE=$(( $ERROR_GET_PROFILE + 1 )) + return 1 + else + PROFILE=$(grep -E '^\bprofile\b *' $FILES | sed -e 's/^.*profile.* =//' -e 's/\"//g' -e 's/#.*$//' -e 's/^ *//' | head -n 1) + fi + else + PROFILE=$AWS_PROFILE + fi +# echo "* using profile=$PROFILE" + ERROR_GET_PROFILE=0 + return 0 +} + +get_region() +{ + if [ -z $ERROR_GET_REGION ] + then + ERROR_GET_REGION=0 + fi + local FILES=$(ls *tfvars 2>/dev/null) + if [ -z "$AWS_REGION" ] + then + if [ -z "$FILES" ] + then + [ $ERROR_GET_REGION -gt 0 ] && echo "* [WARNING] cannot determine region from *.tfvars" + ERROR_GET_REGION=$(( $ERROR_GET_REGION + 1 )) + return 1 + else + REGION=$(grep -E '^\bregion\b *' $FILES | sed -e 's/^.*region.* =//' -e 's/\"//g' -e 's/#.*$//' -e 's/^ *//' | head -n 1) + fi + else + REGION=$AWS_REGION + fi + if [ ! -z $REGION ] + then + if [[ $REGION =~ gov ]] + then + SHORT_REGION=$(echo $REGION | sed -e 's/^us-gov-//' -e 's/-[0-9]$//') + else + SHORT_REGION=$(echo $REGION | sed -e 's/^us-//') + fi + fi +# echo "* using region=$REGION short_region=$SHORT_REGION" + ERROR_GET_REGION=0 + return 0 +} + +# returns value in stdout +replace_placeholders() +{ + local item="$1" + + if [ ! -z $next ] + then + item=$(echo $item | sed -e "s/%%NEXT%%/$next/g") + fi + if [ ! -z $previous ] + then + item=$(echo $item | sed -e "s/%%PREVIOUS%%/$previous/g") + fi + if [ ! -z $PROFILE ] + then + item=$(echo $item | sed -e "s/%%PROFILE%%/$PROFILE/g") + fi + if [ ! -z $REGION ] + then + item=$(echo $item | sed -e "s/%%REGION%%/$REGION/g") + fi + if [ ! -z $SHORT_REGION ] + then + item=$(echo $item | sed -e "s/%%SHORT_REGION%%/${SHORT_REGION}/g") + fi + echo "$item" +} + +get_file_from_git() +{ + local FILE=$1 + local URL=$2 + local status=0 + if [[ -z "$FILE" ]] || [[ -z "$URL" ]] + then + echo "* missing FILE or URL argument" + status=1 + else + if [ -r "$FILE" ] + then + echo "* file $FILE exists, not overwriting" + else + echo "* getting init file $FILE" + curl -q -s -k -o "$FILE" "$URL" + status=$? + fi + fi + return $status +} + +do_clean() +{ + local WHAT=$1 + if [ -z "$WHAT" ] + then + WHAT="clean" + fi + + echo "* executing $WHAT, removing remote_state.*" + echo -n "> " + for f in $(ls remote_state.* -d) + do + rm $f && echo -n " $f" + done + echo "" + + echo "* executing $WHAT, removing links" + echo -n "> " + for f in $(find . -maxdepth 1 -type l -print) + do + rm $f && echo -n " $f" + done + echo "" + return 0 +} + +do_superclean() +{ + do_clean superclean + echo "* executing superclean, removing logs, .terraform files, terraform.tfstate files" + echo -n "> " + for f in $(ls -d logs .terraform .terraform*hcl terraform.tfstate* 2> /dev/null) + do + rm -rf $f && echo -n " $f" + done + echo "" + return 0 +} + +do_help() +{ + local ACTIONS=$@ + echo "* help: $THIS $VERSION" + echo " tf-run: ACTION [list | start_number | tag:start_tag] [end_number | tag:end_tag | only | +N]" + echo " ACTION = plan | apply | destroy | list | init | init-upgrade | clean | superclean | tags" + echo "" + echo " init: get a base tf-run.data if none exists, creates region.tf if not exist, creates locals.tf if not exists; also gets .tf-control* files" + echo " init-upgrade: get the .tf-control* files needed for using TF 1.x" + echo " check: looks over module calls for proper use, proper versions, other things" + echo " list: same as '$THIS plan list'" + echo " plan: run through the contents of tf-run.data and do a plan for each" + echo " apply: run through the contents of tf-run.data and do a apply for each" + echo " destroy: looks for a tf-run.destroy.data, and removes in that specific order. If missing, it attempts all at once." + echo " clean: removes remote_state.*, links." + echo " superclean: removes remote_state.*, links, logs/, .terraform files, terraform.tfstate files" + echo " tags: get a list of tags and its respective step number" + echo "" + echo " arguments:" + echo " * list: list out all of the steps, but don't execute anything" + echo " * start_number: start executing ACTION at step number start_number" + echo " * tag:start_tag: start executing ACTION at tag labelled start_tag. The 'tag:' must be present for this option" + echo " * end_number: stop executing ACTION after step number end_number" + echo " * tag:end_tag: stop executing ACTION on the step before the tag labelled end_tag. The 'tag:' must be present for this option" + echo " * only: execute ACTION for only the one step indicated by start" + echo " * +N: execute ACTION starting at start and ending at start + N" + echo "" + return 0 +} + +ask_continue() { + local continue_status=0 + local PREFIX=$1 + local DURATION=$2 + local DEFAULT=$3 + if [ -z $DURATON ] + then + DURATION=10 + fi + if [ -z $DEFAULT ] + then + DEFAULT="y" + fi +# echo "" +# read -n 1 -p "${PREFIX}continue [y|n: default=$DEFAULT]? " -t $DURATION CONTINUE < /dev/tty + read -n 1 -p "${PREFIX}continue [y|n: default=$DEFAULT]? " -t $DURATION CONTINUE + continue_status=$? + echo "" + + if [ -z $CONTINUE ] + then + CONTINUE=$DEFAULT + fi + if [[ $continue_status != 0 ]] + then + CONTINUE=$DEFAULT + else + if [[ $CONTINUE != "y" ]] && [[ $CONTINUE != "n" ]] + then + CONTINUE="n" + fi + fi +# echo "value=$CONTINUE status=$continue_status" +} + +umask 002 + +THIS=$(basename $0) +VERSION="1.13.13" +LOGDIR="logs" +if [ ! -d $LOGDIR ] +then + mkdir -p $LOGDIR +fi +#GITSYSTEM="gitlab" +GITSYSTEM="github" + +ACTION=$1 +if [ -z "$ACTION" ] +then + echo "* missing ACTION (plan | apply | destroy)" + exit 1 +fi + +if [[ ! -z "$ACTION" ]] && [[ "$ACTION" == "help" ]] +then + do_help + exit 0 +fi + +if [ $ACTION == "list" ] +then + ACTION="plan" + START="list" +fi + +# if [[ $ACTION == "plan" ]] || [[ $ACTION == "apply" ]] || [[ $ACTION == "init" ]] || [[ $ACTION == "destroy" ]] || [[ $ACTION == "clean" ]] || [[ $ACTION = "superclean" ]] +ALL_ACTIONS=(plan apply init init-upgrade check destroy clean superclean tags) +action_valid=0 +for a in "${ALL_ACTIONS[@]}" +do + if [ "$ACTION" == $a ] + then + action_valid=1 + break + fi +done + +if [ $action_valid == 1 ] +then + echo "* running action=$ACTION" +else + echo "* invalid action=$ACTION" + echo "" + do_help + exit 1 +fi + +YMDSTAMP=$(date +%Y%m%d) +stime=$(date +%s) +STAMP="$YMDSTAMP.$stime" +LOGFILE="$LOGDIR/run.$ACTION.$STAMP.log" + +# from: https://stackoverflow.com/questions/25833676/redirect-echo-output-in-shell-script-to-logfile +if [[ "$START" != "list" ]] && [[ "$ACTION" != "init" ]] +then + exec > >(tee -i $LOGFILE) + exec 2>&1 +else + LOGFILE="$LOGFILE (not-created)" +fi + +echo "* START: $THIS $VERSION start=$stime end=$etime logfile=$LOGFILE" +[[ ! -z $PROFILE ]] || get_profile +[[ ! -z $REGION ]] || get_region + +if [ $ACTION == "init" ] +then + get_git_root + + if [ $GITSYSTEM == "github" ] + then + get_file_from_git tf-run.data "https://${GITSYSTEM}.e.it.census.gov/raw/terraform/support/master/local-app/tf-run/applications/base/tf-run.data" + get_file_from_git region.tf "https://${GITSYSTEM}.e.it.census.gov/raw/terraform/support/master/local-app/tf-run/applications/base/region.tf" + get_file_from_git locals.tf "https://${GITSYSTEM}.e.it.census.gov/raw/terraform/support/master/local-app/tf-run/applications/base/locals.tf" + get_file_from_git versions.tf "https://${GITSYSTEM}.e.it.census.gov/raw/terraform/support/master/local-app/tf-run/applications/base/versions.tf" + elif [ $GITSYSTEM == "gitlab" ] + then + get_file_from_git tf-run.data "https://${GITSYSTEM}.e.it.census.gov/terraform/support/-/raw/master/local-app/tf-run/applications/base/tf-run.data" + get_file_from_git region.tf "https://${GITSYSTEM}.e.it.census.gov/terraform/support/-/raw/master/local-app/tf-run/applications/base/region.tf" + get_file_from_git locals.tf "https://${GITSYSTEM}.e.it.census.gov/terraform/support/-/raw/master/local-app/tf-run/applications/base/locals.tf" + get_file_from_git versions.tf "https://${GITSYSTEM}.e.it.census.gov/terraform/support/-/raw/master/local-app/tf-run/applications/base/versions.tf" + fi + + if [ ! -r "$TOP/.tf-control" ] + then + if [ $GITSYSTEM == "github" ] + then + get_file_from_git .tf-control "https://${GITSYSTEM}.e.it.census.gov/raw/terraform/support/master/local-app/aws-account-setup/ansible/roles/setup-git-repo/files/.tf-control" + elif [ $GITSYSTEM == "gitlab" ] + then + get_file_from_git .tf-control "https://${GITSYSTEM}.e.it.census.gov/terraform/support/-/raw/master/local-app/aws-account-setup/ansible/roles/setup-git-repo/files/.tf-control" + fi + fi + if [ ! -r "$TOP/.tf-control.tfrc" ] + then + if [ $GITSYSTEM == "github" ] + then + get_file_from_git .tf-control.tfrc "https://${GITSYSTEM}.e.it.census.gov/raw/terraform/support/master/local-app/aws-account-setup/ansible/roles/setup-git-repo/files/.tf-control.tfrc" + elif [ $GITSYSTEM == "gitlab" ] + then + get_file_from_git .tf-control.tfrc "https://${GITSYSTEM}.e.it.census.gov/terraform/support/-/raw/master/local-app/aws-account-setup/ansible/roles/setup-git-repo/files/.tf-control.tfrc" + fi + fi + exit 0 +fi + +if [ $ACTION == "init-upgrade" ] +then + get_git_root + if [ ! -r "$TOP/.tf-control" ] + then + if [ $GITSYSTEM == "github" ] + then + get_file_from_git .tf-control "https://${GITSYSTEM}.e.it.census.gov/raw/terraform/support/master/local-app/aws-account-setup/ansible/roles/setup-git-repo/files/.tf-control" + elif [ $GITSYSTEM == "gitlab" ] + then + get_file_from_git .tf-control "https://${GITSYSTEM}.e.it.census.gov/terraform/support/-/raw/master/local-app/aws-account-setup/ansible/roles/setup-git-repo/files/.tf-control" + fi + fi + if [ ! -r "$TOP/.tf-control.tfrc" ] + then + if [ $GITSYSTEM == "github" ] + then + get_file_from_git .tf-control.tfrc "https://${GITSYSTEM}.e.it.census.gov/raw/terraform/support/master/local-app/aws-account-setup/ansible/roles/setup-git-repo/files/.tf-control.tfrc" + elif [ $GITSYSTEM == "gitlab" ] + then + get_file_from_git .tf-control.tfrc "https://${GITSYSTEM}.e.it.census.gov/terraform/support/-/raw/master/local-app/aws-account-setup/ansible/roles/setup-git-repo/files/.tf-control.tfrc" + fi + fi + exit 0 +fi + +if [ $ACTION == "check" ] +then + TF_UPGRADE_MODULES="" + TF_UPGRADE_MODULES+=" aws-common-security-groups" + TF_UPGRADE_MODULES+=" aws-edl-launch-instance" + TF_UPGRADE_MODULES+=" aws-iam-role" + TF_UPGRADE_MODULES+=" aws-iam-user" + TF_UPGRADE_MODULES+=" aws-inf-setup" + TF_UPGRADE_MODULES+=" aws-s3" + TF_UPGRADE_MODULES+=" aws-setup-s3-object-logging" + TF_UPGRADE_MODULES+=" aws-tls-certificate" + TF_UPGRADE_MODULES+=" aws-vpc-setup" + TF_UPGRADE_MODULES+=" dns-lookup" + TF_UPGRADE_MODULES+=" aws-ecr-copy-images" + _TFSTRING="" + for f in $TF_UPGRADE_MODULES + do + if [ ! -z $_TFSTRING ] + then + _TFSTRING+="|" + fi + _TFSTRING+="$f" + done + + echo -n "* [check] outdated .tf with git::https format: " + c=$(cat *.tf 2>/dev/null| grep -cE '^[^#]*source.*git::https') + if [ $c == 0 ] + then + echo "OK" + else + echo "NOT-OK" + echo "* found $c source statements, please fix to git@ format." + grep -E '^[^#]*source.*git::https' *.tf + fi + echo "" + + echo -n "* [check] for .tf for eligible modules not using ?ref=tf-upgrade: " + c=$(cat *.tf | grep -E "^[^#]*source.*git.*($_TFSTRING)" | grep -c -v ref=tf-upgrade) + if [ $c == 0 ] + then + echo "OK" + else + echo "NOT-OK" + echo "* found $c source statements not referencing ref=tf-upgrade, please verify with the list at" + echo " https://${GITSYSTEM}.e.it.census.gov/terraform/support/blob/master/docs/how-to/terraform-upgrade/upgrade-code.md#modules" + echo " and change accordingly if the module here is on the list." + grep -E "^[^#]*source.*git.*($_TFSTRING)" *.tf | grep -v ref=tf-upgrade + fi + echo "" + exit 0 +fi + +if [[ $ACTION == "clean" ]] || [[ $ACTION == "superclean" ]] +then + echo ""; echo "About to execute $ACTION. This is destructive and will remove files." + ask_continue "Continue (y|n)? " "" "n" + if [ $CONTINUE == "y" ] + then + if [ $ACTION == "clean" ] + then + do_clean + else + do_superclean + fi + else + echo "* action $ACTION declined" + fi + exit 0 +fi + +TFRUNFILE_VERSION="" +if [ $ACTION == "destroy" ] +then + if [ -r "tf-run.destroy.data" ] + then + RUNFILE="tf-run.destroy.data" + else + TFRUNFILE_VERSION="generated.$STAMP" + RUNFILE="ALL" + fi +else + RUNFILE="tf-run.data" +fi + +# read file tf-run.data +declare -a targets=() +declare -a targets_status=() +declare -A tags +# TFRUNFILE_VERSION="" +# RUNFILE="tf-run.data" +if [ -r $RUNFILE ] +then + c=1 + pos=1 + echo "* reading from $RUNFILE" + while IFS="" read line + do + nline=$(echo $line | sed -e 's/^#.*$//') + if [ ! -z "$nline" ] + then + targets+=( "$line" ) + targets_status+=0 + words=( $line ) + pos=$(( pos + 1 )) + if [ ${words[0]} == "VERSION" ] + then + TFRUNFILE_VERSION=${words[1]} + pos=$(( pos - 1 )) + fi + if [ ${words[0]} == "TAG" ] + then + pos=$(( pos - 1 )) + tags[${words[1]}]=$pos + fi + fi + c=$(( $c + 1 )) + done < $RUNFILE + echo "* read ${#targets[@]} entries from $RUNFILE" +elif [ "$RUNFILE" == "ALL" ] + then + targets+=( "ALL" ) + targets_status+=0 + c=1 +else + echo "* unable to open tf-run.data, exiting" + exit 1 +fi +TOTAL_TARGETS=${#targets[@]} + +if [ $ACTION == "tags" ] +then + echo "* available TAGS and step numbers" + for t in "${!tags[@]}" + do + echo "TAG $t = ${tags[$t]}" + done + echo "" + exit 0 +fi + +if [ -z "$START" ] +then + START=$2 +fi +if [[ ! -z "$START" ]] && [[ "$START" == "list" ]] +then + LIST=1 + echo "> list" +elif [ ! -z "$START" ] +then + LIST=0 + + if [ $(echo "$START" | grep -c "^tag:" ) -gt 0 ] + then + START_TAG=$(echo "$START" | sed -e 's/^tag://') + if [ -z "$START_TAG" ] + then + echo "* start tag:NAME used but NAME is missing" + fi + START=${tags[$START_TAG]} + if [ -z "$START" ] + then + echo "* start tag:$START_TAG used but tag is not found" + fi + fi +else + LIST=0 +fi + +START=$(( START * 1 )) + +END=$3 +if [ ! -z "$END" ] +then + if [ "$END" == "only" ] + then + END=$START + elif [ $(echo "$END" | grep -c "^+") -gt 0 ] + then + NEND=$(echo "$END" | sed -e 's/^+//') + END=$(( START + NEND )) + elif [ $(echo "$END" | grep -c "^tag:" ) -gt 0 ] + then + END_TAG=$(echo "$END" | sed -e 's/^tag://') + if [ -z "$END_TAG" ] + then + echo "* end tag:NAME used but NAME is missing" + fi + END=$(( ${tags[$END_TAG]} - 1 )) + if [ -z "$END" ] + then + echo "* end tag:$END_TAG used but tag is not found" + fi + fi +fi +END=$(( END * 1 )) + +## c=1 +## for t in "${targets[@]}" +## do +## tfargs="" +## if [ "$t" != "ALL" ] +## then +## for tt in $t +## do +## tfargs+="-target=$tt " +## done +## fi +## echo "* $c tf-$ACTION $tfargs" +## c=$(( $c + 1 )) +## done +## exit 0 +## fi + +# add to history: .tf-run.history +TFR_HISTORY=".tf-run.history" + +#if [ ! -r $TFR_HISTORY ] +#then +# echo "timestamp,username,action,start,end,status" >> $TFR_HISTORY +#fi +#echo "$(date +%s),$USER,$ACTION,$START,$END,start" >> $TFR_HISTORY + +status=0 +echo ">> START: start_time=$stime version=$VERSION data.version=$TFRUNFILE_VERSION start=$START end=$END start_tag=$START_TAG" +echo "- profile=$PROFILE region=$REGION short_region=$SHORT_REGION" +c=0 +for t in "${targets[@]}" +do + c=$(( $c + 1 )) + next=$(( $c + 1 )) + + target=$t + words=( $t ) + w=${words[0]} + rest=$(echo "${words[@]:1}" | sed -e "s/%%NEXT%%/$next/g") + rest=$(replace_placeholders "$rest") + + if [[ "$w" == "VERSION" ]] || [[ "$w" == "TAG" ]] + then + TOTAL_TARGETS=$(( $TOTAL_TARGETS - 1 )) + fi + if [[ ! -z $START ]] && [[ $START -gt $c ]] + then + if [[ $w == "VERSION" ]] || [[ $w == "TAG" ]] + then + c=$(( $c - 1 )) + fi + continue + fi + if [[ ! -z $END ]] && [[ $END != 0 ]] && [[ $c -gt $END ]] + then + break +# echo "break c=$c end=$END" +# else +# echo "not break c=$c end=$END" + fi + + case $w in + REMOTE-STATE) + echo "> [$c] $w> generate-remote-state" + if [ $LIST == 0 ] + then + if [ -r ../remote_state.yml ] + then + cat ../remote_state.yml | sed -E s#\(^directory.*\)\"#\\1/$(basename $(pwd))\"# > remote_state.yml + status=$? + echo -n "* generated line: "; grep ^directory remote_state.yml + echo ""; echo "= Complete: $c $w> $rest | status=$status" + else + echo "* missing parent remote_state.yml, exiting" + status=1 + exit $status + fi + targets_status[$c]=$status + fi + continue + ;; + BACKUP-STATE) + echo "> [$c] $w> backup-state to $LOGDIR/backup.$STAMP.tfstate" + if [ $LIST == 0 ] + then + TFNOLOG=1 tf-state pull > $LOGDIR/backup.$STAMP.tftate + status=$? + echo ""; echo "= Complete: $c $w> backup-state | status=$status" + targets_status[$c]=$status + fi + c=$(( $c - 1 )) + continue + ;; + COMMAND) + echo "> [$c] $w> $rest" + if [ $LIST == 0 ] + then + $rest + status=$? + echo ""; echo "= Complete: $c $w> $rest | status=$status" + targets_status[$c]=$status + fi +# should check to see if we are running tf-directory-setup.py, so grab this stuff after each command if missing + [[ ! -z $PROFILE ]] || get_profile + [[ ! -z $REGION ]] || get_region + continue + ;; + LINKTOP) + LINKARG=$(replace_placeholders ${words[@]:1:1}) + get_relative_to_git_root + echo "> [$c] $w> ln -sf ${RELATIVE_PATH}${LINKARG} ./" + if [ $LIST == 0 ] + then + if [ -e "${RELATIVE_PATH}${LINKARG}" ] + then + ln -sf ${RELATIVE_PATH}${LINKARG} ./ + status=$? + else + echo "* linked-to file ${RELATIVE_PATH}${LINKARG} does not exist, skipping" + status=0 + fi + echo ""; echo "= Complete: $c $w> relative-link-to-top $LINKARG | status=$status" + targets_status[$c]=$status + fi + continue + ;; + LINK) + LINKARG=$(replace_placeholders ${words[@]:1:1}) + get_relative_directory $LINKARG + echo "> [$c] $w> ln -sf ${RELATIVE_PATH}${LINKARG} ./" + if [ $LIST == 0 ] + then + if [[ -e "${RELATIVE_PATH}${LINKARG}" ]] && [[ ! -z "${RELATIVE_PATH}" ]] + then +# don't make link if the file is found in the current directory as we'll make a looping link +# the get_relative_directory is looking for the real file in the hierarchy + if [ "${RELATIVE_PATH}" != "./" ] + then + ln -sf ${RELATIVE_PATH}${LINKARG} ./ + status=$? + fi + else + echo "* linked-to file ${RELATIVE_PATH}${LINKARG} does not exist in current or parent dirs, skipping" + status=0 + fi + echo ""; echo "= Complete: $c $w> relative-link $LINKARG | status=$status" + targets_status[$c]=$status + fi + continue + ;; + VERSION) + c=$(( $c - 1 )) + continue + ;; + TAG) +# this is a placeholder, almost like a go-to, but it does not increment the number + echo "" + echo "# [$w] $rest" + c=$(( $c - 1 )) + targets_status[$c]=$status + continue + ;; + COMMENT) + echo "> [$c] $w> $rest" + status=$? +## echo ""; echo "= Complete: $c $w> $rest | status=$status" + targets_status[$c]=$status + continue + ;; + CHECK) + echo "> [$c] $w> $rest" + status=$? + targets_status[$c]=$status + if [ $LIST == 0 ] + then + echo ""; echo "= Complete: $c $w> $rest | status=$status" + ask_continue "} Next: $next, " + if [ $CONTINUE == "n" ] + then + break + fi + fi + continue + ;; + PAUSE) + if [ -z $rest ] + then + SLEEP=15 + else + SLEEP=$rest + fi + if [ $LIST == 0 ] + then + echo "> [$c] $w> sleeping for $SLEEP" + sleep $SLEEP + else + echo "> [$c] $w> would sleep for $SLEEP (actual sleep 2)" + sleep 2 + fi + status=$? + targets_status[$c]=$status + if [ $LIST == 0 ] + then + echo ""; echo "= Complete: $c $w> $rest | status=$status" + ask_continue "} Next: $next, " + if [ $CONTINUE == "n" ] + then + break + fi + fi + continue + ;; + STOP) + echo "> [$c] $w> $rest" + status=$? + if [ $LIST == 0 ] + then + echo ""; echo "= Complete: $c $w> $rest | status=$status" + echo "- Continue $next: $THIS $ACTION $next" + break + else + continue + fi + ;; + CLEAN) + ;; + POLICY) + PFILES="${words[@]:1}" + if [ -z $PFILES ] + then + PFILES="*.tf" + fi + PTARGETS=( $(grep -iE "^resource\b.*aws_iam_policy\b" $PFILES | awk '{print $2 "." $3}' |sed -e 's/"//g') ) + status=$? + echo "> [$c] $w> ($PFILES) ${PTARGETS[@]}" + target="${PTARGETS[@]}" + targets_status[$c]=$status + if [ -z "$target" ] + then + if [ $LIST == 0 ] + then + echo "= No policy targets found, skipping this step | status=$status" + echo "- Continue $next: $THIS $ACTION $next" + fi + continue + fi + ;; + *) + ;; + esac + + if [ $status != 0 ] + then + echo "* error encountered, status=$status; exiting" + exit $status + fi + + tfargs="" + if [ "$t" != "ALL" ] + then + for tt in $target + do + tfargs+="-target=$tt " + done + fi + if [ $LIST == 1 ] + then + echo "> [$c] tf-$ACTION $TFOPTIONS $tfargs" + continue + fi + + echo "> [$c] tf-$ACTION $TFOPTIONS $tfargs" + if [ -z $DRY_RUN ] + then + tf-$ACTION $TFOPTIONS $tfargs + else + echo " (dry-run)" + fi + status=$? + targets_status[$c]=$status + + if [ $status != 0 ] + then + echo "> [$c] exiting status=$status" + break + fi + + if [ -z $DRY_RUN ] + then + echo ""; echo "= Complete: $c $w> $rest | status=$status" + ask_continue "} Next: $next, " +# ask_continue + fi + + if [ $CONTINUE == "n" ] + then + break + fi +done + +etime=$(date +%s) +xtime=$(( $etime - $stime )) +if [ $c -ge $TOTAL_TARGETS ] +then + if [ $LIST == 0 ] + then + echo "<< COMPLETE $c/$TOTAL_TARGETS targets" + fi +else + if [ $LIST == 0 ] + then + echo "<< INCOMPLETE $c/$TOTAL_TARGETS last_item=$c" + fi +fi +echo "<< END: start_time=$stime end_time=$etime elapsed=$xtime logfile=$LOGFILE status=$status" +exit $status + +## TO DO +# add LINK to make a link to a file in successive parent directories (variables.vpc.tf, for example) +# make regionshort +# use get_region and get_profile diff --git a/scripts/tf-run.data b/scripts/tf-run.data new file mode 100644 index 0000000..ea54249 --- /dev/null +++ b/scripts/tf-run.data @@ -0,0 +1,20 @@ +VERSION 1.3.1 +REMOTE-STATE +COMMAND tf-directory-setup.py -l none -f +COMMAND setup-new-directory.sh +COMMAND tf-init -upgrade + +LINKTOP includes.d/variables.account_tags.tf +LINKTOP includes.d/variables.account_tags.auto.tfvars +LINKTOP includes.d/variables.infrastructure_tags.tf +LINKTOP includes.d/variables.infrastructure_tags.auto.tfvars +LINKTOP includes.d/variables.application_tags.tf +LINKTOP includes.d/variables.application_tags.auto.tfvars +LINKTOP common/remote_state.common.tf +LINKTOP infrastructure/%%SHORT_REGION%%/remote_state.infrastructure_%%SHORT_REGION%%.tf + +# module.subnet_tags +# module.sg_web module.base-security-groups + +ALL +COMMAND tf-directory-setup.py -l s3