Infrastructure as Code: Synthesizing Your Entire Stack in Minutes

by Tobias Wren DevOps 8 min read
Infrastructure as Code: Synthesizing Your Entire Stack in Minutes

Infrastructure as Code has been the industry consensus for nearly a decade. Yet most teams still treat it as a better way to click through a cloud console — a transliteration of manual processes into YAML. That is not IaC. That is scripted manual work.

Genuine IaC operates at a higher level of abstraction: you describe desired state, and the system synthesizes the operations required to achieve it. The gap between these two approaches is the gap between infrastructure that scales and infrastructure that breaks.

The Declarative Imperative

Imperative IaC says: “Run these commands.” Declarative IaC says: “This is what should exist.”

The difference is profound. Imperative scripts are brittle — they assume the current state of the world and fail unpredictably when reality diverges. Declarative systems are robust — they diff current state against desired state and generate the minimal set of changes needed.

# Declarative: describe what should exist
resource "vertex_edge_deployment" "api" {
  name    = "production-api"
  regions = ["global"]

  artifact {
    image   = "registry.vertexplatform.io/api:v3.14.159"
    replicas = {
      min = 10
      max = 10000
    }
  }

  routing {
    strategy = "anycast"
    health_check {
      path     = "/internal/health"
      interval = "5s"
      timeout  = "2s"
    }
  }
}

When you run terraform apply, the Vertex provider calculates the diff between what exists and what this HCL declares, then executes only the necessary operations. Scale up? Add nodes. Scale down? Drain connections gracefully before terminating. Rollback? Re-apply the previous .tf state.

Version Control as the Source of Truth

The discipline that separates mature IaC from immature IaC is not tooling — it is process. Specifically: is your infrastructure definition the authoritative source of truth, or is it documentation that sometimes reflects reality?

The two non-negotiables:

1. No manual changes. Ever. If you modify infrastructure through the console during an incident, you must codify that change as a commit within 24 hours. Drift is the enemy of reproducibility.

2. Every change through code review. Infrastructure changes that bypass pull request review are security incidents waiting to happen. The blast radius of an infrastructure change can be orders of magnitude larger than a code change.

# The correct workflow
git checkout -b infra/add-singapore-pop
# Edit .tf files
git commit -m "feat: add SIN01 point of presence"
git push origin infra/add-singapore-pop
# Open PR → review → merge → automated apply via CI

The CI pipeline runs terraform plan on every PR. The plan output becomes a required review artifact. Engineers review what will change before it changes — not after.

The Vertex Terraform Provider

Our Terraform provider exposes the full Vertex platform surface area as first-class resources. Beyond basic compute, you can declare the entire deployment topology including network policy, certificate management, and observability configuration.

resource "vertex_network_policy" "zero_trust" {
  name = "service-mesh-policy"

  mtls {
    enabled           = true
    certificate_authority = vertex_ca.primary.id
    rotation_interval = "24h"
  }

  ingress_rules {
    allow_from = ["service:api-gateway"]
    deny_from  = ["*"]
  }
}

resource "vertex_observability" "api_tracing" {
  deployment_id = vertex_edge_deployment.api.id

  traces {
    sampling_rate = 0.1   # 10% sampling in production
    exporter      = "otlp"
    endpoint      = var.otel_collector_endpoint
  }

  alerts {
    p99_latency_ms    = 50
    error_rate_pct    = 0.1
    notification_channel = "pagerduty"
  }
}

Modularization and Reusability

As your infrastructure grows, raw resource declarations become unwieldy. Modules are the answer — but only if designed with the right boundaries.

Good module boundaries follow service responsibility. Bad module boundaries follow cloud resource type. Your module structure should reflect your system architecture, not your cloud provider’s API.

modules/
  vertex-service/       # A complete deployable service
    main.tf
    variables.tf
    outputs.tf
  vertex-network-zone/  # A complete network zone
    main.tf
    variables.tf
    outputs.tf

A vertex-service module encapsulates the deployment, routing rules, network policies, and observability config for a single logical service. You instantiate it once per service. Configuration surfaces through variables. The internals are an implementation detail.

This is the endgame of IaC: a system where spinning up a new globally-distributed, zero-trust, fully-observed service is a three-line variable assignment.

module "payment_service" {
  source  = "./modules/vertex-service"
  name    = "payment-processor"
  image   = "registry.vertexplatform.io/payments:v2.1.0"
  regions = ["global"]
}

Infrastructure at the speed of thought. No consoles. No manual steps. Pure declarative intent.