$npx -y skills add hashicorp/agent-skills --skill provider-actionsImplement Terraform Provider actions using the Plugin Framework. Use when developing imperative operations that execute at lifecycle events (before/after create, update, destroy).
| 1 | # Terraform Provider Actions Implementation Guide |
| 2 | |
| 3 | ## Overview |
| 4 | |
| 5 | Terraform Actions enable imperative operations during the Terraform lifecycle. Actions are experimental features that allow performing provider operations at specific lifecycle events (before/after create, update, destroy). |
| 6 | |
| 7 | **References:** |
| 8 | - [Terraform Plugin Framework](https://developer.hashicorp.com/terraform/plugin/framework) |
| 9 | - [Terraform Actions RFC](https://github.com/hashicorp/terraform/blob/main/docs/plugin-protocol/actions.md) |
| 10 | |
| 11 | ## First Action Setup |
| 12 | |
| 13 | When adding the first action to a provider that has never had one, several one-time scaffolding steps are required: |
| 14 | |
| 15 | 1. **Implement `ProviderWithActions`** — add an `Actions()` method to the provider that returns `[]func() action.Action`. |
| 16 | 2. **Set `ActionData` in `Configure`** — the provider's `Configure` method must set `resp.ActionData = v` alongside the existing `ResourceData`, `DataSourceData`, and `EphemeralResourceData` assignments. |
| 17 | 3. **Create `ActionWithConfigure` base type** — if the provider uses embedded base types (e.g. `ResourceWithConfigure`), create an equivalent `ActionWithConfigure` type implementing `action.ConfigureRequest` / `action.ConfigureResponse`. |
| 18 | 4. **Action-schema helper variants** — if the provider injects common schema attributes (e.g. `namespace`) via helper functions, action-schema variants are needed since `action/schema` types differ from `resource/schema` types. |
| 19 | |
| 20 | ## File Structure |
| 21 | |
| 22 | Actions follow the standard service package structure: |
| 23 | |
| 24 | ``` |
| 25 | internal/service/<service>/ |
| 26 | ├── <action_name>_action.go # Action implementation |
| 27 | ├── <action_name>_action_test.go # Action tests |
| 28 | └── service_package_gen.go # Auto-generated service registration |
| 29 | ``` |
| 30 | |
| 31 | Documentation structure: |
| 32 | ``` |
| 33 | website/docs/actions/ |
| 34 | └── <service>_<action_name>.html.markdown # User-facing documentation |
| 35 | ``` |
| 36 | |
| 37 | Changelog entry: |
| 38 | ``` |
| 39 | .changelog/ |
| 40 | └── <pr_number_or_description>.txt # Release note entry |
| 41 | ``` |
| 42 | |
| 43 | ## Action Schema Definition |
| 44 | |
| 45 | Actions use the Terraform Plugin Framework with a standard schema pattern: |
| 46 | |
| 47 | ```go |
| 48 | func (a *actionType) Schema(ctx context.Context, req action.SchemaRequest, resp *action.SchemaResponse) { |
| 49 | resp.Schema = schema.Schema{ |
| 50 | Attributes: map[string]schema.Attribute{ |
| 51 | // Required configuration parameters |
| 52 | "resource_id": schema.StringAttribute{ |
| 53 | Required: true, |
| 54 | Description: "ID of the resource to operate on", |
| 55 | }, |
| 56 | // Optional parameters with defaults |
| 57 | "timeout": schema.Int64Attribute{ |
| 58 | Optional: true, |
| 59 | Description: "Operation timeout in seconds", |
| 60 | Default: int64default.StaticInt64(1800), |
| 61 | Computed: true, |
| 62 | }, |
| 63 | }, |
| 64 | } |
| 65 | } |
| 66 | ``` |
| 67 | |
| 68 | ### Common Schema Issues |
| 69 | |
| 70 | **Pay special attention to the schema definition** - common issues after a first draft: |
| 71 | |
| 72 | 1. **Type Mismatches** |
| 73 | - Using `types.String` instead of `fwtypes.String` in model structs |
| 74 | - Using `types.StringType` instead of `fwtypes.StringType` in schema |
| 75 | - Mixing framework types with plugin-framework types |
| 76 | |
| 77 | 2. **List/Map Element Types** |
| 78 | ```go |
| 79 | // WRONG - missing ElementType |
| 80 | "items": schema.ListAttribute{ |
| 81 | Optional: true, |
| 82 | } |
| 83 | |
| 84 | // CORRECT |
| 85 | "items": schema.ListAttribute{ |
| 86 | Optional: true, |
| 87 | ElementType: fwtypes.StringType, |
| 88 | } |
| 89 | ``` |
| 90 | |
| 91 | 3. **Computed vs Optional** |
| 92 | - Attributes with defaults must be both `Optional: true` and `Computed: true` |
| 93 | - Don't mark action inputs as `Computed` unless they have defaults |
| 94 | |
| 95 | 4. **Validator Imports** |
| 96 | ```go |
| 97 | // Ensure proper imports |
| 98 | "github.com/hashicorp/terraform-plugin-framework-validators/int64validator" |
| 99 | "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" |
| 100 | ``` |
| 101 | |
| 102 | 5. **Region/Provider Attribute** |
| 103 | - Use framework-provided region handling when available |
| 104 | - Don't manually define provider-specific config in schema if framework handles it |
| 105 | |
| 106 | 6. **Nested Attributes** |
| 107 | - Use appropriate nested object types for complex structures |
| 108 | - Ensure nested types are properly defined |
| 109 | |
| 110 | ### Schema Validation Checklist |
| 111 | |
| 112 | Before submitting, verify: |
| 113 | - [ ] All attributes have descriptions |
| 114 | - [ ] List/Map attributes have ElementType defined |
| 115 | - [ ] Validators are imported and applied correctly |
| 116 | - [ ] Model struct uses correct framework types |
| 117 | - [ ] Optional attributes with defaults are marked Computed |
| 118 | - [ ] Code compiles without type errors |
| 119 | - [ ] Run `go build` to catch type mismatches |
| 120 | |
| 121 | ## Action Invoke Method |
| 122 | |
| 123 | The Invoke method contains the action logic: |
| 124 | |
| 125 | ```go |
| 126 | func (a *actionType) Invoke(ctx context.Context, req action.InvokeRequest, resp *action.InvokeResponse) { |
| 127 | var data |