Chapter 27

OpenTofu on GCP

Scope. This chapter is a difference. Chapter 26 owns Terraform on Google Cloud — providers, authentication, structure, state, plan review, CI/CD, module design, and anti-patterns — and every one of those explanations holds for OpenTofu unchanged. This chapter covers only what is genuinely different: the governance and licensing question, the registry and its signing authority, client-side state encryption, install-time verification, and the mechanics of migrating. Where a topic is the same, it says so and cites §26.x rather than repeating it. Prerequisites. Chapter 26 in full. Chapter 14 (§14.8 Cloud KMS keys), Chapter 13 (§13.13 write-only arguments). Verified against. OpenTofu 1.12.6 and Terraform 1.15.8, both installed and exercised directly; hashicorp/google provider 8.1.0 resolved from both registries; Cloud SDK 583.0.0; see sources at end.

OpenTofu is a fork of Terraform, created after HashiCorp relicensed Terraform under the Business Source License in August 2023, and now developed under the Linux Foundation. It is not a reimplementation: it reads the same HCL, writes the same state format, uses the same backends, and resolves the same provider source addresses. Almost everything a team knows about Terraform transfers without adjustment.

Which makes the small set of real differences worth stating precisely, because the folk version of this comparison is wrong in both directions. OpenTofu is not "Terraform without the license"; it has capabilities Terraform does not, one of which — client-side state encryption — closes a gap that Chapter 26 had to work around. And it is not a drop-in mirror either: the provider binaries it installs are built and signed by a different organization from the same source, which is a trust-anchor decision an organization with a provider allowlist has to make deliberately.

Two claims that circulate widely are false, and this chapter says so with evidence. State is not incompatible between the two tools — it round-trips in both directions. And OpenTofu does not lack the ephemeral values and write-only arguments that §13.13's secret-handling pattern depends on; it has had them since 1.11. Both were checked by running the binaries rather than by reading about them.

27.1 Why OpenTofu §

The reason to consider OpenTofu is governance, not features. The features came later and are a bonus.

The licensing facts. HashiCorp relicensed Terraform from the Mozilla Public License to the Business Source License 1.1 with version 1.6.0. The current LICENSE file in the Terraform repository names IBM as licensor — HashiCorp was acquired — and specifies MPL 2.0 as the Change License, meaning each version becomes open source again after the change date.

What BUSL actually restricts is narrower than the reaction to it. The restriction is on offering a competing hosted or embedded product. Internal use — the case that describes nearly every reader of this book — is not restricted. An organization that runs Terraform to manage its own estate is not affected by the license change and should not adopt OpenTofu because of it.

The organizations for which it does matter are those building a product that embeds or hosts infrastructure-as-code execution, those with procurement rules that require an OSI-approved license, and those that treat a single vendor's control over a critical tool as a concentration risk.

OpenTofu's own position: MPL 2.0, developed under the Linux Foundation, with a public RFC process and no single corporate owner.

The honest summary is that this is a governance choice with a small technical dividend and a small technical cost. §27.11 is the dividend and §27.4 is the cost. A team choosing on features alone will find the decision closer than the discourse suggests.

Pitfall. Adopting OpenTofu to escape a licensing problem you do not have adds a second toolchain, a second trust anchor, and a second support window (§27.12) in exchange for nothing. Read the license against your actual use before treating this as urgent.

27.2 Terraform Compatibility §

Compatibility is close enough that the interesting question is not "will my configuration run" but "which small things differ".

The subcommand sets are nearly identical. OpenTofu has no subcommand Terraform lacks. Terraform has four surfaces OpenTofu does not:

Terraform-onlyWhat it is
terraform stacksHCP Terraform stack operations
terraform querySearch and list remote infrastructure
terraform modulesShow declared modules in a working directory
terraform state identitiesList resource identities in state

The OpenTofu-only surface is flags and language, not commands. The ones that matter operationally:

  • -exclude and -exclude-file on plan and apply — the inverse of -target, and a better-shaped tool for the "apply everything except this" case.
  • -json-into=FILE on most commands, writing machine-readable output to a file while keeping human-readable output on the terminal. In CI this removes the usual choice between a log a person can read and a log a script can parse.
  • -concise on plan and apply, which suppresses the refresh chatter.
  • Variables and locals in backend blocks. Terraform rejects these: Variables may not be used here. This is the one early-evaluation difference that survives scrutiny (§27.9).

