Templates & Provisioning Patterns
One blueprint, many environments
Templates (HCL, CloudFormation, bicep, Pulumi code) reduce an environment to a parameterized blueprint: variables, locals, module reuse, conditionals, and loops keep one tree standing in for N environments.
What you will learn
- Parameterize a config so one tree serves prod/stage/dev
- Compose reusable modules with inputs/outputs
- Use conditionals + loops sparingly and maintainably
Variables feed the blueprint; modules package a reusable unit (VPC, app, db) with typed inputs/outputs; conditionals/count/for mark variance. The same apply produces any environment by changing the variable values.
What is this?
Building blocks: variables (type, default, validation, sensitive), locals (tin-vars-Neuros), modules (compose resources, inputs/outputs/dependencies), conditionals (count, for_each, ternary) for env-gated features, data sources read existing infra, outputs expose links/interfaces. Tool-specific: CloudFormation parameters+mappings+Fn::If; bicep params+modules; Pulumi code (loops in language). Provenance: consistent naming from vars (project-env-region), tagging strategy, and per-env backends/remotes.
Why do you need it?
- N environments from one tree kill the copy-edit-drift fork problem.
- Modules encode standards (naming, tags, security defaults) once.
- Env-gated features (dev without HA) are a conditional, not a branch.
- Reviews improve the module once; every consumer inherits the fix.
Mental model
A franchise kit: same store design, local menu tweaks
Template + variables
The floor plan (template/module) is identical; the franchisee sets the local variables (city spec, stock) and gets their store — no second architect needed.
How it works
- Define variables + defaults; pass values per environment (tfvars/files or languages).
- Compose modules: module 'app' with inputs (image, count, environment).
- conditionals gate resources (create HA cluster only when enable_ha).
- for_each/count iterate over maps/lists (per-service SG rules).
- Plan resolves all of the above into a concrete resource graph per env.
Template → environments.
Examples
Parameterized module usage
module "app" { source = "./modules/app" name = "checkout" env = var.env # "dev" | "stage" | "prod" replicas = var.env == "prod" ? 3 : 1 subnet_ids = module.vpc.private_subnets tags = { environment = var.env, owner = "payments" }}locals { full_name = "${var.env}-${module.app.name}" }One call to 'app' produces prod/start/whatever by variables; module outputs chain the graph.
In production
- Standard landing-zone modules: vpc, iam roles, observability, per-env instantiation.
- Repo-per-team with shared module registry across org (Terraform registry / private).
- CI matrix applies dev+stage+prod from the same tree with different tfvars.
Best practices
- Keep modules small and opinionated; don't expose 200 knobs.
- Validate variables at the boundary (allowed values, size, env naming).
- Tag everything consistently — cost, ownership, environment.
- Never branch the repo per environment; branch values, not logic.
- Emit module outputs as stable contracts consumers depend on.
Common mistakes
- Copy-paste replication instead of modules (the drift factory).
- Huge parameter lists that defeat modularity; default-heavy APIs invite misuse.
- Conditionals that double in prod, e.g., env-dependent siblings with same names.
- Environment-specific hacks embedded in generic modules (env='qa' exception logic).
Debugging tips
- A module output mismatch resolves consumer plans — check the module's output field first.
- Conditional resources hiding in plan → trace the ternary/count expression with -target debug.
- Duplicate resource names across env → naming locals colliding; add env to the name prefix.
- Validate passes local but plan fails → real-world constraints (names, IAM) beat template rules; read the provider error.
Interview checkpoint
Hands-on lab
- Write a module with typed inputs/outputs
- Instantiate it for dev and prod with different variables
- Verify env-specific plans share one tree
One Template, Two Environments
40 min · intermediate
Quick quiz
Which is a reuse unit taking inputs and exposing outputs?
The maintainable way to vary dev-vs-prod resource counts: