Infrastructure as Code in Multi-Cloud: Production Patterns for CTOs · Daniel Tinizaray

Infrastructure as Code in Multi-Cloud: Production Patterns for CTOs · Daniel Tinizaray

If you manage infrastructure across two or more clouds, you already know the hard problem isn't writing resources — it's keeping them consistent, secure, and auditable as teams grow.

This isn't a Terraform tutorial. It's a guide to production patterns for teams operating multi-cloud infrastructure (AWS + OCI as the reference case), distilled from real projects where a single mistake —corrupted state, exposed secrets, orphaned resources— costs real downtime or unexpected bills.

1. State Management: The Multi-Cloud Achilles Heel

With a single provider, state is just a remote file. In multi-cloud, it's your business map. If it gets corrupted or desynchronized, you lose visibility of what's deployed and where.

Centralized backend with per-environment isolation

Use a single backend (S3 with DynamoDB for locking, or an OCI bucket with OCI Object Storage locking) but with keys scoped by environment and provider:

EnvironmentProviderState Key
devAWSenv:/dev/aws/terraform.tfstate
devOCIenv:/dev/oci/terraform.tfstate
prodAWSenv:/prod/aws/terraform.tfstate
prodOCIenv:/prod/oci/terraform.tfstate

This lets you apply changes to one environment without locking others, and keeps clear separation between providers. Use OpenTofu over Terraform if you prefer MPL licensing and want to avoid unilateral license changes.

⚠️ Critical rule Never use local or shared state between developers. Every operator runs from CI/CD, not their machine.

2. Layered Modular Design

A single directory with 2000 resources is unsustainable. Divide by layer, not by provider:

infrastructure/
└── modules/
│   └── networking/
│   └── compute/
│   └── database/
│   └── storage/
│   └── monitoring/
└── environments/
│   └── dev/
│   │   └── aws/
│   │   └── oci/
│   └── staging/
│   │   └── aws/
│   │   └── oci/
│   └── prod/
│       └── aws/
│       └── oci/
└── terragrunt.hcl

Terragrunt as orchestrator

Terragrunt (or Terramate) solves the repetition problem across environments. Each environment inherits base configuration and only overrides what varies:

# environments/prod/terragrunt.hcl
terraform {
  source = "../../modules//networking"
}

inputs = {
  environment = "prod"
  vpc_cidr   = "10.0.0.0/16"
  region     = "us-east-1"
}

The same module is reused across dev, staging, and prod with different inputs. This drastically reduces code duplication.

3. Secrets Management: Out of State, Out of Code

Terraform state stores all resource values, including secrets if you pass them as plain variables. An exposed S3 bucket with state leaks database credentials, API keys, and tokens.

Production rules:

  • Never rely on sensitive = true as protection — it only hides from output, not from state.
  • Use vault (HashiCorp Vault, AWS Secrets Manager, OCI Vault) as backend for sensitive variables.
  • With Terragrunt: use dependency blocks to source outputs from other modules without hardcoding.
  • With OpenTofu: tofu init -backend-config=backend.hcl with backend config never in the repo.
# Example with AWS Secrets Manager
data "aws_secretsmanager_secret_version" "db" {
  secret_id = "prod/database/credentials"
}

locals {
  db_creds = jsondecode(data.aws_secretsmanager_secret_version.db.secret_string)
}

resource "aws_db_instance" "main" {
  username = local.db_creds.username
  password = local.db_creds.password
}

4. CI/CD: Pipeline as the Single Source of Truth

If someone can run terraform apply from their terminal, they will eventually break something. The CI/CD pipeline must be the only deployment path.

PhaseWhat happensTypical duration
Plantofu plan -out=tfplan + comment on PR30-60s
ReviewHuman reviews diff in PR. Approve = trigger applyvariable
ApplyMerge to main → GitHub Actions runs auto apply2-5 min
DriftDaily cron detects untracked manual changesscheduled

Multi-provider GitHub Actions

name: "IaC - Apply"
on:
  push:
    branches: [main]
    paths: ["environments/prod/**"]

jobs:
  terraform:
    strategy:
      matrix:
        provider: [aws, oci]
    steps:
      - uses: actions/checkout@v4
      - uses: hashicorp/setup-terraform@v3
      - run: tofu init -backend-config=backend-$}{{ matrix.provider }}.hcl
      - run: tofu apply -auto-approve tfplan

5. Cost Control from IaC

The cheapest time to prevent unnecessary spending is before the resource is created. Integrate cost policies directly into your IaC pipeline:

  • Sentinel / OPA policies: Block deploys that create instances without cost tags, or exceed size thresholds.
  • Infracost: Runs on every PR and estimates the cost of the proposed change. Teams see the financial impact before approving.
  • Mandatory tagging: The base module for every resource must enforce tags like Environment, CostCenter, Owner.
# OPA policy: block instances larger than t3.large in dev
deny[msg] {
  input.resource_changes[_].type == "aws_instance"
  instance_type := input.resource_changes[_].change.after.instance_type
  instance_type in ["t3.xlarge", "t3.2xlarge", "m5.xlarge"]
  msg := sprintf("Instance %v not allowed in dev", [instance_type])
}

6. Drift Detection and Remediation

In multi-cloud, someone will always touch something from the web console. Drift is inevitable; automated detection is not optional.

Recommendation: a daily pipeline that runs tofu plan on every environment and notifies if it detects untracked changes. If drift exceeds a threshold, a human reviews; if minor, auto-remediation with tofu apply restores desired state.

📊 Key metric A mature multi-cloud team maintains less than 5% weekly drift in production environments.

Summary: Multi-Cloud IaC Checklist

  • ☑ Remote state with locking and per-environment/provider keys
  • ☑ Modular design: reusable modules, environments as instances
  • ☑ Secrets outside state (Vault / Secrets Manager)
  • ☑ Single CI/CD pipeline for all deploys
  • ☑ Cost estimation on every PR (Infracost)
  • ☑ OPA/Sentinel policies for governance
  • ☑ Daily drift detection
  • ☑ OpenTofu > Terraform to avoid license lock-in
  • ☑ No local terraform apply — CI/CD only

Infrastructure as Code is the foundation of any multi-cloud operation that doesn't want to become a headache. Implementing these patterns from day one —or fixing them gradually— separates teams that control their infrastructure from those controlled by it.


Enjoyed this article?

If you're dealing with these challenges in your company, let's talk. No obligation. 30 minutes to understand your situation.

Book a free call →