Infrastructure as Code Explained: Terraform to Pulumi

Infrastructure as Code Explained: Terraform to Pulumi

Infrastructure as code (IaC) is the practice of defining cloud resources, networks, databases, and Kubernetes clusters in declarative or programmatic files that live in version control, are reviewed like application code, and are deployed through automated pipelines. It replaces clicking through cloud consoles (ClickOps) with reproducible, auditable, peer-reviewed infrastructure changes. The dominant tools in 2026 are Terraform, OpenTofu, Pulumi, AWS CloudFormation, and Crossplane.

Key takeaways

  • IaC means your production environment can be rebuilt from a Git repository. If it cannot, you have configuration files, not infrastructure as code.
  • Terraform and its open-source fork OpenTofu lead by adoption; Pulumi wins on developer experience for teams that prefer real programming languages.
  • Security misconfiguration is the leading cloud breach cause. IaC scanners (tfsec, Checkov) catch most of these before merge.
  • State management is the trickiest operational concern. Use remote backends with locking; never check state files into Git.
  • IaC is a force multiplier for DevOps, not a replacement for it. The pipeline that deploys infrastructure should look like the one that deploys application code (see our Kubernetes vs Docker guide if you are still choosing the orchestration target).
Infrastructure as code workflow: write, plan, apply, state, cloud
How a typical infrastructure-as-code workflow runs.

What is infrastructure as code?

Infrastructure as code is the practice of describing infrastructure in machine-readable files that are treated like application source code: versioned in Git, reviewed in pull requests, tested in CI, and deployed by an automated pipeline. The infrastructure can include virtual machines, load balancers, S3 buckets, IAM roles, DNS records, Kubernetes manifests, and SaaS configurations.

The distinction that matters: a script that creates resources imperatively ("do X, then Y") is not IaC. IaC is declarative. You describe the desired end state and the tool figures out which steps are needed to get there from the current state. That is what makes IaC idempotent: running it twice produces the same result.

Why teams adopt IaC

  • Reproducibility. Spin up staging, dev, and production from the same definitions. Disaster recovery becomes a script, not a four-day operation.
  • Auditability. Every change is a pull request with a reviewer. Compliance teams stop asking who changed the firewall rule because Git already answers.
  • Velocity at scale. Tens of services, hundreds of environments. ClickOps does not scale; code does.
  • Drift detection. Tools detect when someone changes infra outside the pipeline and force it back or alert.
  • Security. Static analysis catches over-permissive IAM, open S3 buckets, and misconfigured security groups before merge.

IaC categories and tools

Declarative, cloud-agnostic

Terraform and its community fork OpenTofu are the de facto standard. You write HashiCorp Configuration Language (HCL), and providers (AWS, Azure, GCP, Cloudflare, Datadog, dozens more) translate to API calls. Strong ecosystem of modules in the public registry. Terraform Cloud and Spacelift add team workflows on top.

Pulumi covers the same ground but lets you use TypeScript, Python, Go, or C# instead of HCL. Mature type checking and IDE support are the main draws. Same provider ecosystem (Pulumi wraps the Terraform providers).

Declarative, cloud-specific

AWS CloudFormation is AWS-native YAML/JSON. Tight integration with AWS services and Stacks/StackSets for multi-account, multi-region rollout. Verbose syntax pushed many teams to the AWS CDK, which generates CloudFormation from TypeScript or Python.

Azure Bicep is Microsoft's modern replacement for ARM templates. Cleaner syntax, native Azure tooling.

Google Cloud Config Connector exposes GCP resources as Kubernetes CRDs.

Kubernetes-native IaC

Crossplane brings IaC into the Kubernetes control plane: cloud resources become Kubernetes objects you reconcile with the same operators you use for workloads. Powerful for platform teams; steeper learning curve.

Configuration management (related but distinct)

Ansible, Chef, and Puppet configure the inside of machines (packages, files, services). They overlap with IaC for VM-heavy estates but are less relevant when the workload is containers on Kubernetes. Most teams use Terraform for cloud resources and Ansible only when they still manage VMs.

How an IaC workflow looks

  1. Developer edits a Terraform file in a feature branch.
  2. They push; CI runs terraform fmt, terraform validate, terraform plan and posts the plan as a PR comment.
  3. An IaC security scanner (tfsec, Checkov, Snyk IaC) runs against the plan.
  4. A reviewer approves; the change merges to main.
  5. The pipeline runs terraform apply against production, with remote state and locking.
  6. A drift-detection job runs nightly and alerts if state diverges from reality.

The pipeline that does this looks identical to the application-deploy pipeline described in our DevOps best practices guide.

State management: the operational core

Terraform tracks the mapping between your code and the real resources in a state file. Bad state hygiene is the most common production incident. Rules that matter:

  • Use a remote backend. S3 + DynamoDB lock, GCS, Azure Blob, Terraform Cloud, or Spacelift. Never check state into Git.
  • One state file per blast radius. Split state by environment (prod/staging) and by service. Tiny state files keep terraform plan fast and contain damage.
  • Lock the backend. Concurrent applies on the same state corrupt it.
  • Encrypt state at rest. State files contain secrets (DB passwords, generated tokens). Use SSE-KMS or equivalent.
  • Audit access to state. State access is the same as production access.

