terraform-provider-aws 数据源解析:aws_billing_service_account 与 AWS 账单服务账号权限配置
【免费下载链接】terraform-provider-awsThe AWS Provider enables Terraform to manage AWS resources.项目地址: https://gitcode.com/GitHub_Trending/te/terraform-provider-aws
本指南以 terraform-provider-aws 中aws_billing_service_account数据源为主题,讲解如何通过该数据源获取 AWS Billing and Cost Management 服务账号的 Account ID 与 ARN,并将其用于 S3 存储桶策略,以授权 AWS 写入计费日志。阅读完成后,你将掌握该数据源的使用方法、无参数调用的设计原理,以及它在 S3 计费日志归档场景中的完整落地配置。
数据源是什么:一个无需 AWS API 调用的静态账号查询
aws_billing_service_account是 terraform-provider-aws 提供的一个特殊数据源:它不向任何 AWS API 发起请求,也不依赖任何参数,而是直接返回 AWS 官方公布的 Billing and Cost Management 服务账号信息。
在 AWS 的计费架构中,当你为 S3 存储桶开启计费日志(Billing Reports / Cost & Usage Report)写入时,写日志的"服务账号"是一个 AWS 官方维护的固定账号,而非你的个人账号。要在 S3 存储桶策略中授权该账号写入,就需要在策略的Principal中引用这个固定账号的 ARN。该数据源存在的意义,就是避免你在 Terraform 配置中硬编码这一串官方账号 ID。
根据官方文档,其核心用途描述为:
使用此数据源获取 AWS Billing and Cost Management 服务账号的 Account ID,用于在 S3 存储桶策略中放行该账号。
对应文档:website/docs/d/billing_service_account.html.markdown。
示例用法:为 S3 计费日志桶授权 AWS 账单服务账号
以下是官方文档给出的完整示例,它展示了从创建存储桶、设置 ACL,到用aws_iam_policy_document构造桶策略、再挂载策略的完整链路:
data "aws_billing_service_account" "main" {} resource "aws_s3_bucket" "billing_logs" { bucket = "my-billing-tf-test-bucket" } resource "aws_s3_bucket_acl" "billing_logs_acl" { bucket = aws_s3_bucket.billing_logs.id acl = "private" } data "aws_iam_policy_document" "allow_billing_logging" { statement { effect = "Allow" principals { type = "AWS" identifiers = [data.aws_billing_service_account.main.arn] } actions = [ "s3:GetBucketAcl", "s3:GetBucketPolicy", ] resources = [aws_s3_bucket.billing_logs.arn] } statement { effect = "Allow" principals { type = "AWS" identifiers = [data.aws_billing_service_account.main.arn] } actions = ["s3:PutObject"] resources = ["${aws_s3_bucket.billing_logs.arn}/*"] } } resource "aws_s3_bucket_policy" "allow_billing_logging" { bucket = aws_s3_bucket.billing_logs.id policy = data.aws_iam_policy_document.allow_billing_logging.json }这段配置中有两个值得注意的细节:
- 第一个 statement:授权账单服务账号对存储桶本身执行
s3:GetBucketAcl和s3:GetBucketPolicy,这是 AWS 写日志前读取桶配置所必需的最小权限; - 第二个 statement:授权
s3:PutObject写入桶内的对象,资源为arn/*(桶下所有对象),这是实际写入计费日志对象所需的权限。
两条语句的Principal都引用了data.aws_billing_service_account.main.arn,从而避免了硬编码官方账号 ARN。
Argument Reference:零参数设计
该数据源不支持任何参数:
This data source does not support any arguments.
这与它的实现方式直接相关。查看源码 internal/service/billing/billing_service_account_data_source.go,其 Schema 中仅定义了两个计算属性(Computed: true),没有Optional的入参:
response.Schema = schema.Schema{ Attributes: map[string]schema.Attribute{ names.AttrARN: schema.StringAttribute{ Computed: true, }, names.AttrID: schema.StringAttribute{ Optional: true, Computed: true, }, }, }声明数据源时甚至不需要写data "aws_billing_service_account" "main" {}之外的任何配置块。
Attribute Reference:id 与 arn
数据源导出以下属性:
| 属性 | 说明 |
|---|---|
id | AWS 账单服务账号的 Account ID |
arn | AWS 账单服务账号的 ARN |
在源码实现中,这两个值的来源非常清晰。Read方法内直接硬编码了官方服务账号 ID,并通过全局 ARN 构造方法生成 ARN:
// See http://docs.aws.amazon.com/awsaccountbilling/latest/aboutv2/billing-getting-started.html#step-2 const serviceAccountID = "386209384616" data.ARN = fwflex.StringValueToFrameworkLegacy(ctx, d.Meta().GlobalARNWithAccount(ctx, "iam", serviceAccountID, "root")) data.ID = fwflex.StringValueToFrameworkLegacy(ctx, serviceAccountID)也就是说:
id固定返回386209384616;arn由GlobalARNWithAccount构造,服务名为iam、账号 ID 为386209384616、资源为root,得到形如arn:aws:iam::386209384616:root的全局 ARN。
GlobalARNWithAccount定义在 internal/conns/awsclient.go,其底层通过arn.ARN结构体拼接出无 Region、指定账号 ID的 ARN:
// GlobalARNWithAccount returns a global (no Region) ARN for the specified service namespace, resource and account ID. func (c *AWSClient) GlobalARNWithAccount(ctx context.Context, service, accountID, resource string) string { return c.arn(ctx, service, "", accountID, resource) }arn方法则负责填充 Partition、Service、Region、AccountID、Resource 五个字段并调用.String()序列化(见 internal/conns/awsclient.go)。因此该 ARN 会随 Provider 配置的分区(如aws、aws-cn、aws-us-gov)自动适配,这也是它比硬编码字符串更健壮的原因之一。
源码层面的实现细节
基于 Terraform Plugin Framework 实现
从源码结构看,该数据源是基于terraform-plugin-framework实现的新一代数据源。文件头部的生成器注释与注册逻辑可以佐证:
// @FrameworkDataSource("aws_billing_service_account", name="Service Account") func newServiceAccountDataSource(context.Context) (datasource.DataSourceWithConfigure, error) { d := &billingServiceAccountDataSource{} return d, nil }其注册信息位于 internal/service/billing/service_package_gen.go,注意其中Region: inttypes.ResourceRegionDisabled()的标记——这表示该数据源与 Region 无关(全局资源),因为账单服务账号在全球范围内是同一个固定账号,不随调用 Region 变化。
不发起任何 AWS API 调用
Read方法内没有创建任何 AWS 服务客户端,也没有调用List*/Get*之类的 API,而是直接向 State 写入常量值。这也是文档中"零参数、无 API 依赖"设计的直接体现。与同包内需要调用 Billing API 的aws_billing_views数据源(见 internal/service/billing/views_data_source.go 中d.Meta().BillingClient(ctx)的用法)相比,aws_billing_service_account是一个纯本地计算的数据源,运行时不会产生任何计费或网络开销。
测试用例对输出的约束
该数据源的行为由接受测试严格锁定。测试文件 internal/service/billing/billing_service_account_data_source_test.go 中:
func TestAccBillingServiceAccountDataSource_basic(t *testing.T) { ctx := acctest.Context(t) dataSourceName := "data.aws_billing_service_account.test" billingAccountID := "386209384616" ... Check: resource.ComposeTestCheckFunc( resource.TestCheckResourceAttr(dataSourceName, names.AttrID, billingAccountID), acctest.CheckResourceAttrGlobalARNAccountID(dataSourceName, names.AttrARN, billingAccountID, "iam", "root"), ), ... }测试明确断言:
id必须等于386209384616;arn必须是一个全局(无 Region)、账号 ID 为386209384616、服务为iam、资源为root的 ARN,即arn:aws:iam::386209384616:root。
这为文章开头的结论提供了可验证的依据:如果你在配置中看到data.aws_billing_service_account.main.arn,它解析出来的就是 AWS 官方账单服务账号的 IAM root ARN。
实际使用注意事项
- 只能用于放行 AWS 账单服务:该账号是 AWS 官方用于写计费日志的专用账号,不能把它当作自己的 AWS 账号使用,也不要试图用它做资源授权以外的操作。
- 配合
aws_iam_policy_document使用更佳:示例中先构造aws_iam_policy_document再通过aws_s3_bucket_policy挂载,避免手写 JSON 策略字符串,也便于把arn作为变量直接引用。 - 权限最小化:官方示例给出了读桶属性(
s3:GetBucketAcl、s3:GetBucketPolicy)与写对象(s3:PutObject)两组权限,实际生产环境建议仅开启确实需要的动作。 - 无需 provider 的 AWS 凭证也能 plan:由于该数据源不调用 API,即使本地凭证权限有限,
terraform plan阶段也能正常解析出arn与id。
总结
aws_billing_service_account是 terraform-provider-aws 中一个"小而精"的全局数据源:零参数、零 API 调用,通过 internal/service/billing/billing_service_account_data_source.go 中硬编码的官方账号 ID386209384616与全局 ARN 构造逻辑,稳定输出id与arn两个属性,专门服务于 S3 计费日志桶的策略授权场景。理解它的实现方式,也有助于举一反三:凡是这类"官方固定账号/固定标识"的查询需求,都可以参考此模式,用常量输出 + 全局 ARN 构造的方式在 Provider 中实现,从而避免用户在配置中硬编码魔法数字。
【免费下载链接】terraform-provider-awsThe AWS Provider enables Terraform to manage AWS resources.项目地址: https://gitcode.com/GitHub_Trending/te/terraform-provider-aws
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考