Infrastructure as code
Desired state or a list of commands
Infrastructure as code puts infrastructure definitions in files that can go through the same review and version-control practices as application code. The important distinction is how a tool interprets those files.
A declarative definition describes the end state. The provisioning system determines how to reach it. An imperative script lists the operations to execute in order.
# Declarative: describe the resource that should exist.
resource "example_network" "app" {
cidr = "10.20.0.0/16"
}
# Imperative: prescribe the operation to run.
cloudctl network create --name app --cidr 10.20.0.0/16
Declarative does not mean that no commands run. It means the configuration states the goal and the provisioning system chooses the operations needed to pursue it.
Wrong "Declarative tools run the file from top to bottom."
Right A tool such as Terraform uses implicit and explicit relationships between resources to determine an operation order. Block order and file order are generally insignificant.
Plan before apply
Terraform exposes a useful two-part workflow. terraform plan reads managed remote objects, compares the current configuration with prior state, and proposes actions. It does not change remote infrastructure. terraform apply performs planned actions through providers.
terraform plan -out=tfplan
terraform apply tfplan
Saving a plan lets automation apply that plan artifact. A plan produced without -out is speculative. Changes to the remote system after a speculative plan can make a later plan differ, so a pull-request preview is evidence for review rather than a permanent promise.
Why Terraform has state
Terraform state records which remote object belongs to each resource instance in configuration. For example, state can bind example_network.app to the provider's network identifier. Terraform also keeps management metadata there.
State is separate from both configuration and live infrastructure:
- Configuration describes the desired resources.
- State maps managed resource instances to remote objects.
- Provider APIs report the current remote objects during refresh.
If someone edits a managed object outside the workflow, the live object can differ from configuration and recorded state. This is drift. A normal plan refreshes Terraform's in-memory view of managed objects and can propose actions that reconcile the live object with configuration.
Wrong "State is the desired state, so Terraform ignores console changes."
Right Configuration expresses the desired state. Terraform uses state to identify managed objects, refreshes their current attributes, and compares what exists with what the configuration requests.
Drift is a decision about intent
A plan that reports an out-of-band change has found a difference. It has not decided whether the manual change was correct. The team still has to choose which representation should win.
For an accidental manual change, review a normal plan that restores the checked-in configuration:
terraform plan -out=tfplan
terraform apply tfplan
For an intentional emergency change, first inspect the drift without changing remote objects:
terraform plan -refresh-only
A refresh-only apply records current remote values in state without changing remote infrastructure. It does not update configuration. If the manual setting should persist, encode that intent in configuration and review the resulting normal plan. Otherwise, a later normal plan can try to reverse the manual setting.
Wrong "Accepting a refresh-only plan makes the code match production."
Right Refresh-only mode reconciles Terraform state with remote objects. Configuration remains a separate file that the team must update.
State is shared operational data
The default local backend writes terraform.tfstate on disk. HashiCorp recommends HCP Terraform or a remote backend for collaboration and warns against putting state in version control or storage without secure access control and state locking.
State locking is a backend capability, and backend support varies. Its purpose is to prevent concurrent writers from corrupting or racing updates to the same state. Access controls matter because state can contain secrets and detailed resource attributes.
Use Terraform's state commands when a binding must change. The underlying JSON format can evolve, so direct edits create an unsupported maintenance burden.
terraform state list
terraform state mv example_service.old example_service.current
Modules create a contract
A Terraform module is a collection of resources managed together. Every configuration has a root module. A module block calls a child module, supplies inputs, and can consume outputs.
module "application_network" {
source = "example.com/platform/network/cloud"
version = "2.3.0"
environment = "production"
cidr = "10.20.0.0/16"
}
Modules can standardize repeated infrastructure, but abstraction has a cost. Microsoft recommends using modules for complex configurations or combinations of resources and warns against excessive abstraction. HashiCorp likewise recommends moderation because too many modules can make a configuration harder to understand and maintain.
A useful boundary groups resources with a coherent purpose and lifecycle. Inputs expose the choices callers may make. Outputs expose only what callers need. A module version gives consumers a reviewable upgrade point.
Wrong "Any repeated resource should become a module."
Right Reuse is one reason to extract a module. The boundary should still simplify the callers and preserve a comprehensible lifecycle.
sensitive means redacted
Terraform can mark variables and outputs as sensitive. It then redacts those values from normal CLI output and the HCP Terraform UI. The values can still be present in state and plan files.
variable "database_password" {
type = string
sensitive = true
}
Do not commit credentials, local state, or saved plan artifacts. Restrict access to remote state and plan storage, and use the backend's supported protection. A saved plan contains the full configuration and planned values. If the plan includes sensitive data, that data is stored in cleartext even when terminal output hides it.
Wrong "The sensitive flag encrypts a value and removes it from state."
Right The flag controls display redaction. Preventing persistence requires a supported ephemeral or write-only mechanism, covered in the senior notes, and secure handling is still required for every artifact that contains the value.
Review the artifact that will run
A speculative plan is useful in a pull request, but it does not freeze configuration, state, or remote infrastructure. HashiCorp's plan documentation says that changes to the target system can alter the final effect of a later configuration change. The final non-speculative plan still needs review.
Automation can save a plan and pass that artifact to apply:
terraform plan -out=tfplan
terraform show tfplan
terraform apply tfplan
Terraform applies the changes recorded in the saved plan instead of generating a new plan. The planning decisions come from the reviewed artifact, but provider APIs can still reject operations or encounter changed remote conditions. A plan file that includes sensitive data stores it in cleartext, so the pipeline must protect saved plans as sensitive artifacts.
Routine use of resource targeting weakens the full-configuration comparison. Terraform documents -target for exceptional circumstances and warns that routine use can lead to undetected drift and confusion about the relationship between configuration and real resources.
Wrong "A successful plan proves apply will succeed."
Right A plan describes proposed actions. Apply still calls remote APIs, and intervening conditions can affect those operations.
Secrets require a persistence model
Ask two separate questions: where does the secret enter the run, and where can Terraform persist it? Redaction answers neither one completely.
In Terraform 1.10 and later, an ephemeral variable or child-module output can be available during an operation while being omitted from state and plan files. References to ephemeral values are restricted to supported ephemeral contexts. Root-module outputs cannot be ephemeral.
variable "api_token" {
type = string
sensitive = true
ephemeral = true
}
provider "example" {
api_token = var.api_token
}
Terraform 1.11 and later supports write-only managed-resource arguments, when the provider defines them. Those arguments let a provider receive a value without Terraform persisting that resource value. Provider support is part of the design constraint, so this is not a universal switch for every secret field.
Wrong "Use sensitive = true; state security is then optional."
Right Sensitive values can be stored in state and plans. Use omission features where supported, keep credentials out of configuration, and restrict every remaining state or plan artifact that can carry a secret.
Immutable infrastructure replaces instances
Microsoft defines immutable infrastructure as infrastructure intended to be replaced with newly configured infrastructure on each deployment, rather than changed in place. Mutable infrastructure receives configuration changes in place.
Immutability is a lifecycle choice, not a claim that an environment never changes. The new version arrives as replacement infrastructure. Traffic can move after the replacement passes checks, and the old version can then be retired.
build replacement -> verify replacement -> move traffic -> retire old instance
This pattern fits replaceable compute more easily than resources that hold durable data. A module or state boundary that couples an application instance to a database can force unrelated resources into one replacement and recovery decision. Separate boundaries can reflect different lifecycles, but each boundary adds coordination and operational overhead.
Terraform's -replace=ADDRESS option can request replacement for a resource that would otherwise be updated or left unchanged. It has been available since Terraform 0.15.2. That command supports a replacement action; it does not by itself supply traffic shifting, health verification, data migration, or rollback.
Modules should preserve ownership
Shared modules standardize resource combinations and expose a smaller contract. They can also hide consequential defaults from callers. Pin a tested module version and review upgrades as infrastructure changes.
module "service" {
source = "example.com/platform/service/cloud"
version = "4.2.0"
name = "billing"
environment = "production"
}
Avoid a deep module tree when composition keeps ownership clearer. HashiCorp recommends a relatively flat module tree because independently reusable modules are easier to combine. The senior tradeoff is not reuse versus duplication in the abstract. It is whether the module contract makes lifecycle, security, and ownership decisions visible to the teams that must operate them.
dig deeper
Primary sources behind these notes - the specs and official docs worth reading in full.
beta
The interviewer part is in the works.
The diagnostic, personal maps, and AI mock interviews are being finished right now. The notes stay free either way. Leave an email and you'll get the first-cohort invite, plus a month of Pro when it opens.