DEV Community

Cover image for OpenTofu vs Terraform in 2026: The Open-Source Fork for Data Platform Teams
Gowtham Potureddi
Gowtham Potureddi

Posted on

OpenTofu vs Terraform in 2026: The Open-Source Fork for Data Platform Teams

opentofu vs terraform is the fork-in-the-road decision every data platform team faced after HashiCorp relicensed Terraform from MPL 2.0 to the Business Source License (BSL 1.1) in August 2023 — do you stay on HashiCorp's now-restricted stack, migrate to the Linux Foundation's MPL-licensed OpenTofu fork, or run both? Every DE team eventually picks IaC; knowing the license implications, provider compatibility, migration path, and when to reach for enterprise alternatives (Spacelift, Env0, Scalr) is what separates a senior platform decision-maker from a mid-level one cargo-culting whichever the last team used.

The tour walks the five pillars — (1) licensing + governance (BSL 1.1's non-compete restrictions vs MPL 2.0 free-and-clear; HashiCorp / IBM corporate vs Linux Foundation), (2) feature diff + provider compatibility + zero-code migration path (tofu init reads existing Terraform state; providers work on both), (3) data platform IaC patterns (Snowflake, BigQuery, AWS with workspaces, remote state, backend locking), (4) OpenTofu-only features that shipped in 1.7 / 1.8 / 1.9 (state encryption at rest, removed block, provider-defined functions, dynamic provider imports), and (5) the decision matrix + enterprise alternatives (Spacelift, Env0, Scalr) + Terragrunt as engine-agnostic wrapper. Every section ships a Solution-Tail interview answer — code, trace, output, why-this-works with __concept__ underlines.

PipeCode blog header for OpenTofu vs Terraform — bold white headline 'OpenTofu vs Terraform' with subtitle 'The BSL Fork · What Data Platform Teams Should Pick' and a stylised fork-in-the-road glyph with two paths leading to OpenTofu tofu-block seal and Terraform T seal on a dark gradient with pipecode.ai attribution.

Practice on SQL library →, SQL optimization drills →, and SQL indexing drills →.


On this page


1. Why the fork matters in 2026

The opentofu vs terraform mental model — license, governance, roadmap

The one-sentence invariant: HashiCorp relicensed Terraform from MPL 2.0 (true open source, OSI-approved) to BSL 1.1 (non-compete restricted) in August 2023, prompting the community to fork the last MPL version into OpenTofu (adopted by the Linux Foundation in September 2023); OpenTofu maintains 100% file / state / provider compatibility with Terraform while shipping community-driven features (state encryption at rest, removed block, provider-defined functions) that HashiCorp had rejected — for the data platform team, the choice is between a vendor-controlled tool with a license that could tighten further vs a foundation-governed tool with genuinely open governance.

The timeline.

  • August 2023 — HashiCorp relicensed Terraform 1.6 to BSL 1.1.
  • August 2023 — Community launched OpenTF fork (later OpenTofu).
  • September 2023 — Linux Foundation adopted OpenTofu.
  • January 2024 — OpenTofu 1.6.0 first stable release.
  • May 2024 — OpenTofu 1.7 shipped state encryption at rest.
  • October 2024 — OpenTofu 1.8 shipped removed block, dynamic provider imports.
  • April 2025 — OpenTofu 1.9 added provider-defined functions parity.
  • April 2024 — IBM acquired HashiCorp; governance concerns intensified.
  • 2026 — Two tools; ~50/50 split by adoption in new projects.

BSL 1.1 restrictions.

  • Non-competing production use free.
  • Competing production use requires HashiCorp license.
  • "Competing" — offering Terraform-as-a-service or a Terraform-compatible service.
  • Converts to MPL 2.0 after 4 years (each version has its own conversion timeline).

Practical impact.

  • End users deploying their own infra — free; unaffected.
  • Consulting shops — free; unaffected.
  • Vendors offering managed Terraform — restricted; must license or fork.
  • Community trust — vendor governance concerns drove fork.

What senior interviewers actually probe.

  • BSL vs MPL semantic difference. Non-compete vs true open source.
  • Compatibility. State, providers, HCL syntax all compatible.
  • Migration. tofu init reads existing Terraform state; zero code changes usually.
  • OpenTofu-only features. State encryption, removed block.
  • Governance. Linux Foundation vs HashiCorp / IBM.
  • HCP integration. Terraform Cloud is HashiCorp-only; use Spacelift/Env0/Scalr for OpenTofu.
  • Sentinel policies. HashiCorp Enterprise; OpenTofu uses OPA (Open Policy Agent).
  • Enterprise alternatives. Spacelift, Env0, Scalr support both.
  • Terragrunt. Wraps either binary; DRY multi-env.
  • Decision framework. New projects → OpenTofu by default unless HCP-integrated.

The fork's effect on ecosystem.

  • Providers ship for both (same Go binaries).
  • Community modules work on both.
  • Cloud platforms (Snowflake, AWS, GCP) don't care which client.
  • Some enterprise tools (Spacelift, Env0) supported both from day one.
  • HashiCorp Cloud / Terraform Cloud only supports Terraform.

Worked example — the migration decision matrix

Question. You're on Terraform 1.5 (last MPL version). Should you upgrade to Terraform 1.7+ (BSL) or switch to OpenTofu 1.7?

Code — analysis.

Case A: Small team, self-managed infra, no HashiCorp Cloud
  → OpenTofu. Simpler, foundation-governed, no license surprise.

Case B: Enterprise using Terraform Cloud + Sentinel
  → Stay on Terraform, unless you migrate to Spacelift/Env0 + OPA.

Case C: SaaS vendor building "Terraform-as-a-service"
  → OpenTofu (BSL prohibits Terraform competitive use).

Case D: Standard consulting shop
  → Either works; new projects prefer OpenTofu for future flexibility.

Case E: Team unsure
  → OpenTofu — foundation ensures future openness.
Enter fullscreen mode Exit fullscreen mode

Rule of thumb. New projects → OpenTofu unless HashiCorp Cloud dependency.

Worked example — the compatibility check

Code.

# Existing Terraform 1.5 project
$ ls
main.tf variables.tf outputs.tf terraform.tfstate

# Try OpenTofu
$ tofu init
# Reads terraform.tfstate transparently
$ tofu plan
# Same output as terraform plan

# Providers install identically
$ ls .terraform/providers/
registry.terraform.io/hashicorp/aws/5.83.0/
Enter fullscreen mode Exit fullscreen mode

Rule of thumb. Migration is s/terraform/tofu/ on the binary; state and providers unchanged.

Worked example — the enterprise landing zone question

Code (contextual).

Enterprise data platform:
- 20+ AWS accounts, 3 GCP projects, 2 Azure subs
- Snowflake, BigQuery, Databricks
- Terraform Cloud with Sentinel policies for governance

Migration to OpenTofu:
- Providers: all compat.
- State: compat.
- Terraform Cloud → Spacelift (drop-in-ish) or Env0.
- Sentinel → Open Policy Agent (OPA).
- Effort: ~2-4 weeks for policy conversion + tool switch.

If you're heavily on HashiCorp Cloud, migration cost is real.
If you're on self-hosted Terraform + S3 backend, migration is trivial.
Enter fullscreen mode Exit fullscreen mode

Rule of thumb. Migration cost = infrastructure-specific; assess before deciding.

Common beginner mistakes

  • Believing OpenTofu = fork = dead-end. Foundation governance = healthy.
  • Assuming BSL = no-op for end users. Fine for direct users; vendors need attention.
  • Not verifying providers work on OpenTofu (they do).
  • Ignoring OPA / policy engine implications when moving off Sentinel.
  • Skipping the incremental migration approach (dev first, then prod).

opentofu vs terraform interview question on new-project choice

A senior interviewer opens with: "You're starting a new data platform IaC repo. Pick OpenTofu or Terraform and justify."

Solution Using OpenTofu with rationale + fallback plan

Choice: OpenTofu.

Rationale:

  1. License clarity. MPL 2.0 = future-proof.
  2. Foundation governance. Community-driven roadmap.
  3. Full compatibility. All providers work.
  4. Feature parity. State encryption + removed block > Terraform equivalents.
  5. Enterprise support. Spacelift, Env0, Scalr all support both.

Fallback if HashiCorp Cloud needed:

  • Switch to Terraform.
  • Or use Spacelift for OpenTofu-hosted alternative to HCP.

Setup:

# Install OpenTofu
brew install opentofu

# Initialize project
tofu init

# Use S3 backend
cat > backend.tf <<EOF
terraform {
  backend "s3" {
    bucket         = "myco-tofu-state"
    key            = "data-platform.tfstate"
    region         = "us-west-2"
    dynamodb_table = "tofu-locks"
    encrypt        = true
  }
}
EOF
Enter fullscreen mode Exit fullscreen mode

Output: OpenTofu-powered data platform IaC.

Why this works — concept by concept:

  • MPL 2.0 license — no future non-compete surprise.
  • Linux Foundation governance — vendor-neutral roadmap.
  • Provider compatibility — all cloud + SaaS providers work.
  • State compatibility — can revert to Terraform if needed.
  • Enterprise tools support — Spacelift/Env0/Scalr replace HCP.

SQL
Topic — SQL
SQL practice library

Practice →

SQL Topic — optimization SQL optimization drills

Practice →


2. Licensing + governance

BSL 1.1 vs MPL 2.0 · HashiCorp / IBM vs Linux Foundation

The mental model in one line: the Business Source License (BSL 1.1) is a source-available license — code is readable and modifiable, non-competitive production use is free, but "competitive" production use (offering the tool as a service) requires a commercial license from HashiCorp; MPL 2.0 (Mozilla Public License) is OSI-approved open source with no such restriction; the practical difference for end users is nil, for cloud vendors is enormous, and for community trust is significant.

Visual diagram of BSL vs MPL — two side-by-side cards Terraform orange with HashiCorp seal BSL ribbon non-compete restrictions, OpenTofu green with Linux Foundation seal MPL 2.0 ribbon true open source; on a light PipeCode card.

Slot 1 — BSL 1.1 in detail.

  • Business Source License, initiated by MariaDB.
  • Source-available: you can read + modify + fork.
  • Non-compete restriction. Can't use to build competing product.
  • Change date. BSL versions convert to MPL 2.0 after 4 years.
  • Change license. MPL 2.0 (specifically for HashiCorp).

Slot 2 — MPL 2.0.

  • Mozilla Public License 2.0.
  • OSI-approved open source.
  • Weak copyleft — modifications to MPL files must be MPL.
  • No competitive restrictions.
  • Foundation-friendly.

Slot 3 — what "competing" means.

  • Offering Terraform-as-a-service (HCP Terraform is HashiCorp's).
  • Building a cloud service that markets itself as Terraform-compatible.
  • Selling Terraform CLI hosting.

What's NOT competing.

  • Using Terraform to deploy your own infra.
  • Consulting on Terraform.
  • Building tools that integrate with Terraform.
  • Running Terraform in CI.

Slot 4 — governance comparison.

Aspect Terraform OpenTofu
Owner HashiCorp (IBM as of 2024) Linux Foundation
Decision-making HashiCorp product team Community RFCs
Contributor guidelines HashiCorp CLA LF CLA
Roadmap Vendor priorities Community proposals
Release cadence 2-3 major/year Similar
Support HashiCorp Business Community + vendor (Spacelift, Env0)

Slot 5 — what changed for community.

  • HashiCorp employees still contribute to Terraform.
  • Community contributions to Terraform continue.
  • OpenTofu has separate maintainer team.
  • Some features land in one before the other.
  • Providers are neutral — usually one release supports both.

Slot 6 — the IBM acquisition (April 2024).

  • IBM acquired HashiCorp.
  • No immediate license changes.
  • Long-term direction: uncertain.
  • Reinforced case for OpenTofu = future-proof.

Slot 7 — commercial vendors.

  • Spacelift — CI/CD platform; supports both.
  • Env0 — similar.
  • Scalr — Terraform Enterprise alternative; supports both.
  • HCP Terraform — HashiCorp only.

Slot 8 — enterprise features comparison.

Feature Terraform Enterprise / HCP OpenTofu + Spacelift/Env0
State backend S3 / GCS + native S3 / GCS + native
Runs HCP Terraform Spacelift / Env0
Policy-as-code Sentinel OPA / Rego
RBAC Built-in Built-in
Cost estimation Yes Yes (Infracost integration)
Drift detection Yes Yes

Slot 9 — Sentinel vs OPA.

  • Sentinel — HashiCorp's policy language; only Terraform Enterprise.
  • OPA (Open Policy Agent) — CNCF project; Rego language; works with anything.
  • OPA is broader — used for K8s admission, API gateways, DB access.
  • Migration — Sentinel → OPA takes weeks for large policy sets.

Slot 10 — enterprise readiness.

  • OpenTofu 1.8+ has parity for most enterprise needs.
  • HCP Terraform has more polish for HashiCorp-Enterprise features.
  • Third-party enterprise tools bridge the gap.

Common beginner mistakes

  • Confusing source-available with open source.
  • Assuming BSL affects all users (only competitive vendors).
  • Skipping OPA when migrating off Sentinel.
  • Not considering enterprise features when picking tool.

Worked example — the enterprise migration checklist

Terraform Enterprise → OpenTofu + Spacelift migration:

1. Inventory: state files, workspaces, policies.
2. Set up Spacelift.
3. Migrate state (import existing).
4. Convert Sentinel policies to Rego (OPA).
5. Migrate CI pipelines (Spacelift API).
6. Verify: dry-run all plans.
7. Cutover: point CI at Spacelift.
8. Decommission: Terraform Enterprise after grace period.

Timeline: 2-6 weeks depending on scale.
Enter fullscreen mode Exit fullscreen mode

Rule of thumb. Migration cost = policy conversion + tool switch; state + provider are trivial.

Worked example — the vendor decision

You're a SaaS company selling "managed Terraform".
Option A: License from HashiCorp (BSL commercial).
Option B: Migrate to OpenTofu (MPL, no restriction).

Option B is the community-preferred path; most competing tools already migrated.
Enter fullscreen mode Exit fullscreen mode

Rule of thumb. Vendors offering "Terraform-as-a-service" moved to OpenTofu.

Worked example — dual-support in Spacelift

# Spacelift stack config
version: 1.9.0
runner_image: ghcr.io/spacelift-io/runner-terraform:1.9-tf  # Terraform
# OR
runner_image: ghcr.io/spacelift-io/runner-terraform:1.8-tofu  # OpenTofu
Enter fullscreen mode Exit fullscreen mode

Rule of thumb. Spacelift supports both; switch is a config change.

opentofu vs terraform interview question on Sentinel policy conversion

A senior interviewer asks: "You have 50 Sentinel policies enforcing tag standards, cost limits, network CIDRs. How do you convert to OPA?"

Solution Using OPA + Rego + Spacelift/Env0 integration

# Rego (OPA) policy
package terraform.tags

deny[msg] {
  resource := input.resource_changes[_]
  resource.type in ["aws_instance", "aws_s3_bucket", "aws_rds_cluster"]
  not resource.change.after.tags.Environment
  msg := sprintf("resource %s must have Environment tag", [resource.address])
}

deny[msg] {
  resource := input.resource_changes[_]
  resource.type in ["aws_instance", "aws_s3_bucket"]
  not resource.change.after.tags.Owner
  msg := sprintf("resource %s must have Owner tag", [resource.address])
}
Enter fullscreen mode Exit fullscreen mode

Migration steps:

  1. Export Sentinel policies.
  2. Convert to Rego (semi-mechanical).
  3. Load into Spacelift as policies.
  4. Run tests against known-good plans.
  5. Enable enforcement.

Why this works — concept by concept:

  • OPA/Rego — CNCF standard; works everywhere.
  • Spacelift enforcement — policies block bad plans.
  • Semi-mechanical conversion — same intent, different syntax.
  • Testing — verify no regressions before enforcement.
  • Effort — 50 policies ≈ 2-3 weeks.
SQL Topic — SQL SQL practice library

Practice →

SQL Topic — indexing SQL indexing drills

Practice →


3. Feature diff + migration

Provider compatibility + state backend + OpenTofu-only features + zero-code migration

The mental model in one line: OpenTofu maintains ~100% compatibility with Terraform's HCL syntax, state file format, and provider ecosystem — running tofu init in an existing Terraform directory reads the state, downloads providers, and shows the same plan as Terraform would; the OpenTofu-only features (state encryption at rest, removed block, dynamic provider imports, provider-defined functions) are additive — you get them if you use OpenTofu but don't lose anything if you're on Terraform.

Visual diagram of feature diff + migration — provider compat matrix with 8 providers green ticks, state backend card S3/GCS/remote strips, OpenTofu-only card with encryption + removed block + provider functions chips; on a light PipeCode card.

Slot 1 — HCL syntax compatibility.

100% compatible. Same resource, variable, output, module, provider blocks.

Slot 2 — state file compatibility.

  • Same JSON format.
  • tofu init reads existing terraform.tfstate.
  • tofu plan produces identical output.
  • Migration = 0 code changes typically.

Slot 3 — provider compatibility.

  • Same provider registry protocol.
  • Providers are Go binaries; work on both.
  • OpenTofu has its own registry mirror + accepts terraform registry providers.
  • Major providers (AWS, GCP, Azure, Snowflake, BigQuery, Kubernetes) explicitly test both.

Slot 4 — state backend compatibility.

Both support:

  • S3 + DynamoDB lock.
  • GCS.
  • Azure Blob.
  • HTTP.
  • Postgres.
  • Consul.
  • etcd.

Migration: change binary; state unchanged.

Slot 5 — OpenTofu-only feature: state encryption at rest.

# OpenTofu 1.7+
terraform {
  encryption {
    key_provider "aws_kms" "default" {
      kms_key_id = "arn:aws:kms:us-west-2:..."
      region     = "us-west-2"
    }
    method "aead" "default" {
      keys = key_provider.aws_kms.default
    }
    state {
      method = method.aead.default
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

State encrypted before writing to S3/GCS/etc.

Slot 6 — OpenTofu-only feature: removed block.

# OpenTofu 1.8+
removed {
  from = aws_instance.old_server

  lifecycle {
    destroy = true   # or false to only remove from state
  }
}
Enter fullscreen mode Exit fullscreen mode

Track resource removals in state; safer than terraform state rm.

Slot 7 — OpenTofu-only feature: dynamic provider imports.

# OpenTofu 1.8+
provider "aws" {
  alias  = "region"
  region = each.key
}

resource "aws_s3_bucket" "b" {
  for_each = toset(["us-east-1", "us-west-2"])
  provider = aws.region
}
Enter fullscreen mode Exit fullscreen mode

Slot 8 — OpenTofu-only feature: provider-defined functions.

Providers can define custom functions callable from HCL. OpenTofu 1.9 added parity with Terraform's version.

Slot 9 — migration path.

# Step 1: install OpenTofu alongside Terraform
brew install opentofu

# Step 2: verify compatibility
$ terraform version
Terraform v1.5.7
$ tofu version
OpenTofu v1.8.0

# Step 3: test in dev
cd dev
tofu init   # reads terraform state
tofu plan   # compare output vs terraform plan

# Step 4: verify no drift
diff <(terraform plan) <(tofu plan)

# Step 5: switch CI to tofu binary

# Step 6: switch prod after grace period
Enter fullscreen mode Exit fullscreen mode

Slot 10 — hybrid stacks (not recommended).

Some teams temporarily run both — Terraform for legacy modules, OpenTofu for new. Not recommended long-term — state fragmentation, tool switching in CI.

Common beginner mistakes

  • Assuming migration requires code changes.
  • Missing provider version pins after switching.
  • Not testing in dev first.
  • Not updating CI runner image.
  • Forgetting to switch documentation to tofu commands.

Worked example — migration in a real repo

Code / commands.

cd data-platform-iac/

# Verify current
terraform version
# Terraform v1.5.7

# Install OpenTofu
brew install opentofu

# Test compatibility
tofu init
# Initializing modules...
# Initializing provider plugins...
# ... same output as terraform init

tofu plan
# Same plan as terraform plan

# Switch CI
sed -i '' 's/terraform init/tofu init/g' .github/workflows/*.yml
sed -i '' 's/terraform plan/tofu plan/g' .github/workflows/*.yml
sed -i '' 's/terraform apply/tofu apply/g' .github/workflows/*.yml

# Update Docker
sed -i '' 's/hashicorp\/terraform/opentofu\/opentofu/g' Dockerfile
Enter fullscreen mode Exit fullscreen mode

Rule of thumb. Migration = binary swap; commands identical.

Worked example — enabling state encryption

# terraform.tf (OpenTofu-only)
terraform {
  required_version = ">= 1.7.0"

  backend "s3" {
    bucket = "myco-state"
    key    = "prod.tfstate"
    region = "us-west-2"
  }

  encryption {
    key_provider "aws_kms" "default" {
      kms_key_id = "arn:aws:kms:us-west-2:123456789012:key/abc-def"
      region     = "us-west-2"
    }

    method "aead" "default" {
      keys = key_provider.aws_kms.default
    }

    state {
      method   = method.aead.default
      enforced = true
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Rule of thumb. State encryption at rest = OpenTofu killer feature.

Worked example — safe resource removal

# Before: had this resource
resource "aws_instance" "legacy" { ... }

# Now removing safely (OpenTofu 1.8+)
removed {
  from = aws_instance.legacy
  lifecycle {
    destroy = true
  }
}
Enter fullscreen mode Exit fullscreen mode

Applies destroy on next tofu apply; state cleaned.

Rule of thumb. removed block > manual terraform state rm + destroy.

opentofu vs terraform interview question on incremental migration

A senior interviewer asks: "You have 50 Terraform workspaces spread across dev, staging, prod. How do you migrate to OpenTofu safely?"

Solution Using canary + workspace-by-workspace + verification

# Phase 1: dev workspace
tofu init   # test in dev
tofu plan   # verify identical
tofu apply  # apply small change

# Phase 2: staging
# Same steps, after 1-2 weeks of dev success

# Phase 3: prod
# Split into batches of 10 workspaces per week
# Verify each with diff before/after

# Rollback: revert to terraform binary; state unchanged
Enter fullscreen mode Exit fullscreen mode

Why this works — concept by concept:

  • Canary in dev first — catches surprises without prod impact.
  • Workspace-by-workspace — bounded blast radius.
  • Verificationdiff terraform plan vs tofu plan; identical output = safe.
  • Rollback — trivial; state compatible.
  • Duration — ~1-2 months for 50 workspaces.

SQL
Topic — SQL
SQL practice library

Practice →

SQL Topic — optimization SQL optimization drills

Practice →


4. Data platform IaC patterns

Snowflake + BigQuery + AWS providers + workspaces + remote state

The mental model in one line: a modern data platform IaC repo declares warehouse resources (Snowflake accounts, databases, warehouses, roles; BigQuery datasets, tables, jobs), storage (S3 buckets, GCS buckets), compute (EKS clusters, GKE clusters), and orchestration (Airflow, Argo, Spark Operator via K8s manifests) — organised by workspace-per-env with remote state in S3/GCS + DynamoDB locking, using modules for reuse across environments.

Visual diagram of data platform IaC patterns — Snowflake provider card with account/warehouse/database resources, workspace glyph splitting dev/staging/prod, remote state card S3 backend with locking DynamoDB glyph; on a light PipeCode card.

Slot 1 — Snowflake provider.

terraform {
  required_providers {
    snowflake = {
      source  = "Snowflake-Labs/snowflake"
      version = "~> 0.95"
    }
  }
}

provider "snowflake" {
  account  = var.sf_account
  username = var.sf_user
  role     = "TERRAFORM_ADMIN"
}

resource "snowflake_warehouse" "analytics" {
  name           = "ANALYTICS_WH"
  warehouse_size = "MEDIUM"
  auto_suspend   = 60
  auto_resume    = true
  min_cluster_count = 1
  max_cluster_count = 3
}

resource "snowflake_database" "raw" {
  name = "RAW"
  data_retention_time_in_days = 7
}
Enter fullscreen mode Exit fullscreen mode

Slot 2 — BigQuery.

resource "google_bigquery_dataset" "raw" {
  dataset_id  = "raw"
  location    = "US"
  description = "Raw event data"
  labels = {
    env = "prod"
  }
}

resource "google_bigquery_table" "events" {
  dataset_id = google_bigquery_dataset.raw.dataset_id
  table_id   = "events"
  time_partitioning {
    type  = "DAY"
    field = "event_date"
  }
  clustering = ["user_id"]

  schema = jsonencode([
    { name = "user_id", type = "STRING", mode = "REQUIRED" },
    { name = "event_date", type = "DATE", mode = "REQUIRED" },
    { name = "properties", type = "JSON" },
  ])
}
Enter fullscreen mode Exit fullscreen mode

Slot 3 — AWS.

resource "aws_s3_bucket" "data_lake" {
  bucket = "myco-datalake-${var.env}"
}

resource "aws_s3_bucket_lifecycle_configuration" "lifecycle" {
  bucket = aws_s3_bucket.data_lake.id
  rule {
    id     = "archive-old"
    status = "Enabled"
    transition {
      days          = 90
      storage_class = "GLACIER"
    }
  }
}

resource "aws_iam_role" "data_platform" {
  name = "data-platform-${var.env}"
  assume_role_policy = jsonencode({ ... })
}
Enter fullscreen mode Exit fullscreen mode

Slot 4 — workspaces per env.

tofu workspace new dev
tofu workspace new staging
tofu workspace new prod

tofu workspace select prod
tofu plan -var-file=envs/prod.tfvars
tofu apply -var-file=envs/prod.tfvars
Enter fullscreen mode Exit fullscreen mode

Slot 5 — remote state with locking.

terraform {
  backend "s3" {
    bucket         = "myco-tofu-state"
    key            = "data-platform.tfstate"
    region         = "us-west-2"
    dynamodb_table = "tofu-locks"
    encrypt        = true
  }
}
Enter fullscreen mode Exit fullscreen mode

DynamoDB provides concurrent-lock; only one apply at a time.

Slot 6 — modules for reuse.

modules/
├── snowflake-warehouse/
│   ├── main.tf
│   ├── variables.tf
│   └── outputs.tf
├── bigquery-dataset/
└── data-lake-bucket/

envs/
├── dev/
│   ├── main.tf   # uses modules
│   └── terraform.tfvars
├── staging/
└── prod/
Enter fullscreen mode Exit fullscreen mode

Slot 7 — module usage.

module "analytics_wh" {
  source = "../../modules/snowflake-warehouse"

  name          = "ANALYTICS"
  size          = var.warehouse_size
  auto_suspend  = 60
}
Enter fullscreen mode Exit fullscreen mode

Slot 8 — environment-specific vars.

# envs/prod/terraform.tfvars
warehouse_size = "LARGE"
env            = "prod"

# envs/dev/terraform.tfvars
warehouse_size = "X-SMALL"
env            = "dev"
Enter fullscreen mode Exit fullscreen mode

Slot 9 — secrets via env / Vault.

variable "sf_password" {
  type      = string
  sensitive = true
}

# Passed via TF_VAR_sf_password env var or Vault provider
Enter fullscreen mode Exit fullscreen mode

Never commit secrets to git.

Slot 10 — data provider = full-fledged.

Providers exist for: Snowflake, BigQuery, Redshift, Databricks, dbt Cloud, Fivetran, Airbyte, Great Expectations, Monte Carlo, Prefect Cloud, Grafana Cloud.

Full data-platform IaC is now feasible entirely.

Common beginner mistakes

  • No remote state — collaborators overwrite.
  • No state locking — concurrent applies corrupt.
  • No workspaces — copy-paste config per env.
  • Committing secrets — visible in git history.
  • No modules — copy-paste per env.

Worked example — full data platform stack

# main.tf
module "snowflake" {
  source = "./modules/snowflake"

  account      = var.sf_account
  environments = ["dev", "staging", "prod"]
}

module "bigquery" {
  source = "./modules/bigquery"

  project = var.gcp_project
  datasets = ["raw", "staging", "marts"]
}

module "data_lake" {
  source = "./modules/data-lake"

  bucket_prefix = "myco-datalake"
}

module "airflow" {
  source = "./modules/airflow"

  cluster_name = var.eks_cluster
  namespace    = "airflow"
}
Enter fullscreen mode Exit fullscreen mode

Rule of thumb. Modular data platform IaC scales as team grows.

Worked example — dbt Cloud provider

resource "dbtcloud_project" "main" {
  name        = "data-platform"
  description = "Main dbt project"
}

resource "dbtcloud_job" "daily" {
  project_id       = dbtcloud_project.main.id
  environment_id   = dbtcloud_environment.prod.id
  name             = "Daily Build"
  execute_steps    = ["dbt build"]
  triggers = {
    schedule = "0 4 * * *"
  }
}
Enter fullscreen mode Exit fullscreen mode

Rule of thumb. IaC for dbt Cloud = declarative job definition.

Worked example — Snowflake role hierarchy

resource "snowflake_role" "analyst" {
  name = "ANALYST_ROLE"
}

resource "snowflake_role" "analyst_admin" {
  name = "ANALYST_ADMIN_ROLE"
}

resource "snowflake_role_grants" "analyst_to_admin" {
  role_name = snowflake_role.analyst.name
  roles     = [snowflake_role.analyst_admin.name]
}

resource "snowflake_grant_privileges_to_role" "warehouse" {
  privileges = ["USAGE", "OPERATE"]
  role_name  = snowflake_role.analyst.name
  on_account_object {
    object_type = "WAREHOUSE"
    object_name = snowflake_warehouse.analytics.name
  }
}
Enter fullscreen mode Exit fullscreen mode

Rule of thumb. Snowflake RBAC = codified.

opentofu vs terraform interview question on module design

A senior interviewer asks: "Design a reusable Snowflake warehouse module. What inputs, outputs?"

Solution Using module with inputs, outputs, and locals

# modules/snowflake-warehouse/variables.tf
variable "name" { type = string }
variable "size" { type = string; default = "X-SMALL" }
variable "auto_suspend" { type = number; default = 60 }
variable "min_clusters" { type = number; default = 1 }
variable "max_clusters" { type = number; default = 3 }
variable "environment" { type = string }

# modules/snowflake-warehouse/main.tf
resource "snowflake_warehouse" "this" {
  name              = "${upper(var.name)}_${upper(var.environment)}"
  warehouse_size    = var.size
  auto_suspend      = var.auto_suspend
  auto_resume       = true
  min_cluster_count = var.min_clusters
  max_cluster_count = var.max_clusters

  tag {
    name  = "env"
    value = var.environment
  }
}

# modules/snowflake-warehouse/outputs.tf
output "name" { value = snowflake_warehouse.this.name }
output "size" { value = snowflake_warehouse.this.warehouse_size }
Enter fullscreen mode Exit fullscreen mode

Why this works — concept by concept:

  • Reusable module — inputs parameterise; outputs expose.
  • Environment scoping — name suffix per env.
  • Sensible defaults — most calls only override size.
  • Tags — cost attribution via env tag.
  • DRY — same module for all warehouses.

SQL
Topic — SQL
SQL practice library

Practice →

SQL Topic — indexing SQL indexing drills

Practice →


5. Decision matrix + Terragrunt

Decision framework + enterprise alternatives + Terragrunt wrapper

The mental model in one line: for a new data platform IaC repo in 2026, default to OpenTofu unless you need HashiCorp Cloud (HCP) integration; use Spacelift / Env0 / Scalr for enterprise features (RBAC, policy-as-code, cost, drift); wrap either binary with Terragrunt if you need DRY multi-environment module management with hierarchical config.

Visual diagram of decision matrix + Terragrunt — 6-row decision matrix table with OpenTofu vs Terraform columns per criterion, Terragrunt card wrapping both backends with DRY module strips; on a light PipeCode card.

Slot 1 — the decision matrix.

Criterion OpenTofu Terraform
License ✅ MPL 2.0 (true OSS) ⚠️ BSL 1.1 (non-compete restricted)
Governance Linux Foundation HashiCorp (IBM)
Community Growing rapidly Established
HCP integration
Sentinel policies ❌ (use OPA)
State encryption at rest ✅ built-in (1.7+) ✅ via backend
removed block ✅ (1.8+) ✅ (1.7+)
Provider updates Slight lag possible Native
Enterprise support Spacelift, Env0, Scalr HashiCorp Business, Spacelift, Env0, Scalr
Recommended for new projects Only if HCP dependency

Slot 2 — enterprise alternatives.

  • Spacelift — cloud-hosted IaC platform; supports both; strong RBAC + policy + cost.
  • Env0 — similar; strong on Terraform Cloud parity.
  • Scalr — Terraform Enterprise alternative; supports both.
  • Terraform Cloud (HCP Terraform) — HashiCorp only.

Slot 3 — Terragrunt.

# terragrunt.hcl
terraform {
  source = "git::git@github.com:myco/modules.git//warehouse?ref=v1.2.3"
}

inputs = {
  name         = "analytics"
  size         = "MEDIUM"
  environment  = get_env("TG_ENV")
}

remote_state {
  backend = "s3"
  config = {
    bucket = "myco-tofu-state"
    key    = "${path_relative_to_include()}/tofu.tfstate"
    region = "us-west-2"
  }
}
Enter fullscreen mode Exit fullscreen mode

Slot 4 — Terragrunt directory pattern.

platform-iac/
├── terragrunt.hcl              # global config
├── envs/
│   ├── dev/
│   │   ├── terragrunt.hcl      # env-specific
│   │   └── snowflake/
│   │       └── terragrunt.hcl  # component
│   ├── staging/
│   └── prod/
Enter fullscreen mode Exit fullscreen mode

Each terragrunt.hcl inherits from parent + defines specifics.

Slot 5 — Terragrunt commands.

cd envs/prod/snowflake
terragrunt init
terragrunt plan
terragrunt apply

# Run all environments
terragrunt run-all apply
Enter fullscreen mode Exit fullscreen mode

Slot 6 — Terragrunt vs plain workspaces.

  • Plain workspaces — one Terraform state per env; single config file with var.env.
  • Terragrunt — separate directory + config per env; DRY via includes; better isolation.
  • Terragrunt scales better for large multi-env deployments.

Slot 7 — the OpenTofu + Terragrunt combo.

# terragrunt.hcl
terraform {
  source = "..."
  # Terragrunt calls `tofu` binary (config: TG_TF_PATH)
}
Enter fullscreen mode Exit fullscreen mode

TG_TF_PATH=tofu terragrunt apply — Terragrunt wraps OpenTofu.

Slot 8 — enterprise combo.

Team stack:

  • Backend: OpenTofu.
  • CI: Spacelift or Env0.
  • Policy: OPA.
  • DRY: Terragrunt.
  • Vault: HashiCorp Vault (still MPL for CE).
  • State: S3 + DynamoDB + encryption at rest.

Slot 9 — infracost integration.

# Estimate cost before apply
infracost breakdown --path .
Enter fullscreen mode Exit fullscreen mode

Works with both OpenTofu and Terraform.

Slot 10 — CI patterns.

# GitHub Actions
- uses: opentofu/setup-opentofu@v1
  with:
    tofu_version: 1.9.0

- run: tofu init
- run: tofu plan -out=tfplan
- run: |
    infracost breakdown --path tfplan
    tofu apply tfplan
Enter fullscreen mode Exit fullscreen mode

Common beginner mistakes

  • Picking based on hype rather than team fit.
  • Using Terragrunt when plain workspaces suffice (small teams).
  • Not integrating cost estimation.
  • No policy engine.
  • Skipping state encryption.

Worked example — Terragrunt directory structure

platform-iac/
├── root.hcl                    # global backend + provider config
├── envs/
│   ├── env.hcl                 # shared env logic
│   ├── prod/
│   │   ├── env.hcl             # env-specific inputs
│   │   ├── snowflake-warehouses/
│   │   │   └── terragrunt.hcl
│   │   ├── bigquery-datasets/
│   │   │   └── terragrunt.hcl
│   │   └── data-lake-buckets/
│   │       └── terragrunt.hcl
│   ├── staging/
│   └── dev/
└── modules/
    ├── snowflake-warehouse/
    └── bigquery-dataset/
Enter fullscreen mode Exit fullscreen mode

Rule of thumb. Terragrunt for large teams with strict DRY needs.

Worked example — Spacelift stack setup

# .spacelift/config.yml
version: 1
stack:
  name: data-platform-prod
  branch: main
  terraform_version: 1.9.0
  # OR
  opentofu_version: 1.9.0
  variables:
    - name: TF_VAR_env
      value: prod
    - name: SF_ACCOUNT
      value: myco.us-west-2
      secret: true
Enter fullscreen mode Exit fullscreen mode

Rule of thumb. Spacelift config = declarative stack.

Worked example — cost estimation

# GitHub Actions
- name: Infracost breakdown
  uses: infracost/actions/breakdown@v1
  with:
    path: envs/prod
    format: json
    out-file: /tmp/infracost.json

- name: Post cost estimate
  uses: infracost/actions/comment@v1
  with:
    path: /tmp/infracost.json
Enter fullscreen mode Exit fullscreen mode

Rule of thumb. Cost estimate in PR = fewer surprises.

opentofu vs terraform interview question on tool stack for enterprise data platform

A senior interviewer asks: "You're greenfield for a 100-person enterprise. Data platform on 20 AWS accounts + Snowflake. Pick your IaC + surrounding tools."

Solution Using OpenTofu + Terragrunt + Spacelift + OPA + Infracost + Vault

Stack:

  • IaC binary: OpenTofu 1.9+.
  • Wrapper: Terragrunt for DRY multi-env.
  • CI/CD: Spacelift (self-hosted or SaaS).
  • Policy: OPA integrated with Spacelift.
  • Cost: Infracost in PR checks.
  • Secrets: Vault (Community Edition = MPL).
  • State backend: S3 + DynamoDB lock + AWS KMS encryption at rest (via OpenTofu 1.7+ feature).
  • Providers: aws, snowflake, kubernetes, dbtcloud, helm.

Why this works — concept by concept:

  • OpenTofu — future-proof license; foundation governance.
  • Terragrunt — DRY across 20 accounts × 3 envs.
  • Spacelift — hosted platform; RBAC; parallel runs.
  • OPA — policy-as-code.
  • Infracost — cost visibility before apply.
  • Vault — secrets vault; MPL CE version.
  • S3 + KMS — encrypted state at rest.

SQL
Topic — SQL
SQL practice library

Practice →

SQL
Topic — optimization
SQL optimization drills

Practice →


Cheat sheet — OpenTofu / Terraform recipe list

  • BSL 1.1 — Terraform's license; non-compete for vendors; free for direct users.
  • MPL 2.0 — OpenTofu's license; true open source.
  • tofu init / plan / apply / destroy — commands identical to terraform.
  • State file compatible — migration is binary swap.
  • All providers work on both — Snowflake, BigQuery, AWS, GCP, Azure.
  • OpenTofu 1.7+ — state encryption at rest.
  • OpenTofu 1.8+removed block, dynamic provider imports.
  • OpenTofu 1.9+ — provider-defined functions parity.
  • Spacelift / Env0 / Scalr — enterprise platforms; both engines.
  • HashiCorp Cloud (HCP Terraform) — Terraform only.
  • Sentinel — HashiCorp Enterprise policy; replace with OPA.
  • OPA (Open Policy Agent) — CNCF; broader than Sentinel.
  • Terragrunt — DRY wrapper; works with both binaries.
  • Infracost — cost estimation in PRs.
  • S3 + DynamoDB backend — standard remote state pattern.
  • Workspaces — env isolation via CLI.
  • Modules — reusable resource collections.
  • Never commit secrets — Vault or env vars.
  • Version pinning~> 5.83 or = 5.83.0.
  • Chart of providers — Snowflake, BigQuery, Databricks, dbt Cloud all available.

Frequently asked questions

Do I need to migrate off Terraform?

Only if BSL affects your business — SaaS vendors building Terraform-compatible services. End users deploying their own infrastructure are unaffected legally. That said, most new projects in 2026 choose OpenTofu because of community governance, MPL license, and OpenTofu-only features (state encryption at rest, removed block). Existing projects on Terraform can stay if HCP integration is important; migrate to OpenTofu when convenient otherwise. Rule — assess based on your specific use case.

Can I run both on the same state?

Technically yes — state files are compatible — but not recommended. Pick one; use consistently. Mixing risks version-drift, confusion in CI logs, and unclear provenance. Rule — one tool per project; migrate all or none.

What breaks when migrating?

Almost nothing. Same HCL, same providers, same state format. Command is tofu instead of terraform. HCP Terraform integration and Sentinel policies don't work in OpenTofu — plan replacements (Spacelift/Env0/Scalr for HCP; OPA for Sentinel). Rule — plan migration; execute in dev first; grace period before prod cutover.

What about Terraform Cloud / HCP?

HCP Terraform is HashiCorp-exclusive. OpenTofu alternatives are Spacelift, Env0, Scalr — all support both engines. Choose based on features needed (RBAC, private module registry, policy, cost, drift detection). Rule — Spacelift for cloud-hosted OpenTofu with all enterprise features.

Is OpenTofu ready for production?

Yes — 1.7, 1.8, and 1.9 are stable, widely deployed. Community and vendor sponsorship (Spacelift, Gruntwork, Harness) mean roadmap is well-funded. Major data teams have migrated with no issues. Providers explicitly test both. Rule — production-ready for new projects; migration path clean for existing.

What about IaC alternatives — Pulumi, CDKTF?

Different niche. Pulumi uses real programming languages (TypeScript, Python, Go, C#) — good for complex logic requiring loops and conditionals. CDKTF wraps Terraform / OpenTofu in code. OpenTofu / Terraform remain dominant for declarative HCL, especially in ops-heavy teams. Rule — Pulumi for imperative code; HCL for declarative.

Practice on PipeCode

Pipecode.ai is Leetcode for Data Engineering — every `opentofu vs terraform` pattern above ships with hands-on practice rooms where you compare BSL 1.1 vs MPL 2.0 semantics, migrate a 50-workspace Terraform repo to OpenTofu via `tofu init`, enable state encryption at rest with the OpenTofu 1.7 `encryption` block, wrap the whole platform in Terragrunt for DRY multi-env, and finally stitch Spacelift + OPA + Infracost + Vault into the enterprise IaC stack — the exact platform-tool fluency that senior data-platform interviews probe.

Practice SQL now →
Optimization drills →

Top comments (0)