使用 Terraform AWS Provider 搭建跨账户 Network Firewall 与 Transit Gateway 附件示例
【免费下载链接】terraform-provider-awsThe AWS Provider enables Terraform to manage AWS resources.项目地址: https://gitcode.com/GitHub_Trending/te/terraform-provider-aws
本指南围绕 terraform-provider-aws 仓库中的examples/network-firewall-cross-account-transit-gateway示例展开,完整演示如何在账户 A 中创建 EC2 Transit Gateway 并通过 AWS Resource Access Manager(RAM)共享给账户 B,再让账户 B 将 Network Firewall 防火墙以 Transit Gateway 附件的形式接入该共享网关,最后由账户 A 接受附件。读完本文,你将掌握双 Provider 多账户编排、RAM 资源共享、跨账户 Network Firewall 附件创建与接受(aws_networkfirewall_firewall_transit_gateway_attachment_accepter)的完整实战方案,并理解 Provider 底层对附件状态的轮询等待机制。
示例概览:一个跨账户、跨资源类型的完整链路
该示例的核心目标是:让防火墙和 VPC 网关分属不同 AWS 账户,但协同工作。整体拓扑包含两类角色:
- 第一个账户(first):拥有 EC2 Transit Gateway,创建 RAM 资源共享,并最终接受 Network Firewall 提交的 Transit Gateway 附件请求。
- 第二个账户(second):拥有 Network Firewall 防火墙及其策略,通过共享的 Transit Gateway ID 发起附件,并将防火墙流量接入该网关。
整条资源依赖链(来自 main.tf)如下:
aws_ec2_transit_gateway(账户 first)——创建共享中转网关;aws_ram_resource_share+aws_ram_resource_association+aws_ram_principal_association(账户 first)——创建资源共享,把 TGW 关联进共享,并把第二个账户(通过aws_caller_identity动态取得其账号 ID)设为共享主体;aws_networkfirewall_firewall_policy(账户 second)——定义无状态默认动作;aws_networkfirewall_firewall(账户 second)——设置transit_gateway_id并声明depends_on,确保 RAM 关联完成后再发起附件;aws_networkfirewall_firewall_transit_gateway_attachment_accepter(账户 first)——读取防火墙状态中的附件 ID 并接受,完成跨账户接入。
前置条件:同一组织与 RAM 服务
原文档 README.md 明确列出两项硬性前提,缺一不可:
- 两个 AWS 账户必须位于同一个 AWS Organizations 组织内。跨账户共享 Transit Gateway 依赖组织内的资源共享能力,账户之间必须建立组织信任关系。
- 必须在组织中启用 AWS Resource Access Manager(RAM)。Transit Gateway 的资源共享、主体验证都由 RAM 承载,未启用 RAM 时
aws_ram_principal_association将无法完成授权。
此外,运行本示例还需要两个账户各自的访问密钥(aws_first_access_key/aws_second_access_key及对应 Secret Key),并选择一个两个账户均可访问的区域(示例默认us-east-1)。
多账户配置:双 Provider 与变量定义
变量声明
variables.tf 声明了 5 个无默认值变量,全部由调用方在运行时注入:
variable "aws_first_access_key" {} variable "aws_first_secret_key" {} variable "aws_second_access_key" {} variable "aws_second_secret_key" {} variable "aws_region" {}Provider 配置
main.tf 顶部通过alias声明了两个独立的 AWS Provider 实例,同一份配置即可同时操作两个账户,互不干扰:
terraform { required_version = ">= 0.12" } # First account owns the transit gateway and accepts the Network Firewall attachment. provider "aws" { alias = "first" region = var.aws_region access_key = var.aws_first_access_key secret_key = var.aws_first_secret_key } # Second account owns the Network Firewall and creates the VPC attachment. provider "aws" { alias = "second" region = var.aws_region access_key = var.aws_second_access_key secret_key = var.aws_second_secret_key }这里采用了最直观的凭证注入方式(Access Key 直填)。生产环境中更推荐改用profile或assume_role等替身凭证方式,避免密钥直接出现在命令行或变量文件中;示例的terraform.template.tfvars提供了一套可替换的占位凭证(见下文"运行示例"一节)。
逐步拆解 main.tf:五个资源如何协同
1. 数据源准备
data "aws_availability_zones" "available" { provider = aws.first filter { name = "opt-in-status" values = ["opt-in-not-required"] } } data "aws_caller_identity" "second" { provider = aws.second }aws_availability_zones:过滤出默认开启的可用区(opt-in-not-required),用于给防火墙选取第一个可用区映射,避免选择需要手动启用的"本地"或新开放区域;aws_caller_identity:以账户 second 的身份调用 STS,动态取得其 12 位账号 ID,供 RAM 主体验证使用,避免硬编码账号号。
2. 账户 first:创建并共享 Transit Gateway
resource "aws_ec2_transit_gateway" "example" { provider = aws.first tags = { Name = "terraform-example" } } resource "aws_ram_resource_share" "example" { provider = aws.first name = "terraform-example" tags = { Name = "terraform-example" } } # Share the transit gateway... resource "aws_ram_resource_association" "example" { provider = aws.first resource_arn = aws_ec2_transit_gateway.example.arn resource_share_arn = aws_ram_resource_share.example.id } # ...with the second account. resource "aws_ram_principal_association" "example" { provider = aws.first principal = data.aws_caller_identity.second.account_id resource_share_arn = aws_ram_resource_share.example.id }三段式共享模型:
aws_ram_resource_share先创建空的资源共享容器;aws_ram_resource_association把 TGW 的 ARN 挂进共享;aws_ram_principal_association把账户 second 的账号 ID 加入共享主体列表,TGW 即对账户 second 可见、可用。
3. 账户 second:创建防火墙策略与防火墙
resource "aws_networkfirewall_firewall_policy" "example" { provider = aws.second name = "terraform-example" firewall_policy { stateless_fragment_default_actions = ["aws:drop"] stateless_default_actions = ["aws:pass"] } } # Create Network Firewall in the second account attached to the shared transit gateway resource "aws_networkfirewall_firewall" "example" { provider = aws.second depends_on = [ aws_ram_resource_association.example, aws_ram_principal_association.example, ] name = "terraform-example" firewall_policy_arn = aws_networkfirewall_firewall_policy.example.arn transit_gateway_id = aws_ec2_transit_gateway.example.id availability_zone_mapping { availability_zone_id = data.aws_availability_zones.available.zone_ids[0] } }关键点说明:
- 策略定义了两个无状态默认动作:分片数据包默认
aws:drop,完整数据包默认aws:pass,形成"分片丢弃、主流量放行"的基线(实际规则可在stateless_rule_group_reference/stateful_rule_group_reference中继续补充); - 防火墙通过
transit_gateway_id引用账户 first 创建的 TGW,这是跨账户能力的关键参数。从 firewall.go 的 schema 可见,transit_gateway_id与vpc_id互为排斥(ExactlyOneOf),且二者都是ForceNew——即防火墙要么接入 TGW、要么接入 VPC,切换模式必须重建资源; depends_on强制保证 RAM 关联与主体验证先完成,否则防火墙创建时可能因 TGW 尚未共享给本账户而失败;availability_zone_mapping至少需要一个可用区映射,此处取数据源返回的第一个可用区 ID。
4. 账户 first:接受跨账户附件
# ...and accept it in the first account. resource "aws_networkfirewall_firewall_transit_gateway_attachment_accepter" "example" { provider = aws.first transit_gateway_attachment_id = aws_networkfirewall_firewall.example.firewall_status[0].transit_gateway_attachment_sync_states[0].attachment_id }账户 second 发起附件后,账户 first 侧必须显式接受。附件 ID 并非直接暴露在防火墙主属性上,而是从计算属性firewall_status[0].transit_gateway_attachment_sync_states[0].attachment_id中提取——这正是 firewall.go 中firewall_status块里transit_gateway_attachment_sync_states(含attachment_id计算字段)所定义的输出结构,属于 AWS 返回给防火墙的同步状态信息。
运行示例
原文档给出了两种运行方式。
方式一:复制模板变量文件并修改
cp terraform.template.tfvars terraform.tfvarsterraform.template.tfvars 内容如下(占位凭证,需替换为真实密钥):
# First account aws_first_access_key = "AAAAAAAAAAAAAAAAAAA" aws_first_secret_key = "SuperSecretKeyForAccount1" # Second account aws_second_access_key = "BBBBBBBBBBBBBBBBBBB" aws_second_secret_key = "SuperSecretKeyForAccount2" aws_region = "us-east-1"方式二:通过 CLI 直接传参
terraform apply \ -var="aws_first_access_key=AAAAAAAAAAAAAAAAAAA" \ -var="aws_first_secret_key=SuperSecretKeyForAccount1" \ -var="aws_second_access_key=BBBBBBBBBBBBBBBBBBB" \ -var="aws_second_secret_key=SuperSecretKeyForAccount2" \ -var="aws_region=us-east-1"两种方式等价,推荐前者以便把敏感信息留在本地文件中(注意将terraform.tfvars加入版本控制忽略清单)。
源码级原理:accepter 资源的实现细节
仓库中 firewall_transit_gateway_attachment_accepter.go 是aws_networkfirewall_firewall_transit_gateway_attachment_accepter的框架式(Terraform Plugin Framework)实现,值得关注的实现事实:
- Schema 极简:仅包含一个必填属性
transit_gateway_attachment_id,且带RequiresReplace计划修饰符,即附件 ID 一旦变化就销毁重建;同时暴露timeouts(Create/Delete 默认各 60 分钟)。 - Create 即调用接受 API:
Create直接调用 Network Firewall 客户端的AcceptNetworkFirewallTransitGatewayAttachment,传入附件 ID;随后转入 EC2 客户端的WaitTransitGatewayAttachmentAccepted轮询等待,直到附件状态变为accepted才把状态写入 State。也就是说,terraform apply会阻塞直到跨账户接受真正生效。 - Read 做存在性校验:通过 EC2 的
FindTransitGatewayAttachmentByID查询附件;若状态为deleted或已不存在,则按"资源已消失"处理,从 State 中移除。 - Delete 走删除 API:调用
DeleteNetworkFirewallTransitGatewayAttachment,并对ResourceNotFoundException做幂等容错(已不存在则直接返回成功),随后同样等待删除完成。 - 支持导入:
ImportState使用 Passthrough 方式直接以transit_gateway_attachment_id作为导入 ID。
配套的 firewall_transit_gateway_attachment_accepter_test.go 提供了该资源的接受测试用例,可作为编写跨账户场景测试的参考基线。
运维与排障要点
- 执行顺序依赖:账户 second 的防火墙强依赖 RAM 共享完成,切勿移除
depends_on;若遇到"TGW 不存在或不可访问"类错误,优先检查 RAM 共享的关联与主体验证是否已生效。 - 附件 ID 提取:
firewall_status[0].transit_gateway_attachment_sync_states[0].attachment_id是计算属性,取决于 AWS 返回的同步状态,读取前请确认防火墙确实处于transit_gateway_id模式(而非vpc_id模式)。 - 60 分钟超时:Create/Delete 默认超时均为 60 分钟,大规模跨账户拓扑或跨区域同步较慢时,可通过
timeouts块调整。 - 凭证安全:示例为演示目的使用 Access Key 直传,建议生产环境改用 IAM Role 与
assume_role/profile组合,并结合data "aws_caller_identity"动态解析账号,保持配置可移植。
至此,你已拥有一个可直接运行的跨账户 Network Firewall + Transit Gateway 参考实现:从 TGW 创建、RAM 共享、防火墙创建到附件接受的全链路,既可在 examples/network-firewall-cross-account-transit-gateway 目录中查看完整源码,也可对照 Provider 内部实现(internal/service/networkfirewall)深入理解每个资源背后的 API 调用与状态等待机制。
【免费下载链接】terraform-provider-awsThe AWS Provider enables Terraform to manage AWS resources.项目地址: https://gitcode.com/GitHub_Trending/te/terraform-provider-aws
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考