Install
Terminal · npx$
npx skills add https://github.com/hashicorp/agent-skills --skill provider-actionsWorks with Paperclip
How Provider Actions fits into a Paperclip company.
Provider Actions drops into any Paperclip agent that handles this kind of work. Assign it to a specialist inside a pre-configured PaperclipOrg company and the skill becomes available on every heartbeat — no prompt engineering, no tool wiring.
S
SaaS FactoryPaired
Pre-configured AI company — 18 agents, 18 skills, one-time purchase.
$27$59
Explore packSource file
SKILL.md478 linesExpandCollapse
---name: provider-actionsdescription: Implement Terraform Provider actions using the Plugin Framework. Use when developing imperative operations that execute at lifecycle events (before/after create, update, destroy).metadata: copyright: Copyright IBM Corp. 2026 version: "0.0.1"--- # Terraform Provider Actions Implementation Guide ## Overview 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). **References:**- [Terraform Plugin Framework](https://developer.hashicorp.com/terraform/plugin/framework)- [Terraform Actions RFC](https://github.com/hashicorp/terraform/blob/main/docs/plugin-protocol/actions.md) ## File Structure Actions follow the standard service package structure: ```internal/service/<service>/├── <action_name>_action.go # Action implementation├── <action_name>_action_test.go # Action tests└── service_package_gen.go # Auto-generated service registration``` Documentation structure:```website/docs/actions/└── <service>_<action_name>.html.markdown # User-facing documentation``` Changelog entry:```.changelog/└── <pr_number_or_description>.txt # Release note entry``` ## Action Schema Definition Actions use the Terraform Plugin Framework with a standard schema pattern: ```gofunc (a *actionType) Schema(ctx context.Context, req action.SchemaRequest, resp *action.SchemaResponse) { resp.Schema = schema.Schema{ Attributes: map[string]schema.Attribute{ // Required configuration parameters "resource_id": schema.StringAttribute{ Required: true, Description: "ID of the resource to operate on", }, // Optional parameters with defaults "timeout": schema.Int64Attribute{ Optional: true, Description: "Operation timeout in seconds", Default: int64default.StaticInt64(1800), Computed: true, }, }, }}``` ### Common Schema Issues **Pay special attention to the schema definition** - common issues after a first draft: 1. **Type Mismatches** - Using `types.String` instead of `fwtypes.String` in model structs - Using `types.StringType` instead of `fwtypes.StringType` in schema - Mixing framework types with plugin-framework types 2. **List/Map Element Types** ```go // WRONG - missing ElementType "items": schema.ListAttribute{ Optional: true, } // CORRECT "items": schema.ListAttribute{ Optional: true, ElementType: fwtypes.StringType, } ``` 3. **Computed vs Optional** - Attributes with defaults must be both `Optional: true` and `Computed: true` - Don't mark action inputs as `Computed` unless they have defaults 4. **Validator Imports** ```go // Ensure proper imports "github.com/hashicorp/terraform-plugin-framework-validators/int64validator" "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" ``` 5. **Region/Provider Attribute** - Use framework-provided region handling when available - Don't manually define provider-specific config in schema if framework handles it 6. **Nested Attributes** - Use appropriate nested object types for complex structures - Ensure nested types are properly defined ### Schema Validation Checklist Before submitting, verify:- [ ] All attributes have descriptions- [ ] List/Map attributes have ElementType defined- [ ] Validators are imported and applied correctly- [ ] Model struct uses correct framework types- [ ] Optional attributes with defaults are marked Computed- [ ] Code compiles without type errors- [ ] Run `go build` to catch type mismatches ## Action Invoke Method The Invoke method contains the action logic: ```gofunc (a *actionType) Invoke(ctx context.Context, req action.InvokeRequest, resp *action.InvokeResponse) { var data actionModel resp.Diagnostics.Append(req.Config.Get(ctx, &data)...) // Create provider client conn := a.Meta().Client(ctx) // Progress updates for long-running operations resp.Progress.Set(ctx, "Starting operation...") // Implement action logic with error handling // Use context for timeout management // Poll for completion if async operation resp.Progress.Set(ctx, "Operation completed")}``` ## Key Implementation Requirements ### 1. Progress Reporting - Use `resp.SendProgress(action.InvokeProgressEvent{...})` for real-time updates- Provide meaningful progress messages during long operations- Update progress at key milestones- Include elapsed time for long operations ### 2. Timeout Management - Always include configurable timeout parameter (default: 1800s)- Use `context.WithTimeout()` for API calls- Handle timeout errors gracefully- Validate timeout ranges (typically 60-7200 seconds) ### 3. Error Handling - Add diagnostics with `resp.Diagnostics.AddError()`- Provide clear error messages with context- Include API error details when relevant- Map provider error types to user-friendly messages- Document all possible error cases Example error handling:```go// Handle specific errorsvar notFound *types.ResourceNotFoundExceptionif errors.As(err, ¬Found) { resp.Diagnostics.AddError( "Resource Not Found", fmt.Sprintf("Resource %s was not found", resourceID), ) return} // Generic error handlingresp.Diagnostics.AddError( "Operation Failed", fmt.Sprintf("Could not complete operation for %s: %s", resourceID, err),)``` ### 4. Provider SDK Integration - Use provider SDK clients from `a.Meta().<Service>Client(ctx)`- Handle pagination for list operations- Implement retry logic for transient failures- Use appropriate error types ### 5. Parameter Validation - Use framework validators for input validation- Validate resource existence before operations- Check for conflicting parameters- Validate against provider naming requirements ### 6. Polling and Waiting For operations that require waiting for completion: ```goresult, err := wait.WaitForStatus(ctx, func(ctx context.Context) (wait.FetchResult[*ResourceType], error) { // Fetch current status resource, err := findResource(ctx, conn, id) if err != nil { return wait.FetchResult[*ResourceType]{}, err } return wait.FetchResult[*ResourceType]{ Status: wait.Status(resource.Status), Value: resource, }, nil }, wait.Options[*ResourceType]{ Timeout: timeout, Interval: wait.FixedInterval(5 * time.Second), SuccessStates: []wait.Status{"AVAILABLE", "COMPLETED"}, TransitionalStates: []wait.Status{"CREATING", "PENDING"}, ProgressInterval: 30 * time.Second, ProgressSink: func(fr wait.FetchResult[any], meta wait.ProgressMeta) { resp.SendProgress(action.InvokeProgressEvent{ Message: fmt.Sprintf("Status: %s, Elapsed: %v", fr.Status, meta.Elapsed.Round(time.Second)), }) }, },)``` ## Common Action Patterns ### Batch Operations- Process items in configurable batches- Report progress per batch- Handle partial failures gracefully- Support prefix/filter parameters ### Command Execution- Submit command and get operation ID- Poll for completion status- Retrieve and report output- Handle timeout during polling- Validate resources exist before execution ### Service Invocation- Invoke service with parameters- Wait for completion (if synchronous)- Return output/results- Handle service-specific errors ### Resource State Changes- Validate current state- Apply state change- Poll for target state- Handle transitional states ### Async Job Submission- Submit job with configuration- Get job ID- Optionally wait for completion- Report job status ## Action Triggers Actions are invoked via `action_trigger` lifecycle blocks in Terraform configurations: ```hclaction "provider_service_action" "name" { config { parameter = value }} resource "terraform_data" "trigger" { lifecycle { action_trigger { events = [after_create] actions = [action.provider_service_action.name] } }}``` ### Available Trigger Events **Terraform 1.14.0 Supported Events:**- `before_create` - Before resource creation- `after_create` - After resource creation- `before_update` - Before resource update- `after_update` - After resource update **Not Supported in Terraform 1.14.0:**- `before_destroy` - Not available (will cause validation error)- `after_destroy` - Not available (will cause validation error) ## Testing Actions ### Acceptance Tests - Test action invocation with valid parameters- Test timeout scenarios- Test error conditions- Verify provider state changes- Test progress reporting- Test with custom parameters- Test trigger-based invocation ### Test Pattern ```gofunc TestAccServiceAction_basic(t *testing.T) { ctx := acctest.Context(t) resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(ctx, t) }, ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, TerraformVersionChecks: []tfversion.TerraformVersionCheck{ tfversion.SkipBelow(tfversion.Version1_14_0), }, Steps: []resource.TestStep{ { Config: testAccActionConfig_basic(), Check: resource.ComposeTestCheckFunc( testAccCheckResourceExists(ctx, "provider_resource.test"), ), }, }, })}``` ### Test Cleanup with Sweep Functions Add sweep functions to clean up test resources: ```gofunc sweepResources(region string) error { ctx := context.Background() client := /* get client for region */ input := &service.ListInput{ // Filter for test resources } var sweeperErrs *multierror.Error pages := service.NewListPaginator(client, input) for pages.HasMorePages() { page, err := pages.NextPage(ctx) if err != nil { sweeperErrs = multierror.Append(sweeperErrs, err) continue } for _, item := range page.Items { id := item.Id // Skip non-test resources if !strings.HasPrefix(id, "tf-acc-test") { continue } _, err := client.Delete(ctx, &service.DeleteInput{ Id: id, }) if err != nil { sweeperErrs = multierror.Append(sweeperErrs, err) } } } return sweeperErrs.ErrorOrNil()}``` ### Testing Best Practices **Service-Specific Prerequisites**- Always check for service-specific prerequisites that must be met before actions can succeed- Document prerequisites in action documentation and test configurations **Error Pattern Matching**- Terraform wraps action errors with additional context- Use flexible regex patterns: `regexache.MustCompile(\`(?s)Error Title.*key phrase\`)` **Test Patterns Not Applicable to Actions**1. Actions trigger on lifecycle events, not config reapplication2. Before/After Destroy Tests: Not supported in Terraform 1.14.0 ### Running Tests Compile test to check for errors:```bashgo test -c -o /dev/null ./internal/service/<service>``` Run specific action tests:```bashTF_ACC=1 go test ./internal/service/<service> -run TestAccServiceAction_ -v``` Run sweep to clean up test resources:```bashTF_ACC=1 go test ./internal/service/<service> -sweep=<region> -v``` ## Documentation Standards Each action documentation file must include: 1. **Front Matter** ```yaml --- subcategory: "Service Name" layout: "provider" page_title: "Provider: provider_service_action" description: |- Brief description of what the action does. --- ``` 2. **Header with Warnings** - Beta/Alpha notice about experimental status - Warning about potential unintended consequences - Link to provider documentation 3. **Example Usage** - Basic usage example - Advanced usage with all options - Trigger-based example with `terraform_data` - Real-world use case examples 4. **Argument Reference** - List all required and optional arguments - Include descriptions and defaults - Note any validation rules 5. **Documentation Linting** - Run `terrafmt fmt` before submission - Verify with `terrafmt diff` ## Changelog Entry Format Create a changelog entry in `.changelog/` directory: ```.changelog/<pr_number_or_description>.txt``` Content format:```release-note:new-actionaction/provider_service_action: Brief description of the action``` ## Pre-Submission Checklist Before submitting your action implementation: - [ ] Code compiles: `go build -o /dev/null .`- [ ] Tests compile: `go test -c -o /dev/null ./internal/service/<service>`- [ ] Code formatted: `make fmt`- [ ] Documentation formatted: `terrafmt fmt website/docs/actions/<action>.html.markdown`- [ ] Changelog entry created- [ ] Schema uses correct types- [ ] All List/Map attributes have ElementType- [ ] Progress updates implemented for long operations- [ ] Error messages include context and resource identifiers- [ ] Documentation includes multiple examples- [ ] Documentation includes prerequisites and warnings ## References - [Terraform Plugin Framework Documentation](https://developer.hashicorp.com/terraform/plugin/framework)- [Terraform Provider Development](https://developer.hashicorp.com/terraform/plugin)- [terraform-plugin-framework GitHub](https://github.com/hashicorp/terraform-plugin-framework)- [terraform-plugin-testing](https://github.com/hashicorp/terraform-plugin-testing)Related skills