github cloudposse/atmos v1.217.0-rc.1

pre-releaseone hour ago

🚀 Enhancements

fix(output): stop after-* hooks from corrupting backend.tf.json when backend uses !terraform.state @zack-is-cool (#2358) ## Summary

Fixes #2356. The after-terraform-apply store hook path regenerated
backend.tf.json / providers_override.tf.json from un-rendered
component sections when the backend referenced !terraform.state,
overwriting a correctly-rendered file with literal YAML-function strings:

-        "bucket": "atmos-tfstate-dev",
-        "dynamodb_table": "atmos-tfstate-lock-dev",
+        "bucket": "!terraform.state tfstate-backend dev s3_bucket_id",
+        "dynamodb_table": "!terraform.state tfstate-backend dev dynamodb_table_name",

The hook then failed its tofu output call with:

Error: Backend initialization required: please run "tofu init"
Reason: Backend configuration block has changed

Why

Regression introduced in v1.216.0 by #2309 (commit 3c0e748ce) +
follow-up commit c7ef142a9 ("fix: skip-init should skip yaml function
evaluation"
). c7ef142a9 added a guard disabling YAML-function
evaluation when SkipInit && authManager == nil to avoid failing on
auth-requiring functions in the post-hook context. The guard is overly
broad — it also disables evaluation of non-auth functions like
!terraform.state — so sections returned from DescribeComponent retain
literal YAML-function strings. execute() then extracts config.Backend
from those sections and writes them to disk via GenerateBackendIfNeeded.

Fix

Thread processYamlFunctions bool through execute() in
pkg/terraform/output/executor.go and guard the artifact-regeneration
block (Step 4 / Step 5) behind it. When YAML functions were not
evaluated upstream, execute() must not regenerate artifacts from the
un-rendered sections. The backend file on disk from the init/apply phase
is already correct; leaving it alone is always safe. Output reading
(tofu output) still works via the on-disk state.

Minimal, localized diff — four commits:

  1. refactor(output): inject BackendGenerator and thread processYamlFunctions through execute() — pure DI plumbing, no behavior change.
  2. fix(output): skip artifact regeneration when YAML functions were not processed — the actual guard.
  3. test(output): assert backend-generator calls match processYamlFunctions in SkipInit tests — locks in the invariant in four existing SkipInit tests.
  4. test(output): regression test for #2356 backend.tf.json corruption — byte-identical integration assertion.

Test plan

  • New unit test TestExecutor_Execute_SkipsArtifactRegen_WhenYamlFunctionsNotProcessed (demonstrably red before the guard, green after).
  • Four existing SkipInit tests strengthened with zero-call expectations on the backend-generator mock.
  • Inverse assertion in TestExecutor_GetAllOutputs_SkipInit_WithAuthManager_ProcessesYamlFunctions: GenerateBackendIfNeeded + GenerateProvidersIfNeeded called exactly once when auth is present.
  • Integration regression test TestExecutor_Regression_Issue2356_BackendFileUnchangedInSkipInitPath: writes a rendered backend.tf.json, drives GetOutputWithOptions(SkipInit=true, authManager=nil), asserts the file is byte-identical. Fails without the guard; passes with it.
  • go test ./pkg/terraform/output/... -count=1 green.
  • make lint / ./custom-gcl run --new-from-rev=origin/main clean (one dupl warning on the new test vs the existing SkipInit test is suppressed with //nolint:dupl + justification — they test contrasting invariants at the same call site; extracting shared scaffolding would obscure the red/green comparison).
  • Manual end-to-end via LocalStack + Redis repro (ignore/issues/post-apply-hook-backend-racecondition/repro.sh in the branch, referenced from #2356). Exits 0 with FIX VERIFIED on this branch; backend file byte-diff is empty after the after-apply hook.
  • CI full suite — opening this PR runs it.

Follow-up

The processYamlFunctions = false guard in GetOutputWithOptions /
fetchAndCacheOutputs is the deeper design issue — auth availability
should not gate evaluation of non-auth YAML functions. Tracked in #2357.
This PR is the minimal regression fix for v1.216.x.

Release

  • fix: conventional commit → patch release (v1.216.1).
  • No schema changes, no user-facing config changes.
  • No roadmap update (regression fix, not a feature).

Summary by CodeRabbit

  • Bug Fixes

    • Backend and provider override files are regenerated only when YAML functions are processed, preventing unnecessary rewrites.
    • Fixed a case where skip-initialization could overwrite already-rendered backend/provider files, preserving existing configurations.
  • Tests

    • Added regression tests to ensure backend/provider files remain unchanged in the skip-initialization path and to validate correct conditional regeneration behavior.

Don't miss a new atmos release

NewReleases is sending notifications on new releases.