terraform-provider-aws 中 aws_ami_ids 数据源详解:AMI 批量检索的参数、实现与测试验证
【免费下载链接】terraform-provider-awsThe AWS Provider enables Terraform to manage AWS resources.项目地址: https://gitcode.com/GitHub_Trending/te/terraform-provider-aws
本篇围绕 Terraform AWS Provider 的aws_ami_ids数据源展开:它会按owners、filter等条件调用 EC2DescribeImagesAPI 批量查找 AMI,并将匹配到的镜像 ID 列表以ids属性输出。读完本文,你将掌握该数据源全部参数(含name_regex本地正则过滤、sort_ascending排序、include_deprecated等)的正确用法,并能从源码层面理解其分页查询、错误处理与 ID 排序的完整链路。
典型用法:按属主与名称模式筛选 AMI
aws_ami_ids的定位是“获取一组 AMI ID 列表”(官方文档见 website/docs/d/ami_ids.html.markdown)。最典型的场景是只查某一个属主(如 Ubuntu 官方账户099720109477)名下符合名称模式的全部镜像:
data "aws_ami_ids" "ubuntu" { owners = ["099720109477"] filter { name = "name" values = ["ubuntu/images/ubuntu-*-*-amd64-server-*"] } }此时data.aws_ami_ids.ubuntu.ids即返回所有匹配的 AMI ID 列表。需要强调的是:owners是必填项,且至少提供一个值——源码中该属性声明为Required: true, MinItems: 1,并对每个元素附加了validation.NoZeroValues校验(不允许空字符串),定义位于 ec2_ami_ids_data_source.go。
参数参考(Argument Reference)
结合官方文档与源码 schema 定义(dataSourceAMIIDs),该数据源支持以下参数:
| 参数 | 必填 | 类型/默认值 | 说明 |
|---|---|---|---|
region | 否 | 字符串 | 查询所在区域。默认使用 provider 配置中的 Region,与其他 provider 资源的 region 语义一致 |
owners | 是 | 字符串列表,至少 1 项 | 限定搜索范围的 AMI 属主列表。合法取值为:AWS 账户 ID、self(当前账户)、AWS 属主别名(如amazon、aws-marketplace、microsoft)。元素禁止为空值(NoZeroValues校验) |
executable_users | 否 | 字符串列表 | 仅保留对镜像具有显式启动权限的用户可见的镜像。取值为数值的账户 ID 或self |
filter | 否 | filter块,可多个 | 名称/取值对的过滤条件,完整可用过滤器名称以 EC2 APIDescribeImages的 Filters 参考为准 |
include_deprecated | 否 | 布尔,默认false | 为true时返回结果包含已弃用(deprecated)的 AMI;为false或未指定时不包含 |
name_regex | 否 | 正则字符串 | 对 AWS 返回的 AMI 名称做本地正则匹配,可实现 AWS API 过滤器不支持的更复杂模式;因过滤在本地执行,结果集过大时有性能影响,建议配合其他参数缩小返回范围 |
sort_ascending | 否 | 布尔,默认false | 按创建时间排序方向。默认降序(最新的在前),设为true则升序 |
几点源码级的补充细节:
name_regex在 schema 层通过validation.StringIsValidRegExp校验,保证配置的字符串是合法正则(源码),非法正则在 plan 阶段即报错,而不是等到 read 时才失败。include_deprecated与sort_ascending均使用Default: false声明默认值,与文档描述一致(源码)。
filter块
filter块包含两个字段:
name— 过滤器名称(必填)。合法名称即 EC2 APIDescribeImages所接受的所有 Filter 名称,例如name、creation-date、architecture、virtualization-type、state、image-type等。values— 该过滤器接受的取值集合(必填,字符串集合)。
从源码看,filter属性复用了 EC2 服务包统一的customFiltersSchema(TypeSet+name/values两个 Required 字段,定义在 filters.go),read 时经 newCustomFilterList 转换为 EC2 API 的Filters结构。这意味着aws_ami_ids的过滤语义与 AWS CLI/API 完全对齐:多个 filter 之间是 AND 关系,同一 filter 的多个 values 之间是 OR 关系。
输出属性与 ID 生成机制
除上述参数外,数据源额外导出:
ids— AMI ID 列表,按创建时间排序,方向由sort_ascending决定。源码中该属性为TypeList、Computed(源码),元素顺序稳定可靠。
关于 Terraform 状态中的id属性值得说明:数据源 read 完成后会执行d.SetId(strconv.Itoa(create.StringHashcode(fmt.Sprintf("%#v", input))))(源码)。从这段实现看,id是对最终DescribeImagesInput结构体格式化的字符串做 hash 得到的整数。其直接效果是:相同查询条件复用已有状态,条件变化时状态自动刷新——这在数据源场景下是常见的稳定标识做法,ids本身才是用户真正消费的输出。
源码实现:一次 read 的完整调用链
read 入口为 dataSourceAMIIDsRead,整个流程可拆为四步。
1. 组装 DescribeImagesInput
input := ec2.DescribeImagesInput{ IncludeDeprecated: aws.Bool(d.Get("include_deprecated").(bool)), Owners: flex.ExpandStringValueList(d.Get("owners").([]any)), } if v, ok := d.GetOk("executable_users"); ok { input.ExecutableUsers = flex.ExpandStringValueList(v.([]any)) } if v, ok := d.GetOk(names.AttrFilter); ok { input.Filters = newCustomFilterList(v.(*schema.Set)) }owners无条件放入输入;executable_users与filter仅在配置了值时才设置到输入中(GetOk语义),避免向 API 传递空参数。
2. 分页拉取全部镜像
查询通过 findImages 执行,该函数封装了分页与错误处理:
pages := ec2.NewDescribeImagesPaginator(conn, input) for pages.HasMorePages() { page, err := pages.NextPage(ctx) if tfawserr.ErrCodeEquals(err, errCodeInvalidAMIIDNotFound) { return nil, &retry.NotFoundError{ LastError: err, } } if err != nil { return nil, err } output = append(output, page.Images...) }从源码结构看有两个要点:
- 使用 SDK v2 的
DescribeImagesPaginator自动翻页,保证结果集不受单页大小限制; - 当 API 返回
InvalidAMIID.NotFound错误码时,转换为retry.NotFoundError(错误码常量定义于 errors.go)。对于数据源而言,这意味着若按 ID 条件查询不到任何镜像,Terraform 会得到一个明确的“未找到”错误,而不是静默返回空列表。
3. name_regex 本地过滤
if v, ok := d.GetOk("name_regex"); ok { r := regexache.MustCompile(v.(string)) for _, image := range images { name := aws.ToString(image.Name) // Check for a very rare case where the response would include no // image name. No name means nothing to attempt a match against, // therefore we are skipping such image. if name == "" { continue } if r.MatchString(name) { filteredImages = append(filteredImages, image) } } } else { filteredImages = images[:] }这印证了文档中“该过滤在本地对 AWS 返回结果执行”的描述:正则只匹配镜像的Name字段(MatchString为部分匹配而非全匹配),且对极少数无名称的镜像直接跳过。正则使用regexache.MustCompile预编译,编译失败会在 plan/validate 阶段被拦截(配合前述StringIsValidRegExp校验)。
4. 按创建时间排序并输出
slices.SortFunc(filteredImages, func(a, b awstypes.Image) int { atime, _ := time.Parse(time.RFC3339, aws.ToString(a.CreationDate)) btime, _ := time.Parse(time.RFC3339, aws.ToString(b.CreationDate)) compare := atime.Compare(btime) if d.Get("sort_ascending").(bool) { return compare } return -compare }) for _, image := range filteredImages { imageIDs = append(imageIDs, aws.ToString(image.ImageId)) }排序依据是 API 返回的CreationDate字段(RFC3339 格式):sort_ascending = true时正序(旧 → 新),默认false时取-compare反转为倒序(新 → 旧)。因此ids[0]在默认配置下就是创建时间最新的镜像 ID,这是“取属主下最新一批镜像批量部署”类场景的关键保证。
验收测试对行为的实证验证
配套的验收测试位于 ec2_ami_ids_data_source_test.go,覆盖了文档声明的三条核心行为:
- 基础查询(
TestAccEC2AMIIDsDataSource_basic):用owners = ["099720109477"]加name过滤ubuntu/images/hvm-instance/ubuntu-*,断言ids.# > 0; - 排序方向(
TestAccEC2AMIIDsDataSource_sorted):构造两个aws_ami数据源取得两张已知镜像,再用aws_ami_ids的name过滤同时命中它们,分别以sort_ascending = false/true各跑一步,断言ids.0/ids.1的相对顺序正好相反; - include_deprecated(
TestAccEC2AMIIDsDataSource_includeDeprecated):以include_deprecated = true查询 Ubuntu 镜像并断言结果非空。
其中排序测试的配置值得注意:它对第二张镜像额外叠加了creation-date过滤(限定到某个月份),使两张镜像的创建时间明确不同,从而让升/降序断言具有确定性。
Timeouts 与使用建议
该数据源声明了 read 超时,默认为 20 分钟,对应 Terraform 的超时配置项read(默认20m)。源码中通过schema.ResourceTimeout{Read: schema.DefaultTimeout(20 * time.Minute)}实现(源码)。对结果集很大的属主(如amazon),这一宽松默认值给了分页拉取充分时间,但如果配置了过宽的过滤器导致单次查询超过 20 分钟,仍会因超时失败。
基于以上实现细节,实践上建议:
- 务必提供
owners并尽量收窄:owners必填本身就是防止全库扫描的设计;查询公开镜像时用属主别名(amazon),查询发行方镜像时用具体账户 ID。 - 优先用 API 侧
filter,其次才是name_regex:name_regex在本地对全量返回结果做匹配,文档已明确提示“结果集大时可能有性能影响”,正确做法是先靠owners+filter(如creation-date、architecture、state = available)把 API 返回集压小,再用正则做精细模式匹配。 - 利用排序方向取最新镜像:默认降序下
ids[0]即最新镜像,可与slice(data.aws_ami_ids.x.ids, 0, 1)等写法配合实现“取最新一个 AMI”的模式;需要稳定旧版本审计时再显式开启sort_ascending。 - 注意 deprecated 语义:
include_deprecated默认false,若属主已弃用某些镜像而你希望它们仍出现在列表中(例如做清理盘点),需显式置为true。
该数据源在 provider 中的注册信息可见 service_package_gen.go(TypeName: "aws_ami_ids"),与同服务包内的 aws_ami 单镜像数据源文档 构成互补:查单张镜像用aws_ami,查一组 ID 用aws_ami_ids。
【免费下载链接】terraform-provider-awsThe AWS Provider enables Terraform to manage AWS resources.项目地址: https://gitcode.com/GitHub_Trending/te/terraform-provider-aws
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考