Terraform AWS Provider 实战:使用aws_availability_zones数据源获取可用区列表
【免费下载链接】terraform-provider-awsThe AWS Provider enables Terraform to manage AWS resources.项目地址: https://gitcode.com/GitHub_Trending/te/terraform-provider-aws
导读
aws_availability_zones是 Terraform AWS Provider(terraform-provider-aws)中 EC2 服务模块提供的复数形式数据源,用于一次性获取当前 AWS 账户在指定区域内有权限访问的全部可用区(Availability Zone)名称、ID 与所属分组信息。本文以官方文档 website/docs/d/availability_zones.html.markdown 为主线,结合 ec2_availability_zones_data_source.go 源码与 ec2_availability_zones_data_source_test.go 测试用例,系统讲解其参数语义、过滤策略、导出属性及底层调用链,帮助你用它安全地生成多可用区(Multi-AZ)高可用架构。
数据源定位:与单数形式aws_availability_zone的区别
在动手使用之前,需要先厘清两个容易混淆的数据源:
aws_availability_zones(本文主角):返回可用区的列表,面向"批量获取一组可用区"的场景。例如在多可用区部署时,用names[0]、names[1]分别取前两个可用区来创建子网。aws_availability_zone(单数):查询单个可用区的详细信息,包括zone_id、zone_type、parent_zone_id、parent_zone_name、opt_in_status、network_border_group等字段。其实现见 ec2_availability_zone_data_source.go。
两者底层都调用 EC2 的DescribeAvailabilityZonesAPI,只是单数形式最终通过findAvailabilityZone收敛为唯一结果(找不到或结果不唯一会报SingularDataSourceFindError,见 ec2_availability_zone_data_source.go)。
基础用法:按状态筛选可用区
最常见的场景是声明数据源后,直接引用其names属性创建子网。官方文档给出的完整示例如下:
# Declare the data source data "aws_availability_zones" "available" { state = "available" } # e.g., Create subnets in the first two available availability zones resource "aws_subnet" "primary" { availability_zone = data.aws_availability_zones.available.names[0] # ... } resource "aws_subnet" "secondary" { availability_zone = data.aws_availability_zones.available.names[1] # ... }要点说明:
state = "available"限定只返回状态正常的可用区,避免把处于impaired、unavailable状态的区域写入配置;- 数据源本身不创建任何资源,仅在
terraform plan/apply时执行一次读取,将结果写入 state 供其他资源引用; names[0]、names[1]是有序列表索引,这一点依赖源码中的排序保证(详见下文"源码解析")。
进阶用法:通过filter精确过滤
当需要更细粒度的过滤时,使用filter块。官方文档给出两个极具代表性的场景。
场景一:获取全部 Local Zones(不论 opt-in 状态)
data "aws_availability_zones" "example" { all_availability_zones = true filter { name = "opt-in-status" values = ["not-opted-in", "opted-in"] } }all_availability_zones = true表示忽略账户的 opt-in 状态,把 Local Zones 也纳入返回范围;配合opt-in-status过滤条件,可以拿到not-opted-in和opted-in两类 Local Zone 的完整集合。
场景二:只要标准可用区(剔除 Local Zones)
data "aws_availability_zones" "example" { filter { name = "opt-in-status" values = ["opt-in-not-required"] } }官方文档特别提醒:当区域启用 Local Zones 后,默认情况下 API 和本数据源会同时返回 Local Zones 与标准可用区。若只想获得标准可用区,上面的opt-in-not-required过滤是推荐写法——标准可用区无需 opt-in,其状态正是该值。
filter块的字段定义:
name(必填):过滤字段名,合法取值以 EC2DescribeAvailabilityZonesAPI 参考为准(如state、opt-in-status、zone-name、zone-id、zone-type等);values(必填):该字段可接受的取值集合,任一值匹配即命中(OR 语义)。
参数(Argument Reference)
数据源支持的完整参数如下:
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
all_availability_zones | bool | 否 | 设为true时,无论账户 opt-in 状态如何,都包含全部可用区与 Local Zones |
exclude_names | set(string) | 否 | 需要从结果中剔除的可用区名称列表 |
exclude_zone_ids | set(string) | 否 | 需要从结果中剔除的可用区 ID 列表 |
filter | block | 否 | 自定义过滤块,可重复声明多个,语义为 AND(多个 filter 需同时满足) |
region | string | 否 | 查询区域,默认使用 Provider 配置中的区域 |
state | string | 否 | 按当前状态过滤,取值只能是"available"、"information"、"impaired"或"unavailable";默认返回账户可访问的完整可用区集合,不区分状态 |
从源码视角看这些参数的语义(见 ec2_availability_zones_data_source.go):
exclude_names与exclude_zone_ids在 schema 中定义为TypeSet,读取阶段会被转成*schema.Set用于成员判定;state字段使用了enum.Validate[awstypes.AvailabilityZoneState]()做取值校验(ec2_availability_zones_data_source.go),该校验器由 internal/enum/validate.go 提供,本质是validation.StringInSlice的泛型封装,并在内部用sync.Map缓存编译结果;filter复用 EC2 模块通用的customFiltersSchema()(定义于 filters.go),一个数据源可声明多个 filter 块。
导出属性(Attribute Reference)
除了已配置的参数外,数据源还会导出以下只读属性:
| 属性 | 类型 | 说明 |
|---|---|---|
id | string | 可用区所属的区域名(Region),即读取阶段d.SetId(Region)的结果 |
group_names | set(string) | 可用区分组名集合。标准可用区的分组名与其区域名相同;Local Zones 的分组名为其关联组名,例如us-west-2-lax-1 |
names | list(string) | 账户可用的可用区名称列表,如["us-west-2a", "us-west-2b", ...] |
zone_ids | list(string) | 与names一一对应的可用区 ID 列表,如["usw2-az1", "usw2-az2", ...] |
实战中常见两种引用方式:
# 引用名称(可读性好) availability_zone = data.aws_availability_zones.available.names[0] # 引用 ID(更稳定,名称变更不影响) availability_zone_id = data.aws_availability_zones.available.zone_ids[0]源码解析:读取流程与底层调用链
数据源的注册与实现位于 ec2_availability_zones_data_source.go,核心读取函数为dataSourceAvailabilityZonesRead(L77-L159),整体流程如下:
- 构建 API 请求:声明
ec2.DescribeAvailabilityZonesInput{}; - 透传
all_availability_zones:配置了该参数时,直接写入input.AllAvailabilityZones(L85-L87); - 组装过滤条件:
state参数会被转换为名为"state"的 Filter;filter块则通过newCustomFilterList转换为[]awstypes.Filter(L89-L102)。值得注意的是,若过滤列表为空,代码会主动置为nil,注释明确说明"EC2 API 不接受空的 filters 列表"(L104-L107); - 发起调用:
conn.DescribeAvailabilityZones(ctx, &input),conn来自 Provider 上下文中的EC2Client(L110); - 稳定排序:用
slices.SortFunc+cmp.Compare按ZoneName字典序排序(L115-L117)。这正是names[0]、names[1]索引用法可靠的前提; - 本地剔除:遍历排序结果,跳过命中
exclude_names或exclude_zone_ids的条目,同时对group_names做去重(L119-L144); - 写回 state:
SetId设为当前区域名,并依次写入group_names、names、zone_ids(L146-L156)。
关于filter的转换逻辑:newCustomFilterList(filters.go)把 Terraform 侧的 filter Set 逐项映射为awstypes.Filter{Name, Values},values使用flex.ExpandStringValueEmptySet展开;多个 filter 块会全部 append 到请求的Filters数组中,因此多个 filter 之间是 AND 关系,而同一 filter 内的多个 values 是 OR 关系。
超时(Timeouts)
数据源支持read超时配置,默认20 分钟,与源码中schema.DefaultTimeout(20 * time.Minute)一致(ec2_availability_zones_data_source.go)。可按需调整:
data "aws_availability_zones" "available" { state = "available" timeouts { read = "10m" } }测试验证:行为约定是如何被守护的
测试文件 ec2_availability_zones_data_source_test.go 用一组 Acceptance Test 固化了本数据源的关键行为约定:
TestAccEC2AvailabilityZonesDataSource_basic:空配置即可读取,并断言group_names、names、zone_ids均非空、结果有序(testAccCheckAvailabilityZonesMeta,L131-L154);TestAccEC2AvailabilityZonesDataSource_allAvailabilityZones:验证all_availability_zones = true;TestAccEC2AvailabilityZonesDataSource_filter:验证filter块(测试用name = "state"、values = ["available"],见 L265-L274);TestAccEC2AvailabilityZonesDataSource_excludeNames与excludeZoneIDs:分别用data.aws_availability_zones.all.names[0]、zone_ids[0]作为排除项,再断言两个数据源的names.#与zone_ids.#确有差异(testAccCheckAvailabilityZonesExcluded,L156-L188);TestAccEC2AvailabilityZonesDataSource_stateFilter:验证state = "available"过滤后的结果仍完整可用。
这些测试可以直接作为验收标准:任何对数据源排序、剔除、过滤行为的改动都必须通过它们,否则会破坏依赖names[n]索引的下游配置。
最佳实践与注意事项
- 优先按 ID 而不是按名称引用:可用区名称可能随账户/区域迁移变化,而
zone_ids(如usw2-az1)相对稳定;对要求强一致性的多可用区部署,建议使用zone_ids[n]。 - 关注 Local Zones 的默认行为:区域启用 Local Zones 后,默认返回结果会混入 Local Zones,可能让你的"3 个可用区"实际落在不同物理分组。需要纯标准可用区时,务必加上
opt-in-status = ["opt-in-not-required"]过滤。 exclude_names与exclude_zone_ids的适用场景:当某些可用区因容量或预算原因不可用时,可以基于全量结果排除特定名称/ID,而不是硬编码索引,保证其他可用区变更时配置依然成立。- 不要把数据源结果当作常量:
aws_availability_zones的结果随 AWS 后端状态变化(新区域、Local Zone 开通、可用区状态变化都会改变列表),因此应避免在需要稳定拓扑的场景中直接拼接固定索引;结合count循环与length()判断更稳妥。 - 区域覆盖:
region参数允许在 Provider 默认区域之外单独指定查询区域,适合跨区域信息收集场景。
小结
aws_availability_zones是构建多可用区基础设施时最常用的 EC2 数据源之一:它把"账户在区域内可用的可用区清单"抽象为可直接引用的names/zone_ids列表,并通过state、filter、exclude_names/exclude_zone_ids提供灵活的筛选能力。本文既覆盖了官方文档的全部参数与示例,也通过源码揭示了排序、剔除、过滤的具体实现与测试守护,帮助你写出可预测、可维护的多可用区 Terraform 配置。
【免费下载链接】terraform-provider-awsThe AWS Provider enables Terraform to manage AWS resources.项目地址: https://gitcode.com/GitHub_Trending/te/terraform-provider-aws
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考