Infrastructure as Code is the practice of expressing the desired state of computing infrastructure (servers, networks, storage, identity, monitoring, DNS, queues, certificates, almost anything addressable through an API) in machine-readable source files that are committed to version control and applied by an automation tool. The source files describe what should exist; the automation tool decides what to create, update, or delete to make reality match that description. The output of a successful IaC run is a reproducible, audited change to live infrastructure plus a record of exactly which resources are now considered managed.
Before IaC, infrastructure was provisioned through three patterns that the exam contrasts with the IaC approach. The first is manual provisioning, often called ClickOps: an operator opens a cloud console, clicks through wizards, fills in forms, and remembers (or fails to remember) what they did. The change is invisible to anyone who was not watching the screen, the resulting resource is described nowhere except in the console itself, and rebuilding the same environment in another region or for another customer means clicking through the same wizards from memory. The second is imperative scripting: shell scripts, PowerShell, or SDK programs that call provider APIs one verb at a time (aws ec2 run-instances, then aws ec2 create-tags, then aws ec2 authorize-security-group-ingress). The script captures the procedure but not the state; running it twice usually produces two of everything, and editing it later means working out by hand which API calls now need a different verb. The third is configuration management in the Puppet, Chef, Ansible, or Salt sense, which excels at converging the inside of an existing machine (packages, services, files, users) but historically did not own the provisioning of the machine itself. IaC in the Terraform sense complements configuration management by owning the layer underneath: the existence and topology of the resources that configuration management then mutates.
The line that places Terraform squarely on the IaC side of that comparison is the declarative model. In a declarative tool you write what the end state should look like (one virtual network with these subnets, three virtual machines with this image and this size, one DNS record pointing at this load balancer) and the tool figures out which create, update, replace, or destroy operations are required to reach that end state from whatever exists today. You never write IF the vm exists THEN update its size ELSE create it; the engine derives that branch from comparing your desired state to the real state. Imperative scripts, by contrast, encode the procedure step by step. The declarative shape is what gives an IaC run two of its essential properties: idempotency (re-running the same configuration against the same real infrastructure produces no further changes) and diff-driven change (the tool can show you exactly which resources would be created, modified, replaced, or destroyed before any side effect happens). Both properties depend on the engine maintaining a record of what it believes exists, which is where state enters the picture in the very next chapter.
A complete IaC system in the Terraform sense involves three artifacts that the exam expects you to name precisely. The first is the configuration: one or more .tf files in a working directory, written in HashiCorp Configuration Language (HCL), declaring the desired resources, their inputs, their dependencies, and the providers that own them. The configuration is what lives in the version-control repository. The second is state: an internal file (terraform.tfstate for the local backend, an equivalent object in a remote backend for team use) that maps each declared resource address (aws_instance.web) to the real-world object the engine created (an EC2 instance ID, a current set of attributes, and the provider's serialised resource schema). The third is the execution plan: the diff that Terraform computes by refreshing state from real infrastructure, comparing that refreshed state to the configuration, and producing an ordered list of create, update, replace, or destroy actions. The three artifacts together give IaC its loop: source of truth in configuration, ground truth in state, intended change in the plan, executed change in apply, and an updated state at the end.
The vocabulary above also draws a distinction that the exam sometimes uses to construct distractors. Provisioning is bringing infrastructure into existence and changing its shape over time: virtual machines, networks, IAM roles, S3 buckets, RDS clusters, Kubernetes clusters as black-box objects from a cloud's perspective. Configuration management is mutating the inside of an existing machine: installing packages, dropping config files, restarting services. Terraform is a provisioning tool. It can call configuration-management tools (it has remote-exec, local-exec, and file provisioners as a last-resort escape hatch, and it integrates with Ansible, Chef, and similar through user-data or post-creation steps) but it is not itself a configuration management tool, and the exam regularly flags answers that conflate the two.
💡 Exam Trap: When a question describes a workflow that installs packages on a running VM, sets
nginx.conf, and starts thenginxservice, the correct categorisation is configuration management, not Infrastructure as Code. Terraform may create the VM, but reshaping its filesystem is a configuration-management responsibility that Terraform delegates to other tools or touser_data/cloud-init.
💡 Exam Trap: "Declarative" and "imperative" are not synonyms for "Terraform" and "scripts". The Terraform Cloud Development Kit (CDKTF), AWS CDK, and Pulumi are imperative-looking general-purpose languages that compile down to Terraform or CloudFormation; the engines underneath are still declarative. The declarative property lives in the engine, not in the syntax the human types.
⚠️ Anti-Pattern: Treating an imperative bash script that wraps cloud CLI calls as "Infrastructure as Code". The script is code, and it provisions infrastructure, but without a state model and a diff engine it gives you neither idempotency nor a reviewable plan. The exam treats such scripts as imperative provisioning, not IaC.
The declarative model in HCL
Below is the minimal Terraform configuration that creates a single S3 bucket. It is the smallest possible illustration of the IaC shape: a declared desired state, a provider that knows how to reach the API, and no procedural verbs.
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
}
provider "aws" {
region = "eu-west-1"
}
resource "aws_s3_bucket" "logs" {
bucket = "acme-platform-logs"
tags = {
Environment = "prod"
Owner = "platform-team"
}
}
The resource block does not say "if the bucket does not exist, create it; otherwise update its tags". It says "a bucket named acme-platform-logs with these tags should exist". The engine compares that statement to state, derives the difference, and produces an execution plan. If the bucket already exists with the exact same tags, the plan is empty and the run is a no-op. If a tag is missing, the plan is an in-place update. If the bucket name in the configuration changes to a value that does not match the real bucket, the plan is a destroy plus a create, because S3 bucket names are immutable.
What an IaC run actually looks like
The diagram below shows the four-stage loop every Terraform run follows. It is the same loop whether the target is one cloud or fifteen, and it is the loop the exam uses to test "what does Terraform actually do" questions.
Flowchart: The IaC Reconciliation Loop
The same loop also explains why IaC tools produce safer changes than scripts. The diff (the plan) is a fully rendered document of what is about to happen. A reviewer can read every change in a pull request the same way they read code, and the apply step is the only one that touches infrastructure.
Idempotency, drift, and the role of state
Idempotency in the IaC sense is not just "running twice does not do anything weird". It is the stronger property that running the same configuration N times against the same real infrastructure produces exactly N-1 no-op runs after the first apply. The engine achieves this by remembering, in state, what it created last time and refreshing that memory against the live API at the start of every plan. Drift is what the engine calls a difference between state and reality (a tag changed in the console, an instance terminated by an autoscaler, a security-group rule added by a different team) and is detected during the refresh phase that precedes every plan. Drift detection is one of the headline outcomes of having state: an imperative script cannot detect drift because it has no record of what it last created.
📊 Limit: Terraform's drift detection runs against the providers' API surfaces only. It does not see changes that exist only inside the resource (the contents of an S3 object, the rows in a database, the files on a VM's disk) unless the provider exposes those as schema attributes. The exam often pairs this with a question about whether Terraform "detects all changes": the honest answer is "all changes that show up in the provider's read API".
🎯 Scenario: A platform team has been provisioning AWS networks by running a bash script that calls
aws ec2 create-vpc, thenaws ec2 create-subnetsix times. The script has grown to 800 lines and the team is asked whether it counts as IaC. The right framing for the exam: it is code that provisions infrastructure, but without a state model, an idempotency guarantee, or a diff phase, it is imperative scripting, not IaC. Rewriting the same intent as a Terraform configuration brings the missing properties.
💡 Exam Trap: Questions sometimes ask whether Terraform "configures" servers. The accurate answer is "Terraform provisions the server resource; for in-OS configuration it delegates to user-data scripts, cloud-init, or external configuration-management tools." A distractor that says "Terraform replaces Ansible and Puppet" is wrong on the exam even though some teams use it that way in practice.
⚠️ Anti-Pattern: Encoding the same resource twice (once with a
resourceblock and once with a CLI side-call inside alocal-execprovisioner) and expecting Terraform to manage both. The provisioner side-call is invisible to the diff engine, so the next plan will not see the resource it created, and drift will accumulate silently until something breaks.
Decision Anchor
Choose IaC (Terraform-style) when the same infrastructure must be reproduced across environments, audited via pull requests, or rolled back to a known good shape; when more than one engineer needs to make changes safely; or when a regulatory regime requires a written record of every modification. Choose configuration management (Ansible-style) when the unit of change is the inside of an already-existing machine: package versions, service settings, file contents, OS-level users. Choose neither (manual console) only for genuinely one-off exploration, on resources that will be destroyed within the session and never reproduced.
The exam frequently encodes this anchor as a multiple-choice prompt: "which tool category fits scenario X best?" The correct mapping is provisioning -> Terraform, in-machine configuration -> Ansible/Chef/Puppet, exploratory -> console.
IaC versus the deployment pipeline
A subtle distinction the exam likes is the line between infrastructure and application deployment. IaC tools own the infrastructure (the runtime that the application lives on). Continuous-delivery tools (Argo CD, Spinnaker, GitHub Actions, GitLab CI, Jenkins) own the artefact deployment (which build of the application is running). Terraform can deploy applications in some cases (a Kubernetes manifest applied through the kubernetes provider, a Lambda function created from a zip), but it is at its sharpest at the infrastructure layer. The reason is the lifecycle mismatch: applications change many times a day; infrastructure changes weekly or monthly. A plan/apply loop is overhead per release; a CD tool's incremental rollout is leaner for application traffic shifts. Treating Terraform as "the deployment tool for everything" is the most common mis-framing the exam tests, and the correct mental model is "Terraform owns the platform; the CD tool owns what runs on the platform".
This positioning also explains a question pattern: "Which of the following tasks is most appropriate for Terraform?" The right answer is always the one that names a long-lived infrastructure object (VPC, security group, IAM role, RDS database, DNS record, Kubernetes cluster). The wrong answer is the one that names a per-release artefact (the latest binary, the running pod count for a deployment that scales every minute, a feature flag value). Even when Terraform can technically do both, the exam favours the lifecycle match.
