diff --git a/internal/terraform/context_plan_actions_test.go b/internal/terraform/context_plan_actions_test.go index 3df836b8fb..75691d234f 100644 --- a/internal/terraform/context_plan_actions_test.go +++ b/internal/terraform/context_plan_actions_test.go @@ -4088,7 +4088,39 @@ resource "test_object" "a" { if !diags.HasErrors() { t.Fatal("expected errors, got success!") } - expectedErr := "action_trigger actions references non-existent action: The lifecycle action_trigger actions list contains a reference to the action \"action.test_action.hello\" that does not exist in the configuration of this module. This can likely be a typo." + expectedErr := "action_trigger actions references non-existent action: The lifecycle action_trigger actions list contains a reference to the action \"action.test_action.hello\" that does not exist in the configuration of this module." + if diags.Err().Error() != expectedErr { + t.Fatalf("wrong error!, got %q, expected %q", diags.Err().Error(), expectedErr) + } +} + +func TestContextPlan_validateActionInTriggerExistsWithSimilarAction(t *testing.T) { + // this validation occurs during TransformConfig + module := ` +action "test_action" "hello_word" {} + +resource "test_object" "a" { + lifecycle { + action_trigger { + events = [after_create] + actions = [action.test_action.hello_world] + } + } +} +` + m := testModuleInline(t, map[string]string{"main.tf": module}) + p := simpleMockProvider() + ctx := testContext2(t, &ContextOpts{ + Providers: map[addrs.Provider]providers.Factory{ + addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), + }, + }) + + _, diags := ctx.Plan(m, nil, DefaultPlanOpts) + if !diags.HasErrors() { + t.Fatal("expected errors, got success!") + } + expectedErr := "action_trigger actions references non-existent action: The lifecycle action_trigger actions list contains a reference to the action \"action.test_action.hello_world\" that does not exist in the configuration of this module. Did you mean \"action.test_action.hello_word\"?" if diags.Err().Error() != expectedErr { t.Fatalf("wrong error!, got %q, expected %q", diags.Err().Error(), expectedErr) } diff --git a/internal/terraform/transform_config.go b/internal/terraform/transform_config.go index 051d712b06..ddaecbe14d 100644 --- a/internal/terraform/transform_config.go +++ b/internal/terraform/transform_config.go @@ -6,12 +6,15 @@ package terraform import ( "fmt" "log" + "maps" + "slices" "github.com/hashicorp/hcl/v2" "github.com/hashicorp/terraform/internal/addrs" "github.com/hashicorp/terraform/internal/configs" "github.com/hashicorp/terraform/internal/dag" + "github.com/hashicorp/terraform/internal/didyoumean" "github.com/hashicorp/terraform/internal/lang/langrefs" "github.com/hashicorp/terraform/internal/tfdiags" ) @@ -190,10 +193,15 @@ func (t *ConfigTransformer) transformSingle(g *Graph, config *configs.Config) er _, ok := allConfigActions[configAction.String()] if !ok { + suggestion := didyoumean.NameSuggestion(configAction.String(), slices.Collect(maps.Keys(allConfigActions))) + if suggestion != "" { + suggestion = fmt.Sprintf(" Did you mean %q?", suggestion) + } + diags = diags.Append(&hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "action_trigger actions references non-existent action", - Detail: fmt.Sprintf("The lifecycle action_trigger actions list contains a reference to the action %q that does not exist in the configuration of this module. This can likely be a typo.", configAction.String()), + Detail: fmt.Sprintf("The lifecycle action_trigger actions list contains a reference to the action %q that does not exist in the configuration of this module.%s", configAction.String(), suggestion), Subject: action.Expr.Range().Ptr(), Context: r.DeclRange.Ptr(), })