Engineering research / cloud

Terraform 1.17: Policy Checks in plan and apply, the Minimal Refresh Option, and Constant Variables in Provider Requirements

What Terraform 1.17.0 changes in day-to-day use: the -policies flag without an experimental build and the plugin it needs, -minimal-refresh and the drift it can miss, const variables in required_providers, policy evaluation of replacements, and an adoption plan.

By Sandeep Belgavi · Published 2026-10-25 · Prepared 2026-10-22

Recent development · Practical technical guide · 22 October 2026

What Terraform 1.17.0 changes in day-to-day use: the -policies flag without an experimental build and the plugin it needs, -minimal-refresh and the drift it can miss, const variables in required_providers, policy evaluation of replacements, and an adoption plan.

The release

HashiCorp released Terraform 1.17.0 on 21 October 2026. Three new features lead the changelog: variables and locals in provider requirements, a -minimal-refresh planning option, and Terraform Policy described as generally available, with the -policies flag for plan, apply and query no longer requiring an experimental build. Smaller changes follow: diagnostics from renewing ephemeral resources are now shown, pow and log raise errors instead of returning infinity, terraform version -json gains a format_version field, and the binary is built with Go 1.26.10, which no longer trusts certificates from the compromised eMudhra intermediate CAs.

Everything marked as observed below was run on 2026-10-22 with the Windows amd64 build, whose SHA-256 matched the published checksums, against small local configurations.

Minimal refresh: faster plans, narrower drift detection

A normal plan refreshes every managed resource, asking each provider whether the real object still matches state, before computing changes. On large states that is most of the plan time and many provider API calls. The pull request that implements -minimal-refresh describes the rule: Terraform first plans each resource instance from state and configuration, and refreshes only instances whose initial plan shows a change. A refresh of one resource does not cause its dependents to refresh. Instances whose schema was upgraded are always refreshed. Destroy plans, deposed and orphaned instances are never refreshed with the flag.

The probe applied two terraform_data resources, changed the input of one, and planned twice. With -minimal-refresh only the changed resource printed Refreshing state; without it both did. Both plans proposed the same single change, because nothing had drifted.

The trade-off is drift. A resource that someone changed outside Terraform, and whose configuration you did not touch, produces no change in the initial plan, so it is not refreshed and the drift is not reported. The help text says it plainly: faster, at the expense of possibly planning against a stale record. Use it for the inner loop of development and for pipelines that also run a scheduled full plan or -refresh-only plan to catch drift; do not make it the only plan that ever runs against production.

Policy checks: GA in the CLI, with a plugin to install

The changelog says the -policies flag no longer needs an experimental build, and that query policies evaluate resources found by list blocks. On the release binary the flag is accepted but is not listed in terraform plan -help. Pointing it at an empty directory failed with Failed to connect to policy engine: ... "tfpolicy-plugin": executable file not found in %PATH%; pointing it at a path that does not exist failed with Invalid policy path. So a working setup needs the separate Terraform policy CLI and plugin installed on every runner, not only the new Terraform binary.

Two further details. HashiCorp’s Terraform policy documentation page still carried a beta notice when it was read for this guide, for the HCP Terraform side of the workflow; check the status of the parts you depend on. And pull request #38970 changed how replacements are presented to policies: a resource replaced through create-then-destroy or destroy-then-create is now evaluated as a separate create with the new values and a delete with the prior values, where it used to be collapsed into one update. A policy that blocks deletes of a database will now also see the delete half of a replacement.

Constant variables in provider requirements

Provider sources and versions can now come from variables and locals, provided the values are known during init. The pull request introduces const = true on variables for this purpose. The probe confirmed the edges:

HCL from the probe; init was run with -var random_version=3.7.2.

terraform {
  required_providers {
    random = {
      source  = "hashicorp/random"
      version = var.random_version
    }
  }
}

variable "random_version" {
  type  = string
  const = true
}
ProbeResult
init -var random_version=3.7.2Installed hashicorp/random v3.7.2
init with no valueNo value for required variable
Same variable without const = trueUnknown provider version: only literal values and const variables can be evaluated during init

The use case is organisations that pin provider versions or private registry namespaces centrally. The lock file still records the exact selection, so commit it as before; the variable only chooses the constraint that init resolves.

A CI layout that keeps drift visible

The safe way to use minimal refresh is to run two kinds of plan for the same workspace: a fast one where people wait for it, and a full one on a schedule that nobody waits for.

Shell; adapt to your CI system and backend configuration.

# merge request pipeline: fast feedback on the change itself
terraform init -input=false
terraform plan -input=false -minimal-refresh -out=change.tfplan

# nightly pipeline: full refresh, fail when real infrastructure drifted from state
terraform plan -input=false -refresh-only -detailed-exitcode
# exit code 0 = no drift, 2 = drift found (open an issue), 1 = error

Apply from the saved plan file of the merge request so that what was reviewed is what runs, and treat a drift finding from the nightly job as a change to investigate before the next minimal-refresh plan builds on stale state.

An adoption plan

  1. Upgrade CI runners first and compare full plans from 1.16 and 1.17 on every workspace; nothing should change.
  2. Add -minimal-refresh to development and merge-request plans only, and schedule a full or refresh-only plan for each production workspace.
  3. Install the policy CLI and plugin in a runner image, write one policy with tests, and run it in an advisory mode before enforcing.
  4. Review existing policies that reason about updates and deletes, given the new create-plus-delete evaluation of replacements.
  5. Move centrally pinned provider versions to const variables only where several root modules share them.
  6. Watch for newly visible warnings from ephemeral resource renewal and for configurations that relied on pow or log returning infinity.

Failure modes

  • A pipeline that only ever runs minimal-refresh plans, so out-of-band changes stay invisible until they cause an outage.
  • Enabling -policies in CI without the plugin on the runner, which fails every plan.
  • Policies that assumed replacements were updates, now blocking routine replacements or missing their delete side.
  • Provider version variables without const, which fail at init with an error that names the reason.
  • Treating the CLI GA statement as covering every HCP Terraform policy feature without checking its documentation.

Verification

For each workspace, keep one job that runs a full plan on a schedule and fails on unexpected changes. Add a canary resource that you change out of band in a test workspace, and check that the full plan reports it while a minimal-refresh plan, by design, does not. For policies, run the policy tests in CI and record which version of the plugin evaluated them.

Sources and scope

Checked against these sources on 22 October 2026. Event: Terraform v1.17.0 release, 21 October 2026; recheck it before acting. Examples and figures are illustrative, not measurements.

Use minimal refresh for fast plans but keep a scheduled full plan for drift, install the policy plugin before enabling -policies, review policies for create-plus-delete replacements, and mark provider version variables const.