s☁
silvio.cloud
Back to all articles
• 5 min read • Silvio Silva

Terraform in Production (Part 1): Why HCL Beats Cloud-Native IaC

Why mature engineering teams stick with Terraform and HCL despite CloudFormation, CDK, and Bicep. Busting the "single cloud" myth and orchestrating multi-provider infrastructure.

Terraform in Production (Part 1): Why HCL Beats Cloud-Native IaC

Terraform in Production (Part 1): Why HCL Beats Cloud-Native IaC

Let’s be honest with each other: almost everyone starts their Infrastructure as Code (IaC) journey with starry-eyed optimism.

You write a couple of .tf files, watch an S3 bucket and an EC2 instance spin up in three seconds from your terminal, and think: "This is it. Cloud infrastructure solved forever."

Fast-forward eighteen months.

Your repository has devolved into an impenetrable maze of 70-variable wrapper modules. Your root state file takes four minutes just to refresh. A teammate accidentally ran a local terraform apply against staging with outdated credentials, and everyone is terrified to touch the networking module because nobody remembers if changing that security group will drop production database traffic.

Welcome to the real world of Infrastructure as Code.

This article kicks off a dedicated, battle-tested series on Terraform in Production. We aren’t going to rehash generic "Hello World" tutorials. Instead, we are diving straight into architectural reality, production scars, and pragmatic patterns that actually survive enterprise scale:


1. The Cloud-Native IaC Dilemma

Whenever a new infrastructure team forms, the inevitable debate surfaces: "Why bother with Terraform (or OpenTofu) and HCL when AWS has CloudFormation and CDK, Azure has Bicep, and Google Cloud has native Resource Manager?"

On paper, cloud-native tools sound tempting:

  • They are backed directly by the cloud provider.
  • Day-zero support for brand-new cloud features.
  • Zero remote state backends to manage (AWS manages CloudFormation state behind the scenes).

So why does virtually every mature engineering organization gravitate back to Terraform?


2. The "Single Cloud" Fallacy

Here is the truth that cloud provider sales reps won't tell you: no modern production architecture lives solely inside a single cloud provider.

Even if 100% of your production compute runs on AWS, your real-world ecosystem almost certainly looks like this:

  • Cloudflare handles your Edge DNS, WAF rules, SSL certificates, and DDoS mitigation.
  • GitHub / GitLab hosts your code, pull request workflows, and repository branch protections.
  • Datadog / Grafana Cloud ingests your metrics, traces, and alert monitors.
  • PagerDuty / Opsgenie manages your incident escalation policies and on-call schedules.
  • Auth0 / Okta manages your user identity pools and OAuth clients.
  • HashiCorp Vault / 1Password stores your rotating database credentials and certificates.

Now, try using AWS CloudFormation or Azure Bicep to update a Cloudflare DNS record pointing to your newly provisioned Application Load Balancer. Try using CloudFormation to create a Datadog monitor when a DynamoDB table is deployed, or to inject the resulting IAM role ARN into a GitHub repository environment secret.

You can't. Or rather, you have to write clumsy custom Lambda-backed custom resources that fail silently during stack teardown.

Unified Multi-Provider Orchestration

With Terraform, providers for AWS, Cloudflare, GitHub, and Datadog share the exact same dependency graph and execution engine:

# The superpower of Terraform: unified multi-provider orchestration
# AWS + Cloudflare + GitHub wired in a single atomic graph

resource "aws_lb" "api" {
  name               = "production-api-alb"
  internal           = false
  load_balancer_type = "application"
  subnets            = var.public_subnet_ids
}

resource "cloudflare_record" "api" {
  zone_id = var.cloudflare_zone_id
  name    = "api"
  content = aws_lb.api.dns_name
  type    = "CNAME"
  proxied = true
}

resource "github_actions_secret" "api_endpoint" {
  repository      = "frontend-app"
  secret_name     = "VITE_API_URL"
  plaintext_value = "https://${cloudflare_record.api.hostname}"
}

Terraform’s provider ecosystem gives you a single unified dependency graph, a consistent lifecycle model, and an identical review workflow across 3,500+ technologies. You learn one declarative paradigm, and you can orchestrate your entire digital footprint from edge to database.


3. Declarative Predictability vs. Imperative CDK Magic

What about AWS CDK or Pulumi?

Writing TypeScript or Python feels intuitive to software engineers, but imperative IaC introduces an insidious layer of abstraction: code synthesis.

With CDK, your TypeScript code does not directly provision resources; it synthesizes a giant CloudFormation template. When abstractions leak or loops generate dynamic identifiers, you spend your Friday evening debugging synthesized JSON templates rather than reasoning about actual cloud resources.

Terraform’s declarative nature means what you see in HCL is a direct 1:1 mapping of your intended state. The state graph is deterministic, inspectable, and predictable:

# Clean, deterministic, human-readable
resource "aws_s3_bucket" "audit_logs" {
  bucket = "company-audit-logs-2026"

  lifecycle {
    prevent_destroy = true
  }
}

Before a single byte changes in production, terraform plan tells you the exact changes:

Plan: 1 to add, 0 to change, 0 to destroy.

4. Key Takeaways & What's Next

  1. Vendor Neutrality is About Providers, Not Portability: Nobody rewrites their AWS VPC to GCP overnight. The real value is having one toolchain for AWS, Cloudflare, GitHub, and Datadog.
  2. Declarative Beats Synthesized: Clear HCL beats opaque template generators when debugging production issues under pressure.
  3. Plan Before Apply is Non-Negotiable: Inspecting the graph diff before touching cloud APIs is the core of infrastructure safety.

In the next post, we will tackle pipeline isolation: how to run Terraform in GitHub Actions using OpenID Connect (OIDC) and private self-hosted runners inside your VPC, eliminating static cloud credentials completely.

👉 Read Part 2: Air-Gapped CI/CD with GitHub Actions & Private Runners →

SS
Silvio Silva

Cloud & Systems Engineer · silvio.cloud