IaC security best practices

  • Scan in CI. tfsec, Checkov, KICS, Snyk IaC all integrate with GitHub Actions, GitLab CI, and Buildkite. Block on critical findings.
  • Use least-privilege IAM. Modules should request only the AWS actions they use, not *.
  • Never embed secrets. Reference Vault, AWS Secrets Manager, or Doppler from inside the resource definition.
  • Lock module sources. Pin modules to a Git SHA or registry version, not main.
  • Sign your builds. Container images built by the pipeline should be signed (cosign); IaC modules should reference signed artifacts.

AWS, Azure, and multi-cloud IaC

AWS infrastructure as code

For AWS-only shops, the choice is between Terraform/OpenTofu, CloudFormation, and CDK. Terraform wins for teams that may add a non-AWS provider (Datadog, Cloudflare, GitHub). CDK wins for teams that strongly prefer TypeScript and accept CloudFormation's drift/rollback semantics. CloudFormation raw YAML is rarely the right choice in 2026 unless you are deeply invested in AWS Service Catalog.

Azure infrastructure as code

Bicep is the recommended path for new Azure work. Terraform is also first-class on Azure and is the right call for teams that already standardized on it.

Multi-cloud

True multi-cloud (the same workload running on AWS and GCP) is rare. What is common is using different clouds for different workloads (GCP for data, AWS for app, Azure for AD). Terraform handles this cleanly with one provider config per cloud. Pulumi works equally well.

Infrastructure as code examples

A minimal Terraform example that creates an S3 bucket with encryption and lifecycle policy:

resource "aws_s3_bucket" "reports" {
  bucket = "valletta-reports-prod"
}

resource "aws_s3_bucket_server_side_encryption_configuration" "reports" {
  bucket = aws_s3_bucket.reports.id
  rule {
    apply_server_side_encryption_by_default {
      sse_algorithm = "aws:kms"
    }
  }
}

resource "aws_s3_bucket_lifecycle_configuration" "reports" {
  bucket = aws_s3_bucket.reports.id
  rule {
    id     = "expire-90d"
    status = "Enabled"
    expiration { days = 90 }
  }
}

This is three resources, fully reproducible, scannable for security issues, and ready to be reviewed in a pull request. The same model scales to thousands of resources across dozens of services.

Common IaC pitfalls

  • Manual changes. A teammate clicks in the console, drift accumulates, the next plan wants to revert their fix. Solve with drift detection and a strict no-manual-change policy.
  • One monolithic state file. The whole company's infra in one workspace. Plans take 20 minutes; one mistake locks everything. Split states.
  • Module sprawl. Forty bespoke modules nobody uses. Treat modules like a product: small, well-documented, semver-versioned.
  • Provider version drift. Each developer runs a different Terraform version. Pin versions in code and in CI.
  • Long-running resources in IaC. Databases, persistent volumes. Plan for blue-green and use lifecycle.prevent_destroy on stateful resources.

Infrastructure as code in DevOps

IaC is one of the load-bearing practices in DevOps maturity. The other foundational practices (CI/CD, observability, SLOs) all assume the infrastructure they run on is itself reproducible. See our DevOps overview for how the pieces fit together.

IaC platforms in 2026

The trend in 2026 is from "a folder of Terraform files" to "an internal developer platform":

  • Spacelift, Env0, Terraform Cloud: opinionated workflows, policy as code, drift detection out of the box.
  • Backstage + Crossplane: the platform-engineering pattern. Developers self-serve through a portal; the platform abstracts Terraform/Crossplane underneath.
  • Atlantis: lightweight PR-comment automation if you want to stay close to raw Terraform.

If you want to discuss what IaC tooling and team model fits your stage, our platform engineering services team can help. Schedule Free Consultation and we will scope it.

A minimal Terraform example creating an S3 bucket with versioning
A minimal Terraform example: declarative, reviewable, repeatable.

Frequently asked questions

What is the difference between IaC and configuration management?

IaC provisions and manages infrastructure resources (VMs, networks, cloud services). Configuration management (Ansible, Chef, Puppet) configures the inside of those resources (installed packages, files, services). Most modern container-based stacks lean heavily on IaC and use minimal configuration management.

What is the difference between Terraform and Pulumi?

Both manage the same cloud resources through similar provider models. Terraform uses HCL, a domain-specific language. Pulumi uses general-purpose languages (TypeScript, Python, Go, C#). Teams that want strong typing and IDE support prefer Pulumi; teams that want a smaller surface area and a huge community prefer Terraform.

Is OpenTofu a drop-in replacement for Terraform?

OpenTofu is the Linux Foundation fork of Terraform 1.5, created after HashiCorp changed the Terraform license to BUSL in 2023. For most teams it is a drop-in replacement. Diverging features may matter long-term; evaluate before committing.

Should IaC and application code share a repository?

Two common patterns: one repo per service that includes its own IaC, or a separate platform repo that owns shared infrastructure. Service-local IaC is good for ownership; shared platform IaC is good for consistency. Most mature teams do both.

How do you handle secrets in IaC?

Never commit them. The IaC tool reads secrets at apply time from a vault (HashiCorp Vault, AWS Secrets Manager, Doppler, 1Password Secrets Automation) or from CI environment variables sourced from the same vault. Generated secrets (DB passwords) are written back to the vault, not the state file (where possible).

Valletta.Software - Top-Rated Agency on 50Pros

Your way to excellence starts here

Start a smooth experience with Valletta's staff augmentation