Skip to main content

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.

PyPI version Python versions License

Installation

From PyPI

pip install tfwrap

From source

git clone https://github.com/timothy-cloudopsguy/tfwrap.git
cd tfwrap
pip install .

demo recording

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. Use --version to display the current version.

Important behavior to know up-front:

  • tfwrap is 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 (ENV or -e) to determine which properties.<ENV>.json file to load.

Usage examples:

# Check version
tfwrap --version

# 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>.json files in the repo (e.g. dev, prod). tfwrap will fail if it cannot find the matching properties file and --app-name is 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 core stack 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 blue and green S3 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.

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:
    1. Run tfwrap bootstrap / tfwrap apply for the core stack to ensure backend, KVS, CloudFront Function, and SSM values exist.
    2. Run tfwrap for the frontend stack (plan/apply) once core is present.

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 ./tfwrap from the repo root. The script:
    • Reads the ENV environment variable or -e/--env flag to determine which properties.<ENV>.json file to use; the bootstrap step uses the same environment selection so the same environment/region/account locking applies during bootstrap.
    • If no bootstrap/ directory exists, automatically creates one with the necessary Terraform configuration to provision an S3 bucket for remote state storage.
    • Runs the bootstrap Terraform workspace 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.tf is created/managed safely during the migration to avoid accidentally clobbering other backends.

Environment properties (properties.<ENV>.json)

  • This repo includes properties.dev.json, properties.prod.json (and the Terraform code reads properties.${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 and tfwrap read.
    • 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.
  • How to use them:
    • Pass the environment to Terraform (the repository's variables.tf expects an environment variable that maps to properties.<env>.json).
    • tfwrap reads properties.${ENV}.json (via ENV or -e/--env) to synthesize APP_NAME and other defaults if not explicitly supplied.

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

  • tfwrap is 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 tfwrap will 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 output to 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 blue and green site 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

  • Terraform Version Requirement: tfwrap uses use_lockfile = true in the S3 backend configuration, which requires Terraform version 1.10.x or later. All example stacks and auto-generated bootstrap directories include a versions.tf file that enforces this requirement. If you need to use an older version of Terraform, you can modify the generated backend configuration to remove the use_lockfile line and adjust the required_version in versions.tf.
  • tfwrap contains helpful logic around creating a minimal backend.tf and 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.
  • The clean command removes Terraform state files, cache directories, and auto-generated bootstrap directories (identified by the presence of a .tfwrap-autogenerated marker file). User-provided bootstrap directories are preserved.
  • 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

tfwrap-1.0.2.tar.gz (66.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

tfwrap-1.0.2-py3-none-any.whl (36.8 kB view details)

Uploaded Python 3

File details

Details for the file tfwrap-1.0.2.tar.gz.

File metadata

  • Download URL: tfwrap-1.0.2.tar.gz
  • Upload date:
  • Size: 66.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.9.6

File hashes

Hashes for tfwrap-1.0.2.tar.gz
Algorithm Hash digest
SHA256 551b35bd84bc2a712d1cf21eece869606d6a7f25d43f066d9b2f15e1228f71cc
MD5 2f3cc0fc0fb90908f27963138bf18f4a
BLAKE2b-256 308bedb4fe4457a819198b54fed6eef1248014076b4d8f797bb3a3f6c2842d12

See more details on using hashes here.

File details

Details for the file tfwrap-1.0.2-py3-none-any.whl.

File metadata

  • Download URL: tfwrap-1.0.2-py3-none-any.whl
  • Upload date:
  • Size: 36.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.9.6

File hashes

Hashes for tfwrap-1.0.2-py3-none-any.whl
Algorithm Hash digest
SHA256 2628597ba8d81c42f4b2df53bd0183ee04ac9c5e59d96cde12ef742c523741ef
MD5 ddd4ac1f49f5c0726b2b7a8cb2d37434
BLAKE2b-256 b3e3774043659031446f023ed7972c33fa0284af4b09ce757bb5b0cddb50df75

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page