Terraform AWS Provider 数据源 aws_ec2_hosts 完全指南:按条件批量查询 EC2 Dedicated Hosts
2026/9/18 18:36:06 网站建设 项目流程

Terraform AWS Provider 数据源 aws_ec2_hosts 完全指南:按条件批量查询 EC2 Dedicated Hosts

【免费下载链接】terraform-provider-awsThe AWS Provider enables Terraform to manage AWS resources.项目地址: https://gitcode.com/GitHub_Trending/te/terraform-provider-aws

本指南围绕 Terraform AWS Provider 提供的aws_ec2_hosts数据源展开,讲解如何通过filtertagsoutpost_arn等参数批量查询 EC2 Dedicated Hosts(专用主机)的 ID 列表,并深入源码层面剖析其底层实现原理(DescribeHostsAPI 调用、分页逻辑与客户端过滤机制)。读完本文,你将能够在 Terraform 配置中精准筛选专用主机,并理解该数据源在 terraform-provider-aws 仓库中的完整实现链路。

数据源概述:什么是aws_ec2_hosts

aws_ec2_hosts是一个框架(Plugin Framework)实现的数据源,其功能是返回一组与给定过滤器匹配的 EC2 Dedicated Host 的 ID 列表。它与单实例查询的数据源(如aws_ec2_host)不同,专为"批量发现"场景设计:例如你需要获取某个可用区、某个实例类型下所有可用状态的专用主机 ID,供后续资源引用或循环创建实例使用。

该数据源的官方说明位于 website/docs/d/ec2_hosts.html.markdown,其 frontmatter 中的描述为"Provides details about multiple EC2 Dedicated Hosts"。关于 Dedicated Host 的更多背景,可参考 AWS 官方 EC2 User Guide 中的 Dedicated Hosts 章节(文档内链接为外部站点,此处仅提示语义)。

示例用法

按实例类型过滤

最常见的用法是同时传入多个filter块,数据源会返回同时满足所有过滤器(AND 语义)的主机 ID:

data "aws_ec2_hosts" "example" { filter { name = "instance-type" values = ["c5.large"] } filter { name = "state" values = ["available"] } }

这段配置的含义是:查询所有实例类型为c5.large且状态为available(可用)的 Dedicated Host。查询结果会导出到data.aws_ec2_hosts.example.ids,供其他资源引用。

按 Outpost ARN 过滤

outpost_arn参数用于筛选部署在指定 AWS Outpost 上的主机。需要特别注意的是DescribeHostsAPI 并不支持将outpost-arn作为服务端过滤条件,因此该参数是在 Provider 内部通过客户端侧过滤实现的:

data "aws_ec2_hosts" "outpost" { outpost_arn = data.aws_outposts_outpost.example.arn filter { name = "state" values = ["available"] } }

Argument Reference(参数参考)

以下是aws_ec2_hosts支持的参数,全部为可选:

参数是否可选说明
filter可选一个或多个配置块,用于按名称-值过滤。支持的过滤器名称见 AWS EC2 API 参考中DescribeHosts的 Filter 列表。详细说明见下文
outpost_arn可选AWS Outpost 的 ARN。客户端侧过滤,仅返回分配在该 Outpost 上的主机
region可选该数据源执行查询的 AWS 区域,默认使用 Provider 配置中设置的区域
tags可选键值对形式的资源标签映射,每一对标签都必须与目标 Dedicated Host 上的标签精确匹配

filter

每个filter配置块支持以下参数:

参数是否必填说明
name必填过滤器名称,对应DescribeHostsAPI 支持的过滤字段
values必填一个或多个过滤器值组成的列表

从源码看,filter块被建模为集合嵌套块SetNestedBlock),其中的name为必填字符串属性,values为必填字符串集合,定义在 internal/service/ec2/filters.go#L136-L152:

func customFiltersBlock(ctx context.Context) datasourceschema.Block { return datasourceschema.SetNestedBlock{ CustomType: fwtypes.NewSetNestedObjectTypeOfcustomFilterModel, NestedObject: datasourceschema.NestedBlockObject{ Attributes: map[string]datasourceschema.Attribute{ names.AttrName: datasourceschema.StringAttribute{Required: true}, names.AttrValues: datasourceschema.SetAttribute{ CustomType: fwtypes.SetOfStringType, ElementType: types.StringType, Required: true, }, }, }, } }

