Infrastructure as Code¶
Infrastructure as code (IaC) manages infrastructure through reviewed, repeatable definitions rather than undocumented console actions. Its value is controlled change and recovery—not converting every operational action into a framework.
Core Properties¶
- definitions live in version control;
- repeated application converges toward the intended state;
- changes are reviewed before execution;
- environments are reproducible from declared inputs;
- drift and failed reconciliation are observable;
- sensitive values and runtime state are protected;
- ownership and deletion behavior are explicit.
Declarative tools describe desired resources; imperative tools describe steps. Most real systems combine both. Choose based on the resource API and lifecycle rather than treating one style as universally superior.
Boundaries and Ownership¶
Split stacks or states by lifecycle, permissions, blast radius, and ownership—not by creating a module for every resource. A useful boundary can be planned, applied, locked, recovered, and authorized independently.
Avoid circular dependencies and long chains of remote-state reads. Publish a small, stable interface such as a network ID or service endpoint rather than exposing an entire state snapshot.
One resource should have one authoritative manager. Console edits, two IaC stacks, and application controllers fighting over the same field create unsafe drift.
The Change Workflow¶
edit → format/validate → plan → review → apply saved plan → verify → record
The plan is a prediction based on configuration, state, provider behavior, credentials, and the observed remote system. It can become stale. Apply the reviewed saved plan promptly, then verify the actual outcome.
Destructive or replacing actions deserve explicit scrutiny. A green syntax check does not prove a network policy is reachable, a backup is restorable, or a migration is safe.
State¶
State maps declared resource instances to remote objects and may contain sensitive values. For tools that use state:
- store it in a durable remote backend with encryption and access control;
- enable locking or serialized writes;
- retain versioned backups and test recovery;
- never commit state or saved plans containing secrets;
- use supported commands rather than editing state files;
- isolate state according to blast radius and permissions;
- audit emergency unlocks and state surgery.
Terraform's official state and locking documentation describes its current behavior. Force-unlock only after proving the original writer is gone.
A Small Terraform Example¶
terraform {
required_version = ">= 1.10"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 6.0"
}
}
}
variable "bucket_name" {
type = string
}
resource "aws_s3_bucket" "artifacts" {
bucket = var.bucket_name
lifecycle {
prevent_destroy = true
}
}
Version constraints are illustrative and must match the repository's tested toolchain. Commit the dependency lockfile. prevent_destroy is a guardrail, not backup or universal policy.
Typical workflow:
terraform fmt -check
terraform init
terraform validate
terraform plan -out=tfplan
terraform show tfplan
terraform apply tfplan
Saved plans can contain sensitive data; protect and expire them.
Modules¶
A module should encode a stable, repeated capability with a small typed interface and safe defaults. Keep a resource inline until reuse, policy, or complexity justifies extraction.
Good modules:
- expose intent rather than every provider argument;
- validate inputs and document disruptive changes;
- return only outputs consumers need;
- pin compatible dependencies;
- include examples and upgrade guidance;
- have an owner and release process.
A mega-module with dozens of switches is harder to reason about than several explicit configurations.
Secrets¶
Marking a value “sensitive” may hide it from display but does not necessarily keep it out of state. Prefer references to secret-manager objects or short-lived identities over plaintext values. Restrict state access as if it contains secrets, because it often does.
Do not place secrets in source, variable files, plans, logs, or pipeline artifacts. Rotate exposed values rather than relying on deletion from history.
Policy and Security¶
Automated policy can reject public storage, broad identity permissions, missing encryption, unapproved regions, or absent ownership labels. Keep policies testable, versioned, explainable, and equipped with a controlled exception path.
Static scanning does not understand every runtime condition. Combine it with provider-side controls, least-privilege execution identity, review, post-apply verification, and continuous detection.
Testing¶
Use the cheapest evidence that matches the risk:
- parse, format, and type validation;
- provider and module contract checks;
- policy tests;
- plan assertions for critical properties;
- deployment to an isolated test account or project;
- connectivity, permission, resilience, and restore tests;
- production canaries and drift detection.
Mocks cannot prove cloud API behavior. Full environment tests are expensive, so reserve them for boundaries and failure modes that simpler checks cannot cover.
Drift and Imports¶
Drift can come from incident response, external controllers, provider defaults, or unmanaged resources. Detect it on a schedule and classify it:
- accept by updating code;
- reconcile by applying code;
- import an existing resource under ownership;
- deliberately ignore a field owned elsewhere;
- remove the resource safely.
Import establishes a state binding; it does not generate a correct design or prove that subsequent changes are safe.
Immutability and Configuration Management¶
Replaceable compute images reduce drift, but networks, databases, identity, and control planes still evolve in place. “Immutable infrastructure” is a strategy for selected components, not a literal property of the whole system.
Use image building for machine contents, IaC for resource lifecycle, and Configuration Management for settings that must converge on running systems. Minimize overlapping ownership.
Failure and Recovery¶
An interrupted apply may leave some remote operations complete and others failed. Do not blindly rerun or edit state. Inspect tool output, remote resources, state bindings, and provider documentation; then reconcile with the smallest supported operation.
For critical infrastructure, rehearse recovery of state, credentials, providers, modules, and external artifacts from a clean environment. IaC code alone is not a backup of data or third-party services.
Checklist¶
- Is each resource managed by one authoritative system?
- Do stack boundaries match ownership and blast radius?
- Is state remote, locked, encrypted, backed up, and access-controlled?
- Are plans reviewed for replacement and deletion?
- Are secrets excluded from code and artifacts where possible?
- Are modules justified by real reuse or policy?
- Are drift and failed applies detected and owned?
- Can the infrastructure and its state be recovered independently?
- Is post-apply behavior verified, not merely resource existence?
IaC succeeds when infrastructure changes become understandable, reviewable, and recoverable.