Merge pull request #172 from hashicorp/mutahhir/add-action-invocation

Add support for action invocation within Plan JSON
diff --git a/plan.go b/plan.go
index f60739c..579e8d7 100644
--- a/plan.go
+++ b/plan.go
@@ -92,6 +92,8 @@
 	// Timestamp contains the static timestamp that Terraform considers to be
 	// the time this plan executed, in UTC.
 	Timestamp string `json:"timestamp,omitempty"`
+
+	ActionInvocations []*ActionInvocation `json:"action_invocations,omitempty"`
 }
 
 // ResourceAttribute describes a full path to a resource attribute
@@ -140,15 +142,6 @@
 	return nil
 }
 
-func isStringInSlice(slice []string, s string) bool {
-	for _, el := range slice {
-		if el == s {
-			return true
-		}
-	}
-	return false
-}
-
 func (p *Plan) UnmarshalJSON(b []byte) error {
 	type rawPlan Plan
 	var plan rawPlan
@@ -305,3 +298,35 @@
 	// Change contains any information we have about the deferred change.
 	ResourceChange *ResourceChange `json:"resource_change,omitempty"`
 }
+
+type ActionInvocation struct {
+	// Address is the absolute action address
+	Address string `json:"address,omitempty"`
+	// Type is the type of the action
+	Type string `json:"type,omitempty"`
+	// Name is the name of the action
+	Name string `json:"name,omitempty"`
+
+	// ConfigValues is the JSON representation of the values in the config block of the action
+	ConfigValues    interface{} `json:"config_values,omitempty"`
+	ConfigSensitive interface{} `json:"config_sensitive,omitempty"`
+	ConfigUnknown   interface{} `json:"config_unknown,omitempty"`
+
+	// ProviderName allows the property "type" to be interpreted unambiguously
+	// in the unusual situation where a provider offers a type whose
+	// name does not start with its own name, such as the "googlebeta" provider
+	// offering "google_compute_instance".
+	ProviderName string `json:"provider_name,omitempty"`
+
+	LifecycleActionTrigger *LifecycleActionTrigger `json:"lifecycle_action_trigger,omitempty"`
+	InvokeActionTrigger    *InvokeActionTrigger    `json:"invoke_action_trigger,omitempty"`
+}
+
+type LifecycleActionTrigger struct {
+	TriggeringResourceAddress string `json:"triggering_resource_address,omitempty"`
+	ActionTriggerEvent        string `json:"action_trigger_event,omitempty"`
+	ActionTriggerBlockIndex   int    `json:"action_trigger_block_index"`
+	ActionsListIndex          int    `json:"actions_list_index"`
+}
+
+type InvokeActionTrigger struct{}
diff --git a/plan_test.go b/plan_test.go
index c97f8f9..9da2ef6 100644
--- a/plan_test.go
+++ b/plan_test.go
@@ -136,6 +136,49 @@
 	}
 }
 
+func TestPlan_actionInvocations(t *testing.T) {
+	f, err := os.Open("testdata/actions/plan.json")
+	if err != nil {
+		t.Fatal(err)
+	}
+	defer f.Close()
+
+	var plan *Plan
+	if err := json.NewDecoder(f).Decode(&plan); err != nil {
+		t.Fatal(err)
+	}
+
+	if err := plan.Validate(); err != nil {
+		t.Fatal(err)
+	}
+
+	if len(plan.ActionInvocations) != 1 {
+		t.Fatalf("expected exactly 1 action invocation, got %d", len(plan.ActionInvocations))
+	}
+
+	expectedAction := []*ActionInvocation{
+		{
+			Address: "action.bufo_print.success",
+			Type:    "bufo_print",
+			Name:    "success",
+			ConfigValues: map[string]interface{}{
+				"color": nil,
+				"name":  "bufo-the-builder",
+				"ratio": nil,
+			},
+			ConfigSensitive:        map[string]interface{}{},
+			ConfigUnknown:          map[string]interface{}{},
+			ProviderName:           "registry.terraform.io/austinvalle/bufo",
+			LifecycleActionTrigger: nil,
+			InvokeActionTrigger:    &InvokeActionTrigger{},
+		},
+	}
+
+	if diff := cmp.Diff(expectedAction, plan.ActionInvocations); diff != "" {
+		t.Fatalf("unexpected action invocation: %s", diff)
+	}
+}
+
 func TestPlan_UnmarshalJSON(t *testing.T) {
 	t.Parallel()
 
@@ -168,7 +211,6 @@
 			plan.UseJSONNumber(testCase.useJSONNumber)
 
 			err = plan.UnmarshalJSON(b)
-
 			if err != nil {
 				t.Fatal(err)
 			}
diff --git a/testdata/actions/plan.json b/testdata/actions/plan.json
new file mode 100644
index 0000000..a3a2e36
--- /dev/null
+++ b/testdata/actions/plan.json
@@ -0,0 +1 @@
+{"format_version":"1.2","planned_values":{"root_module":{}},"complete":true,"configuration":{"provider_config":{"bufo":{"name":"bufo","full_name":"registry.terraform.io/austinvalle/bufo"}},"root_module":{}},"timestamp":"2025-09-12T09:56:02Z","action_invocations":[{"address":"action.bufo_print.success","type":"bufo_print","name":"success","config_values":{"color":null,"name":"bufo-the-builder","ratio":null},"config_sensitive":{},"config_unknown":{},"provider_name":"registry.terraform.io/austinvalle/bufo","invoke_action_trigger":{}}]}