A Wrapper for Terraform

Brandon Wagner
Aug 23, 2017 · 3 min read

Terraform is a great tool for orchestrating infrastructure and storing infrastructure-as-code. It’s very similar to AWS’s CloudFormation and Microsoft Azure Resource Manager (ARM), with the exception that Terraform is cloud agnostic and AWS/Microsoft’s tools can only be used on their respective platforms. This makes Terraform a great tool to know for whatever cloud environment you’re working in.

There’s a couple of flaws that I’ve stumbled upon while using Terraform in deployment pipelines:

  1. Terraform Stores State Locally By Default
  2. Terraform Does Not Handle Version Updates Well
  3. Terraform Does Not Handle “applications” and “environments” natively

As a result a co-worker and I decided to write a simple wrapper around the terraform binary to handle some remote state setup in S3, ensure we have the right version of Terraform, and setup a directory structure to support application and environments for those applications.

Terraform stores a state file locally which it uses to do delta updates on deployed infrastructure and terminate infrastructure. It also has the ability to hook into a remote backend such as S3, but the command is a bit awkward.

terraform init -backend=true -backend-config="region=us-east-1" 
-backend-config="bucket=terraform-state-bucket" -backend-config="profile=IAM_PROFILE" -backend-config="key=my-cool-tool-dev.tfstate" -force-copy -get=true -input=false

That very long command is very similar to what I had in a Jenkinsfile at one point. And then I wanted to add environments, so I had to template out this command to take in variables for dev, qa, and prod. Version updates were very painful because Terraform does not allow you to move between minor versions if there is a state file written with a specific version. With all of these issues, the wrapper was quickly (yep, it wasn’t too clean… just a bash script) created and solved the problem. Since then, I’ve revamped the terraform remote state script and call it “TRS”. It can be installed via pip. pip install trs Here’s the usage printed:

usage: trs [-h] [-t TARGET] [-r REGION] [-b BUCKET] [-a APP] [-e ENV]
[-p PROFILE] [-c CONFIG] [--auto_version] [-f] [-v]

optional arguments:
-h, --help show this help message and exit
-t TARGET, --target TARGET
module path (default: ".")
-r REGION, --region REGION
AWS Region (default: us-east-1)
-b BUCKET, --bucket BUCKET
S3 Bucket
-a APP, --app APP Application Name
-e ENV, --env ENV Environment (ie. dev, test, qa, prod)
-p PROFILE, --profile PROFILE
AWS Config Profile (default: default)
-c CONFIG, --config CONFIG
Path to .trsconf (default: "./.trsconf")
--auto_version Auto-Increment Terraform Version
-f, --force Force a TRS conf file update
-v, --version Version of trs

The wrapper’s main functions:

  1. Creates the S3 bucket for the remote state if it doesn’t exist already
  2. Stores the remote state in the bucket in a directory named from the “application” you pass in. The state file is named for the environment within the application directory.
  3. - If you’re destroying or updating an application-environment with a state file, the wrapper will check the version of the state file and automatically download the proper version of terraform.
    - If the state file is empty (it’s been terminated) other than the skeleton json, then the state file is deleted and the latest terraform is downloaded. This allows for clean updates of terraform in stacks that already have state files. For example, if your pipelines perform red/black or blue/green deployments, terraform can be cleanly updated after terminating the inactive stack.
    - If you’re applying a new terraform application-version with no previous state file written to S3, then it uses the latest terraform specified.

TRS has support for configuration files (.trsconf) which just stores the CLI args in a file. This makes it really clean and easy to run terraform on stacks for different applications. You can store .trsconf-APP files in a directory and point TRS to the right one. TRS completely wraps the terraform script via a pass through. So any argument that TRS doesn’t recognize as its own parameter will be passed to terraform.

trs --config ~/path/to/trsconf-app apply --var-file my-vars.tfvars

The command above will grab the config file from ~/path/to/trsconf-app , setup remote state with s3 depending on the values in the trsconf-app file, and then execute terraform apply --var-file my-vars.tfvars .

You can check out the wrapper here (PR’s welcome): https://github.com/ewolkowicz/terraform-remote-state

OR just install and start using!

pip install trs

)
Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade