Terraform provisions infrastructure from declarative configuration: you write what cloud resources should exist - a BigQuery dataset, an S3 bucket, a Kubernetes cluster, a network - and Terraform figures out the API calls to create, change, or destroy them to match. It keeps a state file recording what it has built, so on the next run it diffs your config against reality and shows a plan of exactly what will change before touching anything. That plan-then-apply loop, plus one configuration language across AWS, GCP, Azure, and hundreds of other providers, is why Terraform became the default way to stand up data-platform infrastructure as code.
Resources, providers, and HCL
Configuration is written in HCL (HashiCorp Configuration Language). A provider is the plugin that talks to one platform's API; a resource is one managed object. References between resources (google_bigquery_dataset.warehouse.dataset_id) create an implicit dependency graph that Terraform orders automatically.
terraform {
required_providers {
google = { source = "hashicorp/google", version = "~> 5.0" }
}
backend "gcs" {
bucket = "acme-tfstate"
prefix = "warehouse"
}
}
provider "google" {
project = var.project_id
region = "us-central1"
}
resource "google_bigquery_dataset" "warehouse" {
dataset_id = "analytics_warehouse"
location = "US"
default_table_expiration_ms = 7776000000 # 90 days
labels = { team = "data-eng", env = var.environment }
}
resource "google_bigquery_table" "events" {
dataset_id = google_bigquery_dataset.warehouse.dataset_id
table_id = "raw_events"
time_partitioning { type = "DAY", field = "event_date" }
}
The backend "gcs" block stores state remotely so a team shares one source of truth and the state file isn't trapped on one laptop. The table references the dataset's dataset_id, so Terraform creates the dataset first.
Plan, apply, and state
The core loop is terraform plan then terraform apply. Plan compares the desired config, the recorded state, and the live infrastructure, then prints an execution plan: additions, in-place updates, and destroy-and-recreate actions. Nothing changes until you apply. Remote state should be locked (the GCS and S3 backends do this) so two engineers can't apply at once and corrupt it.
# Preview changes and save the exact plan
terraform plan -var="environment=prod" -out=tfplan
# Apply only what that plan described — no surprises
terraform apply tfplan
# Inspect or surgically fix state
terraform state list
terraform state rm google_bigquery_table.events # stop managing without deleting
# Import an existing resource into state
terraform import google_bigquery_dataset.warehouse projects/acme/datasets/analytics_warehouse
Saving the plan with -out and applying that exact file guarantees the apply does only what you reviewed, even if the live infrastructure shifted in between.
Modules and where Terraform fits
A module is a reusable package of resources with input variables and outputs - a "data-lake bucket with logging, lifecycle rules, and encryption" defined once and instantiated per environment. Modules keep large configs from repeating themselves and let a platform team publish vetted building blocks. Terraform owns provisioning - the resources themselves - and hands off to a configuration manager like Ansible for software inside the machine, or to a managed-template service such as Google Cloud Deployment Manager when you want a single-cloud native option. See the infrastructure-as-code and deployment hub for the full landscape.
Frequently asked questions
What is the Terraform state file for? It maps your config to real resource IDs and records the last-known attributes, so Terraform can compute a minimal diff instead of querying and recreating everything. Lose or corrupt it and Terraform no longer knows what it owns. Keep it in a locked remote backend, never in Git, and treat it as sensitive - it can contain secrets.
plan vs apply? Plan computes and shows the changes without making them; apply executes them. In CI, run plan on every pull request for review, and apply only after merge. Save the plan with -out and apply that file so the apply does exactly what was reviewed.
How do I bring existing infrastructure under Terraform? Write the matching resource block, then terraform import it to bind that block to the live object in state. From then on Terraform manages it. Newer provider tooling can generate config for imported resources to save the hand-writing.
Is Terraform multi-cloud out of the box? The language and workflow are uniform across clouds, but resources are not portable - an AWS resource block has no GCP equivalent. "Multi-cloud" means managing several clouds with one tool and workflow, not writing config once and deploying it anywhere.