From 46444353df6a7d7ca110e6ddee8c34cdebf7dd2a Mon Sep 17 00:00:00 2001 From: James Bardin Date: Wed, 4 Jun 2025 13:05:21 -0400 Subject: [PATCH 1/2] allow outputs without top-level ephemeral marks There is currently no way to construct an output value which does not come directly from an ephemeral resource or input. Even if the constructed value is entirely composed of ephemeral attributes or elements, because there is no action to manually mark something as ephemeral (other than a variable block), the resulting value itself is not marked as ephemeral, and fails validation. Instead of strictly checking for an ephemeral mark on the output value, we can accept any value because the result will always be entirely ephemeral anyway. This mirrors the variable block behavior, which can accept any type of value as input, but is evaluated as ephemeral. --- .../terraform/context_plan_ephemeral_test.go | 41 +++++++++++++++++++ internal/terraform/node_output.go | 13 +----- 2 files changed, 42 insertions(+), 12 deletions(-) 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", From e4da541017362513a6a79bef0056a68fad54a597 Mon Sep 17 00:00:00 2001 From: James Bardin Date: Wed, 4 Jun 2025 13:26:46 -0400 Subject: [PATCH 2/2] CHANGELOG --- .changes/v1.12/BUG FIXES-20250604-131240.yaml | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changes/v1.12/BUG FIXES-20250604-131240.yaml 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"