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.
Practice on SQL library →, SQL optimization drills →, and SQL indexing drills →.
On this page
- Why the fork matters in 2026
- Licensing + governance
- Feature diff + migration
- Data platform IaC patterns
- Decision matrix + Terragrunt
- Cheat sheet — OpenTofu / Terraform recipe list
- Frequently asked questions
- Practice on PipeCode
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
removedblock, 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 initreads existing Terraform state; zero code changes usually. -
OpenTofu-only features. State encryption,
removedblock. - 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.
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/
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.
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:
- License clarity. MPL 2.0 = future-proof.
- Foundation governance. Community-driven roadmap.
- Full compatibility. All providers work.
-
Feature parity. State encryption +
removedblock > Terraform equivalents. - 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
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
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.
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.
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.
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
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])
}
Migration steps:
- Export Sentinel policies.
- Convert to Rego (semi-mechanical).
- Load into Spacelift as policies.
- Run tests against known-good plans.
- 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.
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.
Slot 1 — HCL syntax compatibility.
100% compatible. Same resource, variable, output, module, provider blocks.
Slot 2 — state file compatibility.
- Same JSON format.
-
tofu initreads existing terraform.tfstate. -
tofu planproduces 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
}
}
}
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
}
}
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
}
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
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
tofucommands.
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
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
}
}
}
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
}
}
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
Why this works — concept by concept:
- Canary in dev first — catches surprises without prod impact.
- Workspace-by-workspace — bounded blast radius.
-
Verification —
diff 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
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.
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
}
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" },
])
}
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({ ... })
}
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
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
}
}
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/
Slot 7 — module usage.
module "analytics_wh" {
source = "../../modules/snowflake-warehouse"
name = "ANALYTICS"
size = var.warehouse_size
auto_suspend = 60
}
Slot 8 — environment-specific vars.
# envs/prod/terraform.tfvars
warehouse_size = "LARGE"
env = "prod"
# envs/dev/terraform.tfvars
warehouse_size = "X-SMALL"
env = "dev"
Slot 9 — secrets via env / Vault.
variable "sf_password" {
type = string
sensitive = true
}
# Passed via TF_VAR_sf_password env var or Vault provider
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"
}
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 * * *"
}
}
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
}
}
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 }
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
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.
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"
}
}
Slot 4 — Terragrunt directory pattern.
platform-iac/
├── terragrunt.hcl # global config
├── envs/
│ ├── dev/
│ │ ├── terragrunt.hcl # env-specific
│ │ └── snowflake/
│ │ └── terragrunt.hcl # component
│ ├── staging/
│ └── prod/
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
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)
}
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 .
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
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/
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
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
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
SQL
Topic — optimization
SQL optimization drills
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+ —
removedblock, 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.83or= 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
- Drill the SQL practice library → — 450+ DE-focused questions covering resources declared in IaC.
- Sharpen SQL optimization drills → — Snowflake warehouse tuning declared via IaC.
- Layer SQL indexing drills → — BigQuery clustering + partitioning via IaC.
- For the broader SQL interview surface, take the SQL for Data Engineering course →.
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.





Top comments (0)