What Infrastructure as Code Is
Infrastructure as Code is the practice of declaring your projects, networks, service accounts, and resources in text files that you version, review, and apply — instead of clicking through the Cloud Console or firing off a pile of gcloud commands. The files are the source of truth; a tool reads them and makes Google Cloud match. Your infrastructure stops being a thing you remember building and becomes a thing you can read, diff, and rebuild.
The problem it solves is that hand-built infrastructure is unreproducible. Nobody can say exactly how a project was assembled, staging quietly diverges from production, and rebuilding after an incident becomes archaeology against a half-remembered runbook. Write the same infrastructure as code and those questions have answers: the code is the record, the git history is the changelog, and a second identical project is one apply away.
The Cost of Console-Building
Building infrastructure by hand — clicking through the Console, or running gcloud commands one at a time — feels fast for the first resource and compounds into debt by the hundredth. Each manual change escapes the codebase: an audit log may note that it happened, but nothing captures the intent behind it, the desired end state, or how to reproduce it. Two engineers solving the same problem build it two different ways. The knowledge of how a project fits together lives in people's heads and walks out the door when they do.
The sharpest version of this pain is drift between environments. You assemble a production project by clicking, then build staging the same way a month later from memory. The two are subtly different, so a change that works in staging breaks in production, and the difference is undocumented because there was never a document. The Console gives you no diff, no review, and no undo beyond your own recollection.
Declarative, Not Imperative
There are two ways to tell a computer to build infrastructure. The imperative way is a sequence of steps: create this bucket, then this dataset, then wire them together — and you own every step, including checking whether each thing already exists. The declarative way is a description of the end state: here is the bucket, the dataset, and the wiring I want to exist. You describe the destination; the tool computes the route.
That difference is not stylistic. A gcloud script that creates a bucket fails the second time you run it, because the bucket already exists — so you end up wrapping every command in "does this exist yet?" logic. A declarative tool compares your description to what is actually there and does only what is needed to close the gap. The snippet below is the entire instruction to ensure a bucket exists in a project; there is no "create" verb and no "already exists" handling, because you declared a state, not an action.
resource "google_storage_bucket" "raw" { name = "hatch-events-raw" # globally-unique name — declared, not "created" location = "US" }
Run the equivalent imperative command twice and the second run errors because the bucket is already there; you would have to catch that yourself. Apply the declarative resource twice and the second run is a no-op, because the desired state already matches reality. That property has a name, and it is the next idea.
Idempotency and Desired State
Idempotency means applying the same configuration twice yields the same result — the first apply creates what is missing, and the second changes nothing because nothing needs changing. The tool holds a model of desired state and reconciles reality toward it, rather than blindly re-running actions. This is why a declarative apply is safe to run repeatedly and a hand-rolled script is not.
Desired state is also what makes change reviewable. Because the configuration describes the end result, a change to it is a diff — add three lines to add a subnet, change one to resize an instance. That diff goes through the same pull-request review as application code, which means infrastructure changes get the same scrutiny, history, and rollback that code has had for decades.
Provisioning, Configuration, and Orchestration
"Infrastructure as Code" is an umbrella over three different layers, and confusing them is the most common newcomer mistake. Provisioning creates the resources — the project, the network, the Compute Engine instance, the database. Configuration management sets up what runs inside them — installs packages, edits config files, manages services. Orchestration schedules long-running workloads across machines, as Google Kubernetes Engine does for containers.
Terraform is a provisioning tool. It is precise at creating and tracking the existence and shape of cloud resources, and it deliberately does not configure the operating system inside a VM — that is the configuration layer's job, handled by a startup script or a tool like Ansible. Keeping these layers distinct, rather than forcing one tool to do all three, is what keeps an infrastructure pipeline clean. The next topic places Terraform precisely; the one after maps the whole GCP landscape.
Declarative — you commit the desired end state (Terraform, Config Connector) and the tool computes the diff and converges on it. Re-running is safe, and the configuration doubles as documentation of what exists. Prefer it for anything that outlives a one-off task.
Imperative — you script each step (a shell loop over gcloud). Fast for a single throwaway action, but you must handle "does this already exist?" yourself, and re-running is dangerous. It rots as a way to manage standing infrastructure.
- Building a production project in the Console "just this once" and intending to codify it later — the code never gets written and the project becomes an undocumented snowflake nobody dares touch.
- Treating IaC as documentation that can lag reality — once you change a resource by hand, the code lies, and the next apply may revert your manual fix.
- Expecting a provisioning tool to install packages and configure the OS on a VM — provisioning a Compute Engine instance is not configuring it; that is a separate layer's job.
- Scripting raw
gcloudcalls to "do IaC" without any state tracking — you rebuild idempotency badly instead of using a tool that already solved it. - Mixing provisioning, configuration, and orchestration into one tool because it seems simpler, then fighting the tool everywhere it was never meant to go.
- Codify infrastructure from the first resource, before anything reaches production, so there is never a manual baseline to reverse-engineer.
- Make every change through code and never through the Console on managed resources, so declared state and real state stay in sync.
- Put all infrastructure code under version control and review it with the same pull-request process as application code.
- Separate the provisioning layer (Terraform) from the configuration layer (startup scripts, Ansible) deliberately rather than blurring them.
- Treat a declarative configuration as the source of truth, and reach for the Console only to read, never to change.
Knowledge Check
Why is a declarative configuration safe to apply repeatedly when an imperative gcloud script is not?
- It compares desired state to reality and does only what is needed, so a second apply is a no-op
- It runs much faster because all of the steps are compiled to a plan ahead of time
- It skips over any resource that already exists without ever checking its current configuration first
- It only ever creates brand-new resources and never modifies existing ones
A team builds a production project by hand in the Console, then builds staging the same way a month later. What is the predictable result?
- The environments drift apart in undocumented ways, so a change that works in one breaks in the other
- The two projects stay perfectly identical because the GCP Console enforces cross-project consistency
- Staging automatically inherits the production project's full configuration on creation
- Nothing goes wrong, as long as the very same engineer builds both projects
Which task is the job of the configuration-management layer, not the provisioning layer Terraform occupies?
- Installing packages and editing config files inside a running VM
- Creating the Compute Engine instance along with its VPC network and subnet
- Allocating a managed Cloud SQL database instance for the app
- Provisioning a regional Cloud Storage bucket for assets
What does "idempotent" mean for a Terraform apply?
- Applying the same configuration again produces no change once reality already matches it
- Every single apply recreates all of the resources from scratch just to guarantee consistency
- The configuration can only ever be applied once and then becomes permanently read-only
- Each apply has to be run exactly twice in a row before it takes effect
You got correct