Two commonly cited differences are not differences. Terraform 1.15.8 accepts const = true variables and locals in a module source, so early evaluation is narrower than usually claimed; and provider-defined functions exist in both.

.tofu files are read only by OpenTofu and override a same-named .tf, which makes one repository runnable by both tools with tool-specific overrides where needed.

Pitfall. -show-sensitive on plan, apply, show, and output is OpenTofu-only and prints values the tool deliberately redacts. It exists for debugging. In a pipeline it writes secrets to a build log, which is §26.17's failure with a flag attached.

27.3 Installing OpenTofu §

A security book's install section is about verifying the download, not about running a package manager.

Do not pipe an install script to a shell. OpenTofu publishes an installer, and the reason to avoid the convenient invocation is the same reason this book pins provider versions: the thing that installs your infrastructure tool is part of your supply chain (§37.2).

Verify the release before it runs. OpenTofu publishes checksums with the release and signs them, and the project documents verification with cosign against its published identity. The sequence is: download the archive and the checksum file, verify the checksum file's signature, then verify the archive against the checksum file. Verifying the archive against an unverified checksum file proves nothing.

Pin the version in CI explicitly. A pipeline that installs "the latest" changes its infrastructure tool without a change to the repository — which is exactly what §26.26 rejects for modules and §26.2 rejects for providers, and the tool binary deserves the same treatment.

In this environment OpenTofu is 1.12.6, installed to $HOME/bin, and tofu version confirms it. Record the version in your pipeline's output so a plan can be attributed to a specific binary.

The alpine, deb, rpm, and standalone archive channels all exist, and a container image is available. For a CI runner, a pinned image digest is the strongest option because it makes the whole toolchain one immutable reference (§25.12).

Pitfall. Installing OpenTofu alongside Terraform on the same machine, with both on PATH, invites the mistake of running the wrong one against a state directory. The two are compatible enough that it usually works and rare enough that when it does not, the failure is confusing. Pick one per repository and enforce it in CI.

27.4 Provider Compatibility §

Providers resolve identically and the binaries are different. This is the chapter's most important finding and the honest cost of the trade.

registry.opentofu.org implements the same protocol as registry.terraform.io, serving the same providers.v1 and modules.v1 discovery paths. That is why an unmodified configuration works. hashicorp/google is present at the same versions, and the book's ~> 8.0 pin resolves to 8.1.0 under both tools.

But source = "hashicorp/google" is a relative address, and each tool completes it with its own default registry host. Same string, two different origins:

TerraformOpenTofu
Registry hostregistry.terraform.ioregistry.opentofu.org
Binary built fromgithub.com/hashicorp/terraform-provider-googlegithub.com/opentofu/terraform-provider-google
Signed byHashiCorpOpenTofu, key 0C0AF313E5FD9F80

tofu init says so explicitly, reporting Installed hashicorp/google v8.1.0 (signed, key ID 0C0AF313E5FD9F80) where Terraform reports (signed by HashiCorp). The pattern is uniform across providers, not a special case for one.

The source is the same; the build and the signing authority are not. OpenTofu compiles HashiCorp's MPL-licensed provider source through its own release pipeline, and states that the results are "not byte-for-byte identical".

For most teams this is a note. For some it is a control change. An organization with an approved-publisher policy, a provider allowlist keyed on a signing key, or an SBOM process that records provenance must add OpenTofu's key deliberately and document why. That is a trust-anchor decision, and it belongs in a risk register rather than in a pull request nobody read.

Pitfall. A team that mirrors providers internally (§27.12) and switches tools without repointing the mirror silently starts pulling from a different origin. The lock file records the change; nobody reads the lock file diff.

27.5 State Compatibility §

State is compatible in both directions. The widely repeated claim that a fork point makes newer Terraform state unreadable is not supported by either project's documentation and is contradicted by experiment.

Run both binaries against the same state and it settles. Terraform 1.15.8 applies and writes state format version 4; OpenTofu 1.12.6 reads it and reports No changes; OpenTofu applies, rewriting only the terraform_version field; Terraform reads that back and also reports No changes.

The real incompatibility is one field, and it is not in state. tofu init rewrites the dependency lock file, remapping provider source addresses:

Warning: Dependency lock file entries automatically updated
  - registry.terraform.io/hashicorp/random
      => registry.opentofu.org/hashicorp/random
The version selections were preserved, but the hashes were not because
the OpenTofu project's provider releases are not byte-for-byte identical.

