Neural Mastery

Infrastructure as Code

Clicking through a cloud console to create a VPC, three subnets, an EKS cluster, and an S3 bucket works exactly once. Doing it again for staging, again for a disaster recovery region, and again in eight months when nobody remembers the exact settings used, doesn't. Infrastructure as Code (IaC) fixes this the same way version-controlled application code fixed "whose laptop has the real version": infrastructure is defined in files, reviewed like code, and applied by a tool — not clicked together by hand.

Terraform

The dominant IaC tool, cloud-agnostic (one tool, many providers):

Write config
terraform plan
terraform apply
State updated
Terraform diffs "what's declared" against its state file (what it last created) and shows exactly what would change -- before touching anything.
Provider
Resource
Variable
Output
Module
Workspace
The actual infrastructure being declared -- resource "aws_instance" "training_node" { ... } -- Terraform's core building block.
  • Providers: plugins that let Terraform talk to a specific platform (AWS, GCP, Azure, Kubernetes, even SaaS tools like Datadog) — declare a provider block once, then define resources against it.
  • Resources: the actual infrastructure being declared — resource "aws_instance" "training_node" { ... } — Terraform's core building block.
  • Variables: parameterize a configuration (instance type, region, environment name) so the same Terraform code deploys dev, staging, and prod with different inputs.
  • Outputs: values exposed after apply (a generated endpoint URL, a resource ID) — consumed by other Terraform configs or by humans/CI.
  • Modules: reusable, parameterized bundles of resources — package "a standard VPC" or "a standard ML training cluster" once, reuse it across projects instead of copy-pasting the underlying resources.
  • State: Terraform's record of what it last created — the mechanism that lets it compute a diff between "what's declared" and "what actually exists" and apply only the changes needed.

A real, complete main.tf -- provider, a variable, a GPU training instance resource, and an output -- followed by the actual workflow that turns it into running infrastructure:

terraform {
  required_providers {
    aws = { source = "hashicorp/aws", version = "~> 5.0" }
  }
}

provider "aws" {
  region = "us-east-1"
}

variable "instance_type" {
  description = "GPU instance size for the training node"
  type        = string
  default     = "g5.xlarge"
}

resource "aws_instance" "training_node" {
  ami           = "ami-0abcdef1234567890"
  instance_type = var.instance_type
  tags = {
    Name = "ml-training-node"
  }
}

output "training_node_ip" {
  value = aws_instance.training_node.public_ip
}
terraform init                                    # downloads the aws provider plugin, sets up the backend
terraform plan -out=tfplan                        # shows exactly what would change, before anything runs
terraform apply tfplan                            # creates the instance, writes its state to the state file

terraform plan -var="instance_type=g5.2xlarge"    # re-plan against a different variable value
terraform destroy                                 # tears everything this config created back down
vpcdeclared = state
eks_clusterdeclared: 1.29 ≠ state: 1.28
s3_bucketdeclared = state
State says eks_cluster is on 1.28, but the config now declares 1.29 -- "terraform plan" surfaces exactly this one line as a diff, leaving the two matching resources untouched.
  • Remote state: storing that state file in a shared, locked backend (S3 + DynamoDB for locking is the classic AWS pattern) instead of a local file — required the moment more than one person or CI job touches the same infrastructure, to avoid two concurrent applies corrupting state.
CI job A: terraform applyruns
CI job B: terraform apply (same time)waits for lock
The second apply blocks on the lock held by the first -- it waits, then proceeds safely once the first completes. State stays consistent.
  • Workspaces: Terraform's built-in mechanism for managing multiple instances of the same configuration (e.g. one workspace per environment) without duplicating the code.

Immutable Infrastructure vs. Configuration Management

Two different philosophies for keeping infrastructure in a known state:

  • Configuration management (Ansible, Chef, Puppet): describe the desired configuration of a long-lived server, and the tool mutates it into that state — install this package, set this config file, restart this service. The server itself persists and gets updated in place.
  • Immutable infrastructure: never mutate a running server — build a new image/container with the change baked in, and replace the old instance entirely. This is the model containers and most cloud-native infrastructure default to today, because it eliminates an entire class of "it drifted from what the config says it should be" bugs.
old instance
new instance (change baked in)
A new image/container is built with the change baked in, and replaces the old instance entirely -- the model containers and most cloud-native infra default to, eliminating "it drifted from what the config says" bugs.

Terraform itself is agnostic to this distinction (it provisions infrastructure either way) — Ansible is commonly used alongside Terraform, with Terraform provisioning the VM and Ansible configuring what runs on it, though the immutable-infrastructure trend has pushed more of that configuration into the container image itself instead.

Terraform
Provisions the VM itself -- the instance exists, in the right VPC/subnet, with the right size.
+
Ansible (or baked image)
Configures what runs on that VM -- packages, config files, services.
Terraform is agnostic to immutable vs. configuration-management -- it provisions infrastructure either way. The immutable-infrastructure trend has pushed more configuration into the container image itself, but Ansible-alongside-Terraform remains common.

Pulumi

An alternative to Terraform's own HCL language: define infrastructure in a general-purpose language (Python, TypeScript, Go) instead of a domain-specific config format — appealing when infrastructure logic needs real programming constructs (loops, conditionals, functions) that HCL makes awkward. Terraform remains the more common default; Pulumi is worth knowing as the answer to "what if I want to write infrastructure in Python."

resource "aws_instance" "node" { instance_type = var.size # no native loop over sizes }
Terraform: infrastructure in HCL, a domain-specific config format -- the more common default, cloud-agnostic across many providers.

Next: CI/CD & ML CI/CD — once infrastructure is code, it deploys through the same automated pipelines as everything else.

Last updated Sep 5, 2026Edit this pageReport an issue
← Previous
Kubernetes
Next →
CI/CD & ML CI/CD