为 Terraform AWS Provider 添加新 Data Source 的完整实战指南
【免费下载链接】terraform-provider-awsThe AWS Provider enables Terraform to manage AWS resources.项目地址: https://gitcode.com/GitHub_Trending/te/terraform-provider-aws
本篇指南以 Terraform AWS Provider(terraform-provider-aws)仓库中的官方开发文档docs/add-a-new-datasource.md为骨架,系统讲解如何为 AWS 新增的服务或新功能贡献一个全新的 Data Source(数据源)。你将掌握从创建功能分支、借助skaff脚手架生成代码、填写 Schema、实现 Read 处理器、通过注解注册、编写验收测试到提交 Pull Request 的完整开发链路,并理解数据源与 AWS API 之间「Terraform 数据模型 ↔ API 响应」的映射原理。
何时需要新增 Data Source
当 AWS 推出一个全新的服务,或在既有服务中新增了需要实践者(practitioner)在配置中查询该类既有资源的能力时,Provider 就需要新增 Data Source。例如查询现有 VPC 端点、获取某个订阅信息、读取 Capacity Block 报价等场景。
一个重要的判断标准是:任何拥有Describe或Get端点的 API 都可以做成 Data Source,但并不是所有都值得做——有些比另一些更有用。因此提交之前应当评估该数据源对实践者的实际查询价值。
此外,每个 Data Source 都应独立提交评审。包含多个数据源和/或资源的 Pull Request 会显著增加评审难度,维护者通常会要求将其拆分开来。
前置条件:确保 Service Client 已就绪
如果这是新服务的第一个 Data Source,请务必先确认该服务的 Service Client(服务客户端)已经添加并合并进主干分支。也就是说,新服务的接入是数据源开发的前置依赖,具体接入步骤参见 Adding a new Service(新增服务指南)。
新增 Data Source 的整体流程
| 步骤 | 动作 | 关键产出 |
|---|---|---|
| 1 | Fork 仓库并创建功能分支 | 分支f-{datasource name} |
| 2 | 命名并用skaff生成脚手架 | <name>_data_source.go、<name>_data_source_test.go、网站文档 |
| 3 | 填写 Schema | Schema方法中的Attributes与Blocks |
| 4 | 实现 Read 处理器 | AWS API 响应 → 数据源模型 |
| 5 | 注册数据源 | @FrameworkDataSource注解 +go generate |
| 6 | 编写验收测试 | TestAcc...DataSource_basic等 |
| 7 | 填写文档 | website/docs/d/<service>_<name>.html.markdown |
| 8 | 本地格式与 Lint 检查 | make fmt、make lint等全部通过 |
| 9 | 提交 Pull Request 并等待优先级排序 | PR 进入 triage |
第一步:Fork 仓库并创建功能分支
对 Terraform AWS Provider 的开发贡献遵循标准的 GitHub 协作流程。克隆仓库后,为新的数据源创建以f-为前缀的功能分支,例如:
git checkout -b f-vpc_endpoint分支命名、提交规范与 PR 流程的更多细节参见 Raising a Pull Request(提交 PR 指南)。
第二步:命名规则与 skaff 脚手架
命名规则
命名一旦被实践者使用,就只能通过破坏性变更才能修改,因此在动手前必须谨慎。核心命名规则(详见 Naming Conventions(命名规范)):
- HCL 配置名:
aws_<service_identifier>_<name>,三段用下划线连接且全小写。例如aws_ec2_capacity_block_offering。 - Go 函数名:主构造函数命名为
DataSource<ResourceName>(),使用 Mixed Caps(首字母缩略词保持正确大小写,如VPCEndpoint而非VpcEndpoint),不包含服务名。 - Go 文件名:位于
internal/service/<service>/目录下,命名为<name>_data_source.go(多词使用 snake case)。 - 文档文件名:数据源文档放在
website/docs/d/目录,命名<service>_<name>.html.markdown,全小写、不含aws前缀,例如accessanalyzer_analyzer.html.markdown。 - 服务标识符(Service Identifier):通常与 AWS Go SDK v2 的包名及 AWS CLI v2 命令保持一致,全部小写且不含下划线;二者冲突时取较短者。
使用 skaff 生成脚手架
强烈建议使用skaff工具生成数据源及其测试模板。它能自动完成样板代码、结构性最佳实践和重复性命名,并且始终代表仓库当前最新的标准。从 skaff 使用指南 可以看到完整的工作流:
- 安装
skaff:make skaff - 切换到目标服务的目录:
cd internal/service/<service> - 生成脚手架,例如:
skaff datasource --name IAMRole
skaff datasource子命令支持以下参数:
| 参数 | 说明 |
|---|---|
-n, --name | 实体名称(必须为正确的大写驼峰形式,如DBInstance,不能全小写) |
-s, --snakename | 当 skaff 推断不正确时显式指定 snake case 名称(如db_vpc_instance) |
-t, --include-tags | 该数据源具有标签,生成标签处理代码 |
-c, --clear-comments | 不生成教学性 TIP 注释 |
-f, --force | 强制创建,覆盖已存在的文件 |
从 skaff/datasource/datasource.go 的实现可以看到生成的三个文件:
<snake_name>_data_source.go(Go 源码)<snake_name>_data_source_test.go(测试源码)website/docs/d/<service_package>_<snake_name>.html.markdown(网站文档)
skaff会从names包的服务数据中查找当前目录对应的服务包,自动填充 HCL 资源名(ProviderResourceName)、ARN 命名空间(ARNNamespace)、人类可读名称(HumanDataSourceName)等模板字段。注意它的名称校验:--name必须是大写驼峰(否则报错name should be properly capitalized (e.g., DBInstance)),--snakename必须全小写下划线形式;未提供 snake name 时会自动通过names.ToSnakeCase转换。
第三步:填写 Schema
生成的文件位于internal/service/<service>/<name>_data_source.go,其中Schema方法定义了数据源的Attributes(属性)与Blocks(块)。Schema 将 Terraform 数据模型映射到 AWS API,在大多数情况下应当与 API 精确对应。
数据类型与命名
- 为每个 API 属性添加对应的 attribute 或 block,并选择正确的数据类型(字符串、整型、布尔、列表、嵌套对象等)。
- 属性名使用
snake_case,而 AWS API 是CamelCase。 - 与 Schema 定义严格对应的模型结构体名为
<name>DataSourceModel(由skaff命名),其字段通过tfsdk:"..."标签与属性名一一对应。
纯 Computed 的对象列表
对于只包含Computed属性的对象,必须使用框架辅助函数framework.DataSourceComputedListOfObjectAttribute。其源码表明它返回一个仅Computed的schema.ListAttribute:
func DataSourceComputedListOfObjectAttributeT any schema.ListAttribute { return schema.ListAttribute{ CustomType: fwtypes.NewListNestedObjectTypeOfT, Computed: true, ElementType: types.ObjectType{ AttrTypes: fwtypes.AttributeTypesMustT, }, } }注意:Terraform 协议 V6 不支持完全 Computed 的块(block),而 AWS Provider 将在未来某个大版本中采用协议 V6,因此此类场景统一使用该辅助函数而非块。
结构体嵌入与模型验证
skaff生成的 Data Source 结构体嵌入了framework.DataSourceWithModel[exampleDataSourceModel]。该框架类型组合了withModel[T]与DataSourceWithConfigure,提供ValidateModel能力,用于将模型与 Schema 校验对齐。模型结构体通常还嵌入framework.WithRegionModel以携带区域信息。
真实示例:EC2 Capacity Block Offering
仓库中的真实实现 internal/service/ec2/ec2_capacity_block_offering_data_source.go 展示了典型 Schema 写法:查询参数(capacity_duration_hours、instance_count、instance_type)设为Required,返回值(availability_zone、currency_code、tenancy、upfront_fee等)设为Computed,可选过滤参数(start_date_range、end_date_range)设为Optional+Computed,并使用framework.IDAttribute()作为 ID 属性。
第四步:实现 Read 处理器
Data Source 只有Read方法(没有 Create/Update/Delete)。其职责是把 AWS API 响应转换到数据源模型,同时处理不同的响应类型与错误。根据skaff生成的模板注释,Read方法的标准流程为:
- 获取相关服务的客户端连接,例如
conn := d.Meta().<Service>Client(ctx); - 从配置中读取用户输入,反序列化到模型:
req.Config.Get(ctx, &data); - 调用 finder/API 查询 AWS 资源信息;
- 将查询结果映射回模型,设置 ID、参数和属性;
- (如有标签)设置标签;
- 将模型写入状态:
resp.State.Set(ctx, &data)。
AutoFlex 自动转换
在大多数情况下,AutoFlex(Data Handling and Conversion Guide 中的推荐实现) 可以在 Terraform 与 AWS 数据类型之间自动转换,无需自定义处理。真实示例中体现为双向转换:
// 输入转换:模型 → API 请求 response.Diagnostics.Append(fwflex.Expand(ctx, data, &input)...) // 输出转换:API 响应 → 模型 response.Diagnostics.Append(fwflex.Flatten(ctx, output, &data)...)skaff生成的模板还会使用flex.Flatten(ctx, out, &data, flex.WithFieldNamePrefix("<DataSource>"))这类带字段名前缀的展开方式,将诸如<X>Id的 API 字段映射到ID。关于 AWS API 响应与 Terraform State 之间双向映射的完整知识,参见 Data Handling and Conversion Guide(数据处理与转换指南);关于错误的一致化处理,参见 Error Handling Guide(错误处理指南)——生成代码中通过smerr.AddEnrich/smerr.AddError统一收集诊断信息,确保错误信息一致、可追溯。
第五步:注册 Data Source
数据源采用自注册机制:在数据源代码的注释中使用@FrameworkDataSource()注解,即可将数据源加入 Provider。生成模板中的注册代码形如:
package something import ( "context" "github.com/hashicorp/terraform-plugin-framework/datasource" "github.com/hashicorp/terraform-provider-aws/internal/framework" ) // @FrameworkDataSource("aws_something_example", name="Example") func newExampleDataSource(_ context.Context) (datasource.DataSourceWithConfigure, error) { return &exampleDataSource{}, nil } type exampleDataSource struct { framework.DataSourceWithModel[exampleDataSourceModel] } type exampleDataSourceModel struct { // Fields corresponding to attributes in the Schema. }注解的name=参数是人类可读名称,注册时用于生成数据源名常量。写完注解后,在服务包目录执行:
go generate ./internal/service/<service>该命令会自动把数据源注册条目写入服务包下的service_package_gen.go文件。以 internal/service/amplify/service_package_gen.go 为例,生成后的文件(头部标注DO NOT EDIT)会包含FrameworkDataSources方法,返回[]*inttypes.ServicePackageFrameworkDataSource注册表,Provider 由此完成数据源装载。
真实示例中注解与构造函数成对出现(见 ec2_capacity_block_offering_data_source.go):
// @FrameworkDataSource("aws_ec2_capacity_block_offering", name="Capacity Block Offering") func newCapacityBlockOfferingDataSource(_ context.Context) (datasource.DataSourceWithConfigure, error) { d := &capacityBlockOfferingDataSource{} return d, nil }第六步:编写通过的验收测试
为了充分测试数据源,需要编写一套完整的验收测试(Acceptance Tests)。这些测试会真实调用 AWS 服务,因此你需要一个允许 Provider 读取关联资源的 AWS 账户。详细的编写方法参见 Writing Acceptance Tests(验收测试编写指南)。
最低测试要求
- Basic 测试:使用最小配置(包含全部必需字段、不含可选字段),这是每个数据源的底线。
- 可选参数测试:如果数据源支持额外可选参数(例如过滤器 filters),则必须为每个可选参数增加测试进行验证。
测试命名规范
验收测试遵循TestAcc<Service><DataSource>DataSource_<identifier>的命名模式(详见 命名规范)。skaff生成的测试模板(datasourcetest.gtpl)包含:
TestAcc<Service><DataSource>DataSource_basic基础验收测试,使用acctest.ParallelTest并行执行;PreCheck中调用acctest.PreCheckPartitionHasService检查当前分区是否提供该服务;ErrorCheck与ProtoV5ProviderFactories配置;Check中使用resource.TestCheckResourceAttr、resource.TestCheckResourceAttrSet、resource.TestCheckTypeSetElemNestedAttrs以及acctest.MatchResourceAttrRegionalARN(带正则的 ARN 断言)等校验函数;- 测试配置函数形如
testAcc<DataSource>DataSourceConfig_basic(rName),返回包含data "aws_<service>_<name>" "test"的 Terraform HCL 字符串。
模板还包含单元测试示例(Test<DataSource>ExampleUnitTest):单元测试不访问 AWS、快速且廉价,适合将实现中的复杂逻辑隔离出来进行验证。
第七步:填写文档
skaff会自动在website/docs/d/<service>_<name>.html.markdown生成新数据源的文档骨架(模板见 websitedoc.gtpl),包含:
- front matter:
subcategory、layout: "aws"、page_title、description; - 正文:
# Data Source: aws_<service>_<name>标题与一句式描述(以 "Provides details about..." 开头); ## Example Usage使用示例;## Argument Reference(区分必需参数与可选参数);## Attribute Reference(导出的属性列表,如arn、tags)。
文档的参数引用与属性引用必须与 Schema 定义保持一致。如果数据源特别复杂,或依赖另一个服务的资源,可以增加更多示例。当某个取值很可能随 AWS 变化时,链接到 AWS 官方文档也是允许的做法。该文档会在数据源随 Provider 版本发布后,出现在 Terraform Registry 上供所有实践者查阅。
第八步:本地格式与 Lint 检查
提交前必须在本地通过全部格式与静态检查:
make fmt # 格式化代码 make tools # 安装 linters 与依赖 make lint # 运行 Provider 相关 linters make docs-lint # 运行文档 linters make website-lint # 运行网站文档 linters其中make tools首次需要安装各类 linter 与依赖(相关 target 定义在仓库根目录的 GNUmakefile 中),make lint等命令会检测实现与文档中的结构性问题和命名违规(如 Mixed Caps、缩略词大小写等,由 CI 中 Semgrep 测试强制)。
第九步:提交 Pull Request 并等待优先级排序
- Raise a Pull Request:提交规范参见 Raising a Pull Request(提交 PR 指南)。请确保 PR 只包含单一数据源,便于评审。
- Wait for Prioritization:一般来说,Pull Request 会在创建后几天内被 triage(分类),并根据社区反馈进行优先级排序。完整的优先级排序流程参见 prioritization(优先级指南)。
总结:新增 Data Source 自检清单
- 新服务是否已接入 Service Client?(参见 add-a-new-service)
- 分支名是否形如
f-{datasource name}? - 是否使用
skaff datasource生成脚手架,并清除了全部 TIP 注释? - 命名是否符合规范:HCL 名、Go 函数名、文件名、文档文件名?(参见 naming.md)
- Schema 是否与 AWS API 精确对应,纯 Computed 对象列表是否使用了
DataSourceComputedListOfObjectAttribute? - Read 处理器是否正确使用 AutoFlex 与统一错误处理?(参见>是否已通过
@FrameworkDataSource注解并运行go generate ./internal/service/<service>完成注册? - 是否包含 Basic 验收测试与全部可选参数测试?(参见 running-and-writing-acceptance-tests)
- 网站文档参数引用是否与 Schema 一致?
make fmt、make lint、make docs-lint、make website-lint是否全部通过?- PR 是否独立提交、便于评审?
按照上述九个步骤,即可为 Terraform AWS Provider 贡献一个符合项目最新工程标准的全新 Data Source,为 Terraform 生态中的 AWS 资源查询能力添砖加瓦。
【免费下载链接】terraform-provider-awsThe AWS Provider enables Terraform to manage AWS resources.项目地址: https://gitcode.com/GitHub_Trending/te/terraform-provider-aws
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考