A CLI tool for managing Terraform remote backends and bootstrapping infrastructure
Project description
tfwrap
A CLI tool for managing Terraform remote backends and bootstrapping infrastructure with AWS S3 state storage.
Installation
From PyPI
pip install tfwrap
From source
git clone https://github.com/timothy-cloudopsguy/tfwrap.git
cd tfwrap
pip install .
Quickstart
tfwrap provides a CLI to bootstrap and manage Terraform backends. It supports bootstrap, init, plan, apply, destroy, destroy-all, and clean commands and accepts an environment selection either via the ENV environment variable or the -e / --env flag.
Important behavior to know up-front:
tfwrapis idempotent: it will check for an SSM Parameter containing backend metadata before bootstrapping resources. If the SSM parameter exists, the script uses it; otherwise it runs the bootstrap and writes the backend metadata to SSM for future runs.- The bootstrap and main run both use the same environment selection (
ENVor-e) to determine whichproperties.<ENV>.jsonfile to load.
Usage examples:
# One-off invocation using environment variable (no export)
ENV=dev tfwrap plan
# One-off invocation using the -e flag
tfwrap -e dev plan
# Or export then run multiple commands
export ENV=prod
tfwrap plan
tfwrap apply
Note
- The chosen environment must match one of the
properties.<ENV>.jsonfiles in the repo (e.g.dev,prod).tfwrapwill fail if it cannot find the matching properties file and--app-nameis not provided.
Stacks in this example
- This repo now contains two logical stacks:
- core: foundational shared resources for the environment (bootstrap backend, a global CloudFront distribution used for routing/control-plane, CloudFront Function, and a Key-Value Store (KVS)). The
corestack publishes a small set of values (for example an SSL certificate ARN and backend metadata) into SSM so downstream stacks can consume them. - frontend: the blue/green frontend stack that creates
blueandgreenS3 buckets and per-site CloudFront distributions for the application (these are the caching/data-plane distributions). The frontend reads values (example: SSL cert ARN) from SSM and from Terraform outputs when needed.
- core: foundational shared resources for the environment (bootstrap backend, a global CloudFront distribution used for routing/control-plane, CloudFront Function, and a Key-Value Store (KVS)). The
Ordering and dependencies
- Ordering matters: create and apply the core stack before the frontend stack. The frontend demonstrates how to consume a value (an SSL certificate ARN) that the core stack publishes into SSM. While the frontend does not itself provision the cert in this example, it expects the cert value to exist in SSM so it can be referenced (this models a real-world pattern where core teams provide common infrastructure and certs).
- Typical flow:
- Run
tfwrap bootstrap/tfwrap applyfor thecorestack to ensure backend, KVS, CloudFront Function, and SSM values exist. - Run
tfwrapfor thefrontendstack (plan/apply) oncecoreis present.
- Run
Why this repo exists
- Safe, isolated Terraform state: The bootstrap creates an S3 bucket scoped to this repository/stack and uses S3 for both state storage and locking so the remote backend is coupled to this repo only (not a shared/global bucket). That reduces blast radius if state becomes corrupted. This is a much simpler solution.
- Example blue/green frontend: The Terraform code shows a blue/green deployment pattern for a static/frontend application served via CloudFront and S3.
How the bootstrap works (high level)
- Run
./tfwrapfrom the repo root. The script:- Reads the
ENVenvironment variable or-e/--envflag to determine whichproperties.<ENV>.jsonfile to use; the bootstrap step uses the same environment selection so the same environment/region/account locking applies during bootstrap. - Runs a small bootstrap Terraform workspace (in
bootstrap/) that creates an S3 bucket used for Terraform remote state and locking. - Migrates local state into that newly-created S3 backend and then runs the normal plan/apply flow for the example stack.
- Ensures
backend.tfis created/managed safely during the migration to avoid accidentally clobbering other backends.
- Reads the
Environment properties (properties.<ENV>.json)
- This repo includes
properties.dev.json,properties.prod.json(and the Terraform code readsproperties.${var.environment}.json). These files lock a deployment to a specific environment, region, and account by providing the canonical properties the stack uses at plan/apply time. - Why they exist:
- They capture environment-specific inputs (for example,
app_name, region-specific settings, or deploy-time flags) in a single JSON file that Terraform andtfwrapread. - They allow running the exact same stack multiple times in the same account (e.g., two separate deployments of the same application) by giving each deployment its own properties file and therefore its own derived names and backend keys.
- They capture environment-specific inputs (for example,
- How to use them:
- Pass the environment to Terraform (the repository's
variables.tfexpects anenvironmentvariable that maps toproperties.<env>.json). tfwrapreadsproperties.${ENV}.json(viaENVor-e/--env) to synthesizeAPP_NAMEand other defaults if not explicitly supplied.
- Pass the environment to Terraform (the repository's
Per-repo backend vs a global backend (and CloudFormation comparison)
- Per-repo backend (this repo)
- Pros: Isolation — state corruption or accidental deletes are limited to this stack. Easier to reason about ownership and lifecycle per project. You can still export values using Terraform outputs and publish important values to SSM Parameter Store for cross-stack consumption.
- Cons: More S3 resources to manage (one per repo/stack).
- Global backend (single S3 for many stacks)
- Pros: Fewer backend resources to manage centrally; easier to standardize configuration.
- Cons: Higher blast radius — corruption or misconfiguration could affect many stacks at once.
- CloudFormation model
- CloudFormation manages state for you inside AWS (no separate state bucket). That simplifies things operationally but means you are relying on the service’s control plane; it’s harder to get the same per-stack storage isolation semantics for non-CloudFormation tooling. The per-repo Terraform backend gives you explicit control over state isolation similar to how CloudFormation scopes stacks — but with Terraform you choose where that state lives.
Idempotent bootstrap & SSM-stored backend metadata
tfwrapis written to be idempotent: it checks for existing backend configuration and the SSM parameter the bootstrap creates before attempting to create backend resources.- The bootstrap step will create an SSM Parameter that stores the backend content (the backend HCL or equivalent metadata). On subsequent runs
tfwrapwill read that SSM parameter and reuse the stored backend configuration instead of re-bootstrapping.- If the SSM parameter exists and contains valid backend information, the script uses it directly.
- If the SSM parameter does not exist (first run or intentionally removed), the script runs the bootstrap Terraform under
bootstrap/, creates the S3 bucket, and then writes the backend content into the SSM parameter for future runs.
- This pattern provides a safe, repeatable bootstrap that avoids recreating or clobbering backend resources once they exist, and makes the script safe to re-run in CI/CD or local workflows.
Sharing values between stacks
- Use
terraform outputto expose ARNs, bucket names, and other values from a stack. - Persist important, shared values in SSM Parameter Store (the example modules write SSM parameters for bucket names/ARNs). Other stacks can read those parameters to build cross-stack references without directly reading another stack’s state.
Blue/green example notes
- This code models a blue/green deployment for a frontend application behind CloudFront, with separate
blueandgreensite buckets and CloudFront distributions. - A higher-level routing layer (included in this repo as "core") is responsible for directing traffic to the blue or green variant. That routing can be implemented by:
- CloudFront key-value store / request routing (CloudFront Functions or Lambda@Edge decision logic), or
- A top-level CloudFront + Route53 setup that uses TXT records or other control-plane signals to act as a decision tree (the top-level CloudFront performs routing decisions but does not perform caching for this purpose).
Notes & caveats
tfwrapcontains helpful logic around creating a minimalbackend.tfand migrating state; read the script if you need to adjust naming, region, or table names (there is an override for the S3 locking configuration if required).- The bootstrap creates and/or reuses an SSM Parameter containing backend metadata — the script checks this parameter on each run so the bootstrap is safe to run repeatedly.
- If you need cross-stack references, prefer SSM Parameter Store or explicit exported values rather than directly coupling into another stack's state file.
License
- This repository is provided under the license in
LICENSE.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file tfwrap-1.0.3.tar.gz.
File metadata
- Download URL: tfwrap-1.0.3.tar.gz
- Upload date:
- Size: 70.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.9.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f5402d1e7f309942f03a10ce6c405775e4f1c0e0433323ebf9f269b3de24a104
|
|
| MD5 |
b737c77a89bc62cae87bfdf6e3542a92
|
|
| BLAKE2b-256 |
8d3da04486f09de3c172ccdd37817cf3d8a96316e29eb61fdead209b004fda61
|
File details
Details for the file tfwrap-1.0.3-py3-none-any.whl.
File metadata
- Download URL: tfwrap-1.0.3-py3-none-any.whl
- Upload date:
- Size: 36.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.9.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ede711a26e18fdcde6fa477a2e54a8d9037b80778c08c8a4ca8d0849ef7ca845
|
|
| MD5 |
c0d68457889e69c076e5685e712bfd5b
|
|
| BLAKE2b-256 |
76b1998a235603c186a68f22dbfc8242dfc7a9bba8588b009857f740666dae70
|