Infrastructure as Code
Intermediate 14 min read Infrastructure as Code

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.

templatemodulevariableconditionalfor_eachparameterenvironment
Prerequisites: IaC Principles
Before we start

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
In 60 seconds

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.

Introduction

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.

Motivation

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.
Think about it

Mental model

Imagine

A franchise kit: same store design, local menu tweaks

Think of

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.

Mechanics

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.

Variables
Module
Conditionals
Plan
Apply
Hands-on

Examples

Parameterized module usage

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.

Real world

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.
Guidelines

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.
Watch out

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).
When things break

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.
Interviews

Interview checkpoint

Practice

Hands-on lab

One Template, Two Environments
40 min · intermediate
In progress
Objectives
  • 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

0/4 steps
0%
sandbox · Linux sandbox
$
Try these commands
Test yourself

Quick quiz

0/2
1

Which is a reuse unit taking inputs and exposing outputs?

2

The maintainable way to vary dev-vs-prod resource counts:

Go deeper

Official documentation