diff --git a/.changes/v1.12/BUG FIXES-20250604-131240.yaml b/.changes/v1.12/BUG FIXES-20250604-131240.yaml new file mode 100644 index 0000000000..8c8cf6b428 --- /dev/null +++ b/.changes/v1.12/BUG FIXES-20250604-131240.yaml @@ -0,0 +1,5 @@ +kind: BUG FIXES +body: partial ephemeral values were rejected in ephemeral outputs +time: 2025-06-04T13:12:40.440242-04:00 +custom: + Issue: "37210" diff --git a/internal/terraform/context_plan_ephemeral_test.go b/internal/terraform/context_plan_ephemeral_test.go index dd234e0fef..7793b1b6d1 100644 --- a/internal/terraform/context_plan_ephemeral_test.go +++ b/internal/terraform/context_plan_ephemeral_test.go @@ -678,6 +678,47 @@ resource "ephem_write_only" "test" { }, }, }, + + "ad-hoc-ephemeral-output": { + module: map[string]string{ + "child/main.tf": ` +output "value" { + value = { + applying = terraform.applying + static = "test" + } + + // It's valid to assign any partially ephemeral value, the output will always be + // entirely ephemeral. + ephemeral = true +} +`, + "main.tf": ` +module "child" { + source = "./child" +} + +output "root" { + // We expect a diagnostic here indicating that this value is ephemeral. This + // ensures that the module output was valid and the ephemeral marks were + // correctly re-applied during evaluation. + value = module.child.value +} +`, + }, + expectPlanDiagnostics: func(m *configs.Config) (diags tfdiags.Diagnostics) { + return diags.Append(&hcl.Diagnostic{ + Severity: hcl.DiagError, + Summary: "Ephemeral value not allowed", + Detail: "This output value is not declared as returning an ephemeral value, so it cannot be set to a result derived from an ephemeral value.", + Subject: &hcl.Range{ + Filename: filepath.Join(m.Module.SourceDir, "main.tf"), + Start: hcl.Pos{Line: 10, Column: 13, Byte: 277}, + End: hcl.Pos{Line: 10, Column: 31, Byte: 295}, + }, + }) + }, + }, } { t.Run(name, func(t *testing.T) { m := testModuleInline(t, tc.module) diff --git a/internal/terraform/node_output.go b/internal/terraform/node_output.go index 46121f15c6..93f28d16c3 100644 --- a/internal/terraform/node_output.go +++ b/internal/terraform/node_output.go @@ -493,18 +493,7 @@ If you do intend to export this data, annotate the output value as sensitive by // "flagWarnOutputErrors", because they relate to features that were added // more recently than the historical change to treat invalid output values // as errors rather than warnings. - if n.Config.Ephemeral && !marks.Has(val, marks.Ephemeral) { - // An ephemeral output value must always be ephemeral - // This is to prevent accidental persistence upstream - // from here. - diags = diags.Append(&hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Value not allowed in ephemeral output", - Detail: "This output value is declared as returning an ephemeral value, so it can only be set to an ephemeral value.", - Subject: n.Config.Expr.Range().Ptr(), - }) - return diags - } else if !n.Config.Ephemeral && marks.Contains(val, marks.Ephemeral) { + if !n.Config.Ephemeral && marks.Contains(val, marks.Ephemeral) { diags = diags.Append(&hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Ephemeral value not allowed",