That warning is §27.4 restated as an operational event. Versions carry over; hashes do not, because the artifacts differ.

State also records the provider source address on each resource, so a migrated state names registry.opentofu.org thereafter. This is what makes a rollback (§27.6) untidy rather than impossible.

The book's secret-handling pattern is unaffected. OpenTofu 1.11 added ephemeral values, ephemeral resources, and write-only attributes, so §13.13's secret_data_wo and password_wo approach works unchanged. State encryption (§27.11) is an additional layer, not a replacement for keeping secrets out of state in the first place.

Pitfall. "State is compatible" is a statement about these two versions, tested. It is not a guarantee across every future pair. Test the round trip against your own versions before relying on the ability to go back.

27.6 Migrating Terraform to OpenTofu §

Migration is a per-root-module operation, and its risk is concentrated in the moment the lock file changes.

The sequence, one root module at a time:

  1. Back up state and the lock file. Object versioning on the state bucket (§26.14) is your safety net, and this is the operation it exists for.
  2. tofu init. It rewrites .terraform.lock.hcl and warns that it has.
  3. tofu plan. The expected result is No changes — or precisely the plan Terraform produced. Anything else stops the migration.
  4. tofu apply. Even with no infrastructure changes, this normalizes the state file.
  5. Make one small, reversible change and apply it, to prove the whole path works.

Migrate the least important environment first and leave a gap. Sandbox, then development, then staging, then production, with enough time between each to notice a provider behavior difference that only appears under load.

Commit the rewritten lock file as its own change. It is the security-relevant artifact of the migration — it is the record that the provider's trust anchor changed — and burying it inside a larger commit is how that decision escapes review.

Rollback is documented and it is blunt: stop using OpenTofu, restore the backups, and run terraform init and terraform plan to verify no unexpected changes. It is not a clean inverse, because state and lock file both carry registry.opentofu.org references until they are restored or rewritten.

Pitfall. Migrating every root module in one change gives you no gradient. If a difference appears, you cannot tell which environment or which provider surfaced it, and the rollback is estate-wide rather than local.

27.7 Module Compatibility §

Modules are portable without modification, and §26.7's design rules and §26.26's versioning rules apply unchanged.

A module is HCL and a source address, and both tools read both. A module that configures no provider and no backend — which §26.7 requires anyway — has nothing tool-specific in it.

Registry modules resolve from each tool's own default host, exactly as providers do (§27.4). A private module served from a git URL or a gcs:: bucket is host-independent and therefore identical under both, which is a small argument for preferring those sources in a mixed estate.

Early evaluation in module sources is not the differentiator it is claimed to be. Terraform 1.15.8 accepts locals and const = true variables in a module source; OpenTofu additionally accepts a plain variable. The difference is real and narrow, and a module that relies on it is not portable — which is a reason to avoid relying on it rather than a reason to celebrate it.

Keep modules tool-neutral deliberately. If any part of the estate might run either tool, the shared module layer is the wrong place to use a tool-specific feature. Put the tool-specific configuration in root modules, where it is one file per environment and visible.

Pitfall. A .tofu file inside a shared module makes that module behave differently under the two tools while looking identical in a diff. The technique is legitimate at the root-module layer and a trap in a shared module, because the caller cannot see it.

27.8 OpenTofu Project Layout §

The layout is §26.5's layout. Nothing about the directory structure, the root-module-per-environment rule, or the module conventions changes.

What can change is where a value may appear. Because OpenTofu evaluates variables early enough to use them in a backend block, one class of duplication that Terraform forces becomes avoidable:

terraform {
  backend "gcs" {
    bucket = "rc-saas-bootstrap-tfstate-01"
    prefix = "envs/${var.environment}"
  }
}

Terraform rejects this outright with Variables may not be used here., which is why §26.18 warns that the prefix is a line copied between environment directories and easy to get wrong.

Use it with restraint. A parameterized backend makes one file serve every environment, and it also makes the state location depend on a variable — so a wrong variable points a plan at the wrong state. That is the failure §26.15's pitfall describes, made easier to trigger. If you adopt it, derive the value from the directory in CI and assert it, rather than accepting it as user input.

lifecycle { enabled = ... } and lifecycle { destroy = false } are OpenTofu-only meta-arguments. They are worth knowing about and worth treating as portability hazards: a module using them will not run under Terraform at all.

