Contract specification

Parameterless Deployment

A canonical deployable root runs under a generic deployer with no ad hoc parameters; every value it needs resolves from committed, well-defined sources.

Applies at
env/
Status
Stable
Source · managed
env/README.md

01 Defines

The execution contract for canonical environment roots: a deployer that knows only the repository’s conventions can run any root, because nothing about the deployment is supplied by hand at execution time.

02 Applies when

  • A root under env/ is a continuous-deployment target.
  • Several roots are driven by the same generic deployment system.
  • The same deployment has to be repeatable from CI, from a manual dispatch, or from a laptop.

03 Boundaries

  • Developer copies whose identity comes from the user or branch belong in workspaces/, which is allowed to depend on context.
  • Choosing which root to run is the caller’s decision; the contract covers how that root runs.
  • Credentials are resolved by convention, such as named profiles or the ambient chain, not committed as values.

Expected behaviour

  1. 01
    No -var flags, .tfvars files, or interactive prompts are needed to plan or apply a root.
  2. 02
    Required values come from committed configuration, directory conventions, module defaults, or data sources.
  3. 03
    Backends are committed with the root, so state location is never an execution-time choice.
  4. 04
    Running the same root twice from the same commit produces the same deployment.

Behaviour#

If a deployment needs someone to remember a flag, it is only as reliable as that memory. Canonical roots avoid the question entirely: every value they need is written down in the repository or looked up from a well-defined source, so the only thing a deployer has to know is which directory to run.

That is what lets one generic deployer drive every root. It does not carry per-environment knowledge; it runs terraform init, apply, and reads outputs using the same commands everywhere, with -input=false so a missing value fails loudly instead of prompting.

FlowWhere values come from
  1. 01 Committed configuration Backend, domain, tags, and provider profiles in the root’s .tf files Environments
  2. 02 Directory convention The root’s path identifies the deployed unit and tier
  3. 03 Module defaults Anything the root does not set takes the module’s default Modules
  4. 04 Data sources Lookups at plan time, such as Route 53 zone IDs read from SSM

Canonical and derived#

Parameterless (env/)

  • The root’s identity is fixed by its directory
  • State lives at the backend key the root commits
  • Running it twice targets the same instance

Derived identity (workspaces/)

  • Identity comes from the Terraform workspace
  • The workspace is chosen from user, branch, or override
  • Each developer or branch gets a separate instance

Developer context is useful, but it is still a parameter. Keeping it in Workspaces — see Derived Developer Identity — is what lets env/ promise that nothing varies at execution time.

Examples#

Everything the production root needs is in the files beside it:

env/prod/release/main.tf
locals {
  site_domain = "sampletest.dev.prosyon.ca"

  tags = {
    Environment = "production"
    Project     = "static-website"
    ManagedBy   = "terraform"
  }
}

The deployer passes nothing but the root path, and refuses to prompt:

scripts/deploy.sh
terraform -chdir="$root" init -input=false
terraform -chdir="$root" apply -input=false -auto-approve

From the command line, choosing the target is the whole invocation:

make deploy ENV=prod