provider-actions by hashicorp/agent-skills
npx skills add https://github.com/hashicorp/agent-skills --skill provider-actionsTerraform Actions 允许在 Terraform 生命周期中执行命令式操作。Actions 是实验性功能,支持在特定的生命周期事件(创建、更新、销毁之前/之后)执行 Provider 操作。
参考文档:
Actions 遵循标准的服务包结构:
internal/service/<service>/
├── <action_name>_action.go # Action 实现
├── <action_name>_action_test.go # Action 测试
└── service_package_gen.go # 自动生成的服务注册
文档结构:
website/docs/actions/
└── <service>_<action_name>.html.markdown # 面向用户的文档
变更日志条目:
.changelog/
└── <pr_number_or_description>.txt # 发布说明条目
Actions 使用 Terraform Plugin Framework,遵循标准的 Schema 模式:
func (a *actionType) Schema(ctx context.Context, req action.SchemaRequest, resp *action.SchemaResponse) {
resp.Schema = schema.Schema{
Attributes: map[string]schema.Attribute{
// 必需的配置参数
"resource_id": schema.StringAttribute{
Required: true,
Description: "要操作的资源 ID",
},
// 带有默认值的可选参数
"timeout": schema.Int64Attribute{
Optional: true,
Description: "操作超时时间(秒)",
Default: int64default.StaticInt64(1800),
Computed: true,
},
},
}
}
广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
请特别注意 Schema 的定义 - 初稿后常见的问题:
类型不匹配
types.String 而不是 fwtypes.Stringtypes.StringType 而不是 fwtypes.StringTypeList/Map 元素类型
// 错误 - 缺少 ElementType
"items": schema.ListAttribute{
Optional: true,
}
// 正确
"items": schema.ListAttribute{
Optional: true,
ElementType: fwtypes.StringType,
}
Computed 与 Optional
Optional: true 和 Computed: trueComputed验证器导入
// 确保正确导入
"github.com/hashicorp/terraform-plugin-framework-validators/int64validator"
"github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
Region/Provider 属性
嵌套属性
提交前,请验证:
go build 以捕获类型不匹配Invoke 方法包含 Action 的逻辑:
func (a *actionType) Invoke(ctx context.Context, req action.InvokeRequest, resp *action.InvokeResponse) {
var data actionModel
resp.Diagnostics.Append(req.Config.Get(ctx, &data)...)
// 创建 Provider 客户端
conn := a.Meta().Client(ctx)
// 为长时间运行的操作提供进度更新
resp.Progress.Set(ctx, "开始操作...")
// 实现带有错误处理的 Action 逻辑
// 使用 context 进行超时管理
// 如果是异步操作,则轮询完成状态
resp.Progress.Set(ctx, "操作完成")
}
resp.SendProgress(action.InvokeProgressEvent{...}) 进行实时更新context.WithTimeout()resp.Diagnostics.AddError() 添加诊断信息错误处理示例:
// 处理特定错误
var notFound *types.ResourceNotFoundException
if errors.As(err, ¬Found) {
resp.Diagnostics.AddError(
"资源未找到",
fmt.Sprintf("未找到资源 %s", resourceID),
)
return
}
// 通用错误处理
resp.Diagnostics.AddError(
"操作失败",
fmt.Sprintf("无法为 %s 完成操作:%s", resourceID, err),
)
a.Meta().<Service>Client(ctx) 的 Provider SDK 客户端对于需要等待完成的操作:
result, err := wait.WaitForStatus(ctx,
func(ctx context.Context) (wait.FetchResult[*ResourceType], error) {
// 获取当前状态
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("状态:%s,已用时间:%v", fr.Status, meta.Elapsed.Round(time.Second)),
})
},
},
)
Actions 通过 Terraform 配置中的 action_trigger 生命周期块调用:
action "provider_service_action" "name" {
config {
parameter = value
}
}
resource "terraform_data" "trigger" {
lifecycle {
action_trigger {
events = [after_create]
actions = [action.provider_service_action.name]
}
}
}
Terraform 1.14.0 支持的事件:
before_create - 资源创建之前after_create - 资源创建之后before_update - 资源更新之前after_update - 资源更新之后Terraform 1.14.0 不支持:
before_destroy - 不可用(将导致验证错误)after_destroy - 不可用(将导致验证错误)func 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"),
),
},
},
})
}
添加清理函数以清理测试资源:
func sweepResources(region string) error {
ctx := context.Background()
client := /* get client for region */
input := &service.ListInput{
// 过滤测试资源
}
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
// 跳过非测试资源
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()
}
服务特定的先决条件
错误模式匹配
regexache.MustCompile(\(?s)Error Title.*key phrase)不适用于 Actions 的测试模式
编译测试以检查错误:
go test -c -o /dev/null ./internal/service/<service>
运行特定的 Action 测试:
TF_ACC=1 go test ./internal/service/<service> -run TestAccServiceAction_ -v
运行清理以清理测试资源:
TF_ACC=1 go test ./internal/service/<service> -sweep=<region> -v
每个 Action 文档文件必须包含:
前置信息
---
subcategory: "Service Name"
layout: "provider"
page_title: "Provider: provider_service_action"
description: |-
对该 Action 功能的简要描述。
---
带有警告的页眉
使用示例
terraform_data 的基于触发器的示例参数参考
文档格式化检查
terrafmt fmtterrafmt diff 验证在 .changelog/ 目录中创建变更日志条目:
.changelog/<pr_number_or_description>.txt
内容格式:
action/provider_service_action: Action 的简要描述
提交 Action 实现前:
go build -o /dev/null .go test -c -o /dev/null ./internal/service/<service>make fmtterrafmt fmt website/docs/actions/<action>.html.markdown每周安装量
631
代码仓库
GitHub Stars
481
首次出现
2026 年 1 月 26 日
安全审计
安装于
github-copilot515
opencode503
codex496
gemini-cli490
cursor434
amp433
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:
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
Actions use the Terraform Plugin Framework with a standard schema pattern:
func (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,
},
},
}
}
Pay special attention to the schema definition - common issues after a first draft:
Type Mismatches
types.String instead of fwtypes.String in model structstypes.StringType instead of fwtypes.StringType in schemaList/Map Element Types
// WRONG - missing ElementType
"items": schema.ListAttribute{
Optional: true,
}
// CORRECT
"items": schema.ListAttribute{
Optional: true,
ElementType: fwtypes.StringType,
}
Computed vs Optional
Optional: true and Computed: trueBefore submitting, verify:
go build to catch type mismatchesThe Invoke method contains the action logic:
func (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")
}
resp.SendProgress(action.InvokeProgressEvent{...}) for real-time updatescontext.WithTimeout() for API callsresp.Diagnostics.AddError()Example error handling:
// Handle specific errors
var notFound *types.ResourceNotFoundException
if errors.As(err, ¬Found) {
resp.Diagnostics.AddError(
"Resource Not Found",
fmt.Sprintf("Resource %s was not found", resourceID),
)
return
}
// Generic error handling
resp.Diagnostics.AddError(
"Operation Failed",
fmt.Sprintf("Could not complete operation for %s: %s", resourceID, err),
)
a.Meta().<Service>Client(ctx)For operations that require waiting for completion:
result, 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)),
})
},
},
)
Actions are invoked via action_trigger lifecycle blocks in Terraform configurations:
action "provider_service_action" "name" {
config {
parameter = value
}
}
resource "terraform_data" "trigger" {
lifecycle {
action_trigger {
events = [after_create]
actions = [action.provider_service_action.name]
}
}
}
Terraform 1.14.0 Supported Events:
before_create - Before resource creationafter_create - After resource creationbefore_update - Before resource updateafter_update - After resource updateNot Supported in Terraform 1.14.0:
before_destroy - Not available (will cause validation error)after_destroy - Not available (will cause validation error)func 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"),
),
},
},
})
}
Add sweep functions to clean up test resources:
func 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()
}
Service-Specific Prerequisites
Error Pattern Matching
regexache.MustCompile(\(?s)Error Title.*key phrase)Test Patterns Not Applicable to Actions
Compile test to check for errors:
go test -c -o /dev/null ./internal/service/<service>
Run specific action tests:
TF_ACC=1 go test ./internal/service/<service> -run TestAccServiceAction_ -v
Run sweep to clean up test resources:
TF_ACC=1 go test ./internal/service/<service> -sweep=<region> -v
Each action documentation file must include:
Front Matter
---
subcategory: "Service Name"
layout: "provider"
page_title: "Provider: provider_service_action"
description: |-
Brief description of what the action does.
---
Header with Warnings
Example Usage
terraform_dataArgument Reference
Documentation Linting
terrafmt fmt before submissionCreate a changelog entry in .changelog/ directory:
.changelog/<pr_number_or_description>.txt
Content format:
action/provider_service_action: Brief description of the action
Before submitting your action implementation:
go build -o /dev/null .go test -c -o /dev/null ./internal/service/<service>make fmtterrafmt fmt website/docs/actions/<action>.html.markdownWeekly Installs
631
Repository
GitHub Stars
481
First Seen
Jan 26, 2026
Security Audits
Gen Agent Trust HubPassSocketPassSnykPass
Installed on
github-copilot515
opencode503
codex496
gemini-cli490
cursor434
amp433
Computed unless they have defaultsValidator Imports
// Ensure proper imports
"github.com/hashicorp/terraform-plugin-framework-validators/int64validator"
"github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
Region/Provider Attribute
Nested Attributes
terrafmt diff