值得注意的一个实现细节:values在 schema 中是一个Set(无序集合),而对应的customFilterModel结构体(filters.go#L155-L158)使用fwtypes.SetOfString承载值,这意味着在配置中多个filter块的顺序不影响查询结果。

Attribute Reference(属性参考)

除上述参数外,该数据源还会导出以下属性:

属性说明
idsEC2 Dedicated Host 标识符(Host ID)的列表

ids在 schema 中被声明为 Computed(计算属性)的字符串列表(ec2_hosts_data_source.go#L37-L41):

names.AttrIDs: schema.ListAttribute{ CustomType: fwtypes.ListOfStringType, ElementType: types.StringType, Computed: true, },

源码级实现原理

数据源的框架化注册

该数据源使用 Terraform Plugin Framework 实现,通过@FrameworkDataSource注解注册,对应源码为 internal/service/ec2/ec2_hosts_data_source.go:

// @FrameworkDataSource("aws_ec2_hosts", name="Hosts") func newHostsDataSource(context.Context) (datasource.DataSourceWithConfigure, error) { return &hostsDataSource{}, nil }

数据源的模型结构体hostsDataSourceModel继承自framework.WithRegionModel(用于注入region参数),并声明了filteridsoutpost_arntags四个字段(ec2_hosts_data_source.go#L92-L98)。

Read 调用链:从配置到DescribeHosts

数据源的核心逻辑在Read方法(ec2_hosts_data_source.go#L53-L90),完整流程如下:

  1. 从请求配置中解析出用户定义的过滤器、标签与outpost_arn
  2. 通过d.Meta().EC2Client(ctx)获取 EC2 客户端;
  3. 构造ec2.DescribeHostsInput,将用户过滤器转换为 API Filter 列表:
input := ec2.DescribeHostsInput{ Filter: append(newCustomFilterListFramework(ctx, data.Filters), newTagFilterList(svcTags(tftags.New(ctx, data.Tags)))...), }

这里体现了两个关键转换函数:

  • newCustomFilterListFramework(filters.go#L209-L236):把 Terraform 侧的filter集合逐条转换为awstypes.Filter{Name, Values},跳过未设置或未知的条目;
  • newTagFilterList(filters.go#L53-L57):把tags映射转换为tag:<Key>格式的过滤器,实现"标签必须精确匹配"的服务端过滤:
func newTagFilterList(tags []awstypes.Tag) []awstypes.Filter { return tfslices.ApplyToAll(tags, func(tag awstypes.Tag) awstypes.Filter { return newFilter("tag:"+aws.ToString(tag.Key), []string{aws.ToString(tag.Value)}) }) }
  1. 调用findHosts执行查询(详见下文分页逻辑);
  2. 客户端侧过滤 Outpost ARN:由于 API 不支持outpost-arn服务端过滤,代码在拿到全部主机列表后,用tfslices.Filter逐条比对Host.OutpostArn与用户传入的outpost_arn
if !data.OutpostARN.IsNull() && !data.OutpostARN.IsUnknown() { outpostARN := data.OutpostARN.ValueString() output = tfslices.Filter(output, func(v awstypes.Host) bool { return aws.ToString(v.OutpostArn) == outpostARN }) }
  1. 最后通过fwflex.FlattenFrameworkStringValueListOfString把每条主机的HostId展平为字符串列表,写入ids并保存到状态:
data.IDs = fwflex.FlattenFrameworkStringValueListOfString(ctx, tfslices.ApplyToAll(output, func(v awstypes.Host) string { return aws.ToString(v.HostId) }))

分页与错误处理:findHosts

查询动作最终落到 internal/service/ec2/find.go#L355-L376 的findHosts函数,它使用 EC2 SDK 的DescribeHostsPaginator自动分页拉取全部结果,避免单次 API 响应大小限制导致数据缺失:

func findHosts(ctx context.Context, conn *ec2.Client, input *ec2.DescribeHostsInput) ([]awstypes.Host, error) { var output []awstypes.Host pages := ec2.NewDescribeHostsPaginator(conn, input) for pages.HasMorePages() { page, err := pages.NextPage(ctx) if tfawserr.ErrCodeEquals(err, errCodeInvalidHostIDNotFound) { return nil, &retry.NotFoundError{LastError: err} } if err != nil { return nil, err } output = append(output, page.Hosts...) } return output, nil }

该实现还处理了特殊错误码InvalidHostID.NotFound(定义于 internal/service/ec2/errors.go#L51),将其归一化为retry.NotFoundError,便于上层统一处理"查询对象不存在"的场景。

整体执行流程图

Terraform 配置 (filter / tags / outpost_arn) │ ▼ Read() 解析模型 (hostsDataSourceModel) │ ├──► newCustomFilterListFramework ──► []awstypes.Filter(用户 filter 块) ├──► newTagFilterList ─────────────► []awstypes.Filter(tag:<Key> 格式) │ ▼ DescribeHostsInput ──► findHosts()(DescribeHostsPaginator 分页拉取) │ ▼ 服务端返回全部 Host 列表 │ ▼ outpost_arn 非空?──► tfslices.Filter 客户端侧按 OutpostArn 精确匹配 │ ▼ 展平 HostId ──► ids 属性写入 State

测试用例佐证

仓库为aws_ec2_hosts提供了三组接受测试(Acceptance Tests),位于 internal/service/ec2/ec2_hosts_data_source_test.go,可以作为完整可运行的实战模板:

  • TestAccEC2HostsDataSource_filter:先创建aws_ec2_host资源(实例类型c5.large,指定可用区),再用instance-type+availability-zone两个过滤器查询,断言ids.#大于 0;
  • TestAccEC2HostsDataSource_outpostARN:在 Outpost 上创建主机(实例族r5d),配合aws_outposts_outposts/aws_outposts_outpost数据源获取 Outpost ARN,验证outpost_arn客户端过滤后ids.#恰好为 1;
  • TestAccEC2HostsDataSource_tags:创建带Name标签的主机,然后通过tags参数精确匹配,断言ids.#为 1。

其中 filter 场景的配置片段展示了数据源与资源depends_on的正确配合方式:

resource "aws_ec2_host" "test" { availability_zone = data.aws_availability_zones.available.names[0] instance_type = "c5.large" tags = { Name = "tf-acc-test-..." } } data "aws_ec2_hosts" "test" { filter { name = "instance-type" values = ["c5.large"] } filter { name = "availability-zone" values = [aws_ec2_host.test.availability_zone] } }

注意测试中使用了acctest.PreCheckOutpostsOutposts做前置检查(ec2_hosts_data_source_test.go#L41),说明 Outpost 相关用例只有在测试账户具备 Outposts 资源时才可运行——这是实际环境中使用outpost_arn参数前需要确认的前提条件。

实践要点与注意事项

  1. filtertags可组合使用:两者最终都会转换成DescribeHosts的 Filter 数组合并下发,语义为 AND,即结果必须同时匹配所有条件。
  2. outpost_arn是客户端过滤:它发生在 API 返回全部主机之后,因此若账户内主机数量巨大,该参数无法减少 API 数据传输量,仅影响最终返回的ids结果。
  3. values支持通配符:从测试配置可见values = ["r5d.*"]这类通配符写法(ec2_hosts_data_source_test.go#L96-L99),这与 EC2 过滤语法保持一致。
  4. 分页自动处理:底层findHosts使用 Paginator,无需关心主机数量超过单页上限的问题。
  5. 结果集为空时的表现:当没有任何主机匹配时,ids为空列表而非报错(除非 API 返回InvalidHostID.NotFound等异常),下游引用idsfor_each等表达式会自然得到空集合,便于做条件化处理。
  6. 可用区域(region):查询默认在 Provider 配置的区域执行,跨区域查询需显式设置region参数,与 Provider 区域化管理的整体设计一致。

【免费下载链接】terraform-provider-awsThe AWS Provider enables Terraform to manage AWS resources.项目地址: https://gitcode.com/GitHub_Trending/te/terraform-provider-aws

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询