From 9239dd62e94325be4e499c66b4f4a89a0813cf85 Mon Sep 17 00:00:00 2001 From: Liam Cervante Date: Wed, 8 Oct 2025 16:08:04 +0200 Subject: [PATCH] actions: enforce ephemeral into write-only attributes behaviour (#37701) * actions: add test that validates ephemeral behaviour * actuall prevent ephemeral values into non write-only attributes * remove old invalid test --- .../terraform/context_plan_actions_test.go | 46 +++++++++++++++++++ internal/terraform/node_action_instance.go | 9 +++- internal/terraform/node_action_validate.go | 3 ++ 3 files changed, 57 insertions(+), 1 deletion(-) diff --git a/internal/terraform/context_plan_actions_test.go b/internal/terraform/context_plan_actions_test.go index 22d1be5821..ecb6d4386a 100644 --- a/internal/terraform/context_plan_actions_test.go +++ b/internal/terraform/context_plan_actions_test.go @@ -1351,6 +1351,52 @@ resource "test_object" "a" { }, }, + "ephemeral values": { + module: map[string]string{ + "main.tf": ` +variable "secret" { + type = string + ephemeral = true +} +action "test_action" "hello" { + config { + attr = var.secret + } +} +resource "test_object" "a" { + lifecycle { + action_trigger { + events = [before_create] + actions = [action.test_action.hello] + } + } +} +`, + }, + planOpts: &PlanOpts{ + Mode: plans.NormalMode, + SetVariables: InputValues{ + "secret": &InputValue{ + Value: cty.StringVal("secret"), + SourceType: ValueFromCLIArg, + }}, + }, + expectPlanActionCalled: false, + assertValidateDiagnostics: func(t *testing.T, diags tfdiags.Diagnostics) { + if len(diags) != 1 { + t.Fatalf("expected exactly 1 diagnostic but had %d", len(diags)) + } + + if diags[0].Severity() != tfdiags.Error { + t.Error("expected error diagnostic") + } + + if diags[0].Description().Summary != "Invalid use of ephemeral value" { + t.Errorf("expected diagnostics to be because of ephemeral values but was %s", diags[0].Description().Summary) + } + }, + }, + "write-only attributes": { module: map[string]string{ "main.tf": ` diff --git a/internal/terraform/node_action_instance.go b/internal/terraform/node_action_instance.go index cec79da1d5..cfcc89338d 100644 --- a/internal/terraform/node_action_instance.go +++ b/internal/terraform/node_action_instance.go @@ -64,7 +64,14 @@ func (n *NodeActionDeclarationInstance) Execute(ctx EvalContext, _ walkOperation configVal, _, configDiags = ctx.EvaluateBlock(n.Config.Config, n.Schema.ConfigSchema.DeepCopy(), nil, keyData) diags = diags.Append(configDiags) - if diags.HasErrors() { + if configDiags.HasErrors() { + return diags + } + + valDiags := validateResourceForbiddenEphemeralValues(ctx, configVal, n.Schema.ConfigSchema) + diags = diags.Append(valDiags.InConfigBody(n.Config.Config, n.Addr.String())) + + if valDiags.HasErrors() { return diags } } diff --git a/internal/terraform/node_action_validate.go b/internal/terraform/node_action_validate.go index 8c36de6309..f80e2e204b 100644 --- a/internal/terraform/node_action_validate.go +++ b/internal/terraform/node_action_validate.go @@ -102,6 +102,9 @@ func (n *NodeValidatableAction) Execute(ctx EvalContext, _ walkOperation) tfdiag } } + valDiags = validateResourceForbiddenEphemeralValues(ctx, configVal, schema.ConfigSchema) + diags = diags.Append(valDiags.InConfigBody(config, n.Addr.String())) + // Use unmarked value for validate request unmarkedConfigVal, _ := configVal.UnmarkDeep() log.Printf("[TRACE] Validating config for %q", n.Addr)