Pitfall. Adopting OpenTofu-only layout features across the whole repository forecloses going back. Migration was cheap in §27.6 precisely because the configuration was tool-neutral; a configuration that uses variables in backends and .tofu overrides is a one-way door.

27.9 Remote State §

The gcs backend is the same backend, with the same arguments and the same locking mechanism. §26.14 owns the bucket configuration and §26.15 owns the lock; both apply here without change.

The three differences, all small:

  • Variables are permitted in the backend block (§27.8), so the prefix can be computed.
  • universe_domain is accepted as a backend argument, for sovereign-cloud universes.
  • -var and -var-file are accepted on more commands — show, output, force-unlock, untaint — which follows from the first difference, because those commands may now need variable values to resolve the backend and the encryption configuration.

Everything else holds. The .tflock object with a generation-number lock ID, the recommendation to enable object versioning, the CMEK-versus-CSEK asymmetry, and the warning about credentials in -backend-config are all §26.14 and §26.15, unchanged.

The state bucket's IAM, CMEK, audit configuration, and per-environment separation are identical (§26.17, §26.18). A team running both tools against different environments uses one bucket policy and one set of controls.

Pitfall. State encryption (§27.11) changes what is in the object, not who may read it. A team that turns on client-side encryption and then relaxes the bucket's IAM has traded a control it understood for one whose key management it has just taken on.

27.10 CI/CD §

The pipeline is §26.24's pipeline: plan on the pull request, policy-check the plan, human approval, apply the saved plan artifact, with separate identities for plan and apply.

Authentication is identical, because it is the provider's concern and the provider is the same. §26.3 and §26.4 apply in full — Workload Identity Federation, the principalSet pinned to a numeric ID, roles/iam.workloadIdentityUser to become an account and roles/iam.serviceAccountUser to attach one.

Two OpenTofu flags improve a pipeline specifically:

  • -json-into=plan.json writes structured output to a file while leaving readable output on the terminal, so one run produces both the artifact a policy check consumes and the log a human reads. Under Terraform these are two invocations.
  • -concise removes the per-resource refresh lines, which in a large root module is most of the log.

TF_ENCRYPTION supplies the encryption configuration out of band (§27.11), which is the right shape for CI: the key reference lives in the pipeline's environment rather than in the repository, and the configuration in code can then declare enforced = true without naming a key.

Policy validation is unchanged. gcloud scc iac-validation-reports create consumes a Terraform plan in JSON, and OpenTofu's plan JSON is the same format. §22.12 and §26.25 apply.

Pitfall. A pipeline that installs the tool by name rather than by pinned version, on a runner where both tools exist, can silently run the other one. The plan will usually succeed. Assert the binary and its version as a pipeline step, and fail if it is not what the repository expects.

27.11 Security §

This is the section where OpenTofu offers something Terraform does not: state and plan files encrypted client-side, before they are written anywhere.

§26.16 established that Terraform has no such feature — terraform validate rejects the block with Blocks of type "encryption" are not expected here. while tofu validate accepts it. Under Terraform the only protection is the bucket's.

Cloud KMS is the right key provider, and its arguments are exact:

terraform {
  encryption {
    key_provider "gcp_kms" "state" {
      kms_encryption_key = join("/", [
        "projects/rc-saas-shared-sec-01/locations/us-central1",
        "keyRings/kr-us-central1-state",
        "cryptoKeys/k-tofu-state",
      ])
      key_length = 32
    }

    method "aes_gcm" "state" {
      keys = key_provider.gcp_kms.state
    }

    state {
      method   = method.aes_gcm.state
      enforced = true
    }

    plan {
      method   = method.aes_gcm.state
      enforced = true
    }
  }
}

kms_encryption_key and key_length are both required, and AES-GCM needs a 16, 24, or 32-byte key — 32 is the sensible choice. AES-GCM is the only non-experimental method.

enforced = true is the control that matters. Without it, a misconfiguration writes plaintext and nothing complains. With it, OpenTofu refuses to write unencrypted state or plan files at all.

plan { } is the half that Terraform users forget they need. §26.17's pitfall is that the plan artifact holds the same secrets as state and is routinely stored as a build artifact. This encrypts it.

Migrating existing unencrypted state requires a fallback block, because OpenTofu deliberately refuses to read plaintext once encryption is configured — a manipulated-state protection. The documented path declares an unencrypted method as the fallback, applies once so state is rewritten encrypted, then removes the fallback.

Everything else in §26.17 still applies. Encryption protects the object; it does not replace bucket IAM, audit logging, or keeping secrets out of state through write-only arguments, which OpenTofu also supports (§27.5).

