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.

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:

# 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.
    • 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.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

  • 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.
  • 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.4.tar.gz (70.5 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.4-py3-none-any.whl (36.6 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: tfwrap-1.0.4.tar.gz
  • Upload date:
  • Size: 70.5 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.4.tar.gz
Algorithm Hash digest
SHA256 489fd6721e31d2adb3f9ed3783ff03d00477ef8da44268c2f8a501154736f837
MD5 220a6c9e683e723bb9d9d42e8b32bc4b
BLAKE2b-256 293d87ca15fe45f7530d2842bf6e2c47652d3949e1d421ab947fbcc2eb8f9b32

See more details on using hashes here.

File details

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

File metadata

  • Download URL: tfwrap-1.0.4-py3-none-any.whl
  • Upload date:
  • Size: 36.6 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.4-py3-none-any.whl
Algorithm Hash digest
SHA256 e1ede1ab11121d765c5c04d842c08c751af5658f4dc5f69973ca06486191e7dc
MD5 a4e419278f145e9068e5a85ef2c0a144
BLAKE2b-256 e9893a3808cbea7493c19cef37454bf46589eb6fe7fb98b4017dc2f9e1f51431

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