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.
pull/37210/head
James Bardin 11 months ago
parent 10f3524bc5
commit 46444353df

@ -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)

@ -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",

Loading…
Cancel
Save