Pitfall. Key saturation is real: AES-GCM degrades with a short, static key used indefinitely. Cloud KMS with rotation is the documented answer, and it is the reason to prefer gcp_kms over the passphrase-based provider even though the latter is easier to set up.

27.12 OpenTofu in Enterprise Environments §

Two enterprise concerns differ from Terraform's: how providers are supplied on a restricted network, and what the support commitment is.

Provider mirroring is configured in .tofurc, not .opentofurc — a name almost everyone gets wrong on the first attempt. .terraformrc is honored for backward compatibility and loses to .tofurc when both exist.

provider_installation blocks constrain where providers may come from, which is the control an air-gapped or allowlisted environment needs. A filesystem_mirror or network_mirror with an explicit include list means the pipeline cannot reach any registry, and the mirror's contents become the approved set — which pairs directly with the trust-anchor decision in §27.4.

OpenTofu also supports OCI registries as a module source and as a provider mirror, which lets an organization reuse the Artifact Registry it already has (Chapter 24) and inherit its IAM, scanning, and immutability controls rather than running a separate mirror.

There is no first-party managed backend equivalent to HCP Terraform. Terraform's help text names HCP Terraform directly; OpenTofu's says "cloud backend". An organization currently relying on a managed run environment has to replace that with its own pipeline (§27.10), which is a larger piece of work than the tool swap.

Support windows are shorter than an enterprise upgrade cycle. OpenTofu 1.12.x support ends 2027-02-01. Plan the upgrade cadence before adopting, because an unsupported infrastructure tool is a control with no security patches.

Pitfall. Approving OpenTofu as a tool without also approving its signing key and its registry host approves half the decision. The provider binaries are the code that runs with the pipeline's credentials, and they are the part that changed (§27.4).

27.13 OpenTofu Module Standards §

§26.26 owns module versioning and §26.7 owns module design. Both apply unchanged, and this section adds only what portability requires.

Write modules tool-neutral by default. A shared module that uses lifecycle { enabled }, a .tofu file, or a variable in a backend cannot run under Terraform. If any consumer might, the feature belongs in a root module.

Prefer host-independent sources for shared modules — a git URL or a gcs:: bucket — because a registry source resolves against each tool's own default host (§27.4). This also gives the module the access control of a system you already run.

-deprecation=module:{all,local,none} is an OpenTofu-only control for surfacing deprecated module variables and outputs. In a repository with many modules and many callers it turns an intended deprecation into a visible warning, which is the missing piece in §26.26's SemVer story: the standard says a removal is breaking, and this is what tells callers before it happens.

Test modules with the tool that will run them. Terraform's test command has -junit-xml and -parallelism that OpenTofu's does not, so a CI test harness built around those needs adjusting.

Pitfall. Publishing one shared module library for an estate that runs both tools, and testing it under only one, means half the consumers run untested code. Either test both in CI or state plainly which tool the library supports.

27.14 OpenTofu Example Environment §

A complete root module, showing only the parts that differ from §26.5's Terraform equivalent.

terraform {
  required_version = ">= 1.11"

  required_providers {
    google = {
      source  = "hashicorp/google"
      version = "~> 8.0"
    }
  }

  backend "gcs" {
    bucket = "rc-saas-bootstrap-tfstate-01"
    prefix = "envs/prod"
  }

  encryption {
    key_provider "gcp_kms" "state" {
      kms_encryption_key = var.state_kms_key
      key_length         = 32
    }

    method "aes_gcm" "state" {
      keys = key_provider.gcp_kms.state
    }

    state {
      method   = method.aes_gcm.state
      enforced = true
    }

    plan {
      method   = method.aes_gcm.state
      enforced = true
    }
  }
}

provider "google" {
  project = var.project_id
  region  = "us-central1"

  default_labels = {
    managed-by = "opentofu"
    env        = var.environment
  }
}

module "network" {
  source = "git::https://source.example.com/tf-modules.git//network?ref=v2.3.0"

  project_id  = var.project_id
  name_prefix = "rc-saas-${var.environment}"
  subnets     = var.subnets
}

Four things in that file are OpenTofu-specific and everything else is Chapter 26. The encryption block and its four nested blocks; the >= 1.11 floor, which is where write-only attributes arrived; the git module source chosen for host independence (§27.13); and the managed-by label, which is worth setting honestly so an auditor reading Cloud Asset Inventory (§29.6) can tell which tool owns a resource.

The provider pin is ~> 8.0, identical to the rest of the book, and resolves to the same 8.1.0 — from a different origin (§27.4).

Run the same gates. tofu fmt -check, tofu validate, a plan saved to an artifact, gcloud scc iac-validation-reports create on the plan JSON, and a human approval before apply. The pipeline shape is §26.24's and the controls are the same controls.

Pitfall. An encryption block referencing a variable means tofu show and tofu output need that variable too — which is why OpenTofu accepts -var on those commands (§27.9). A pipeline step that runs tofu output without the variables fails in a way that looks like a state problem and is not.

Chapter Summary §

  • OpenTofu is a fork of Terraform under the Linux Foundation and MPL 2.0; the reason to adopt it is governance, and the technical differences are few and specific.
  • Terraform's BUSL restricts competing hosted or embedded offerings, not internal use; the current license file names IBM as licensor.
  • The subcommand sets are near-identical: OpenTofu has none Terraform lacks, and Terraform adds stacks, query, modules, and state identities.
  • State is compatible in both directions, tested: both write format version 4 and only terraform_version changes hands. The common claim of a fork-point incompatibility is false.
  • OpenTofu has ephemeral values and write-only attributes since 1.11, so §13.13's secret pattern works unchanged and state encryption is an additional layer, not a substitute.
  • The registry is not a mirror: registry.opentofu.org serves binaries built from the same source by the OpenTofu project and signed with key 0C0AF313E5FD9F80, explicitly not byte-for-byte identical to HashiCorp's.
  • tofu init rewrites the lock file's provider addresses and preserves versions but not hashes; commit that change on its own so the trust-anchor decision is reviewed.
  • Client-side state and plan encryption is OpenTofu's real advantage; gcp_kms requires kms_encryption_key and key_length, AES-GCM wants 32 bytes, and enforced = true is what makes it a control.
  • Encrypting the plan file closes §26.17's forgotten copy.
  • Migrating existing unencrypted state needs a fallback block, because OpenTofu refuses to read plaintext once encryption is configured.
  • Variables are permitted in backend blocks under OpenTofu and rejected by Terraform; this is the one early-evaluation difference that survives checking.
  • The CLI config file is .tofurc, not .opentofurc.
  • OpenTofu 1.12.x support ends 2027-02-01; an unsupported infrastructure tool is a control with no security patches.
  • Keep shared modules tool-neutral and prefer host-independent sources, so the estate is not locked to one tool by its module library.
  • Migrate one root module at a time, least important first, and verify tofu plan reports no changes before applying.

Security Checklist §

ControlWhy it mattersHow to verify (CLI + Console)
OpenTofu release verified before installThe tool that builds the estate is in its supply chainChecksum signature verification in the install job
Tool and version pinned in CIOtherwise the infrastructure tool changes with no repository changePipeline asserts tofu version matches the expected value
OpenTofu's signing key approved deliberatelyProvider binaries come from a different publisher (§27.4)Provider allowlist or mirror configuration names key 0C0AF313E5FD9F80
Lock file change committed as its own reviewIt records the trust-anchor change during migrationMigration commit history
encryption block present with enforced = true on state and planWithout enforced, a misconfiguration writes plaintext silentlygrep -A2 'state {' *.tf and a deliberate misconfiguration test
Encryption key is Cloud KMS, not a passphraseAES-GCM suffers key saturation with a static keykey_provider "gcp_kms" present; key rotation set (§14.8)
KMS key lives in the security projectSeparates key administration from key use (§2.21)gcloud kms keys describe on the key's full resource name
Bucket controls unchanged from §26.14Encryption protects the object, not access to itBucket IAM, versioning, public access prevention, CMEK
Secrets still kept out of stateEncryption is a second layer, not permission to store secretsgrep -rE '_wo(_version)? *=' *.tf
-show-sensitive absent from every pipelineIt prints values the tool redacts, into a build loggrep -r 'show-sensitive' in pipeline definitions
Provider installation constrained on restricted networksMakes the mirror the approved setprovider_installation block in .tofurc
Migration verified with tofu plan reporting no changesAny other output means the tools disagree about realityMigration runbook step 3
Shared modules run under both tools if both are in useOtherwise half the consumers run untested codeCI matrix covering both binaries
Upgrade planned before the support window ends1.12.x support ends 2027-02-01Roadmap entry and the pinned version in CI

Sources §