Terraform源模块用data块致资源意外重建问题求助
Terraform Data块依赖导致的资源连锁重建问题
在Terraform源模块中使用data块处理依赖时遇到异常:以VPC、Subnet和CloudWatch为例,对Amazon CloudWatch进行微小变更(如修改retention_days),会触发VPC和Subnet的重建。
具体场景:VPC依赖CloudWatch(VPC流日志需要CloudWatch日志组名称),Subnet依赖VPC(使用VPC ID)。CloudWatch的微小变更会强制重建VPC流日志,进而引发VPC重建,最终导致Subnet被重建。
该问题仅在使用data块处理依赖时出现,与云提供商无关。移除源模块中的data块可得到预期结果,但升级Terraform及提供商版本无法解决此问题。
相关配置代码
main.tf
module "cloudwatch" { for_each = var.Amazon_CloudWatch source = "./source_modules/cloudwatch" cloud_watch_group_name = each.value["cloud_watch_group_name"] retention_days = each.value["retention_days"] } module "vpc" { for_each = var.AWS_VPC_Virtual_Private_Cloud source = "./source_modules/vpc" vpc_name = each.value["vpc_name"] data_cloud_watch_name = each.value["data_cloud_watch_name"] vpc_cidr = each.value["vpc_cidr"] depends_on = [module.cloudwatch] } module "subnet" { for_each = var.AWS_Subnet source = "./source_modules/subnet" subnet_cidr = each.value["subnet_cidr"] subnet_name = each.value["subnet_name"] data_vpc_name = each.value["data_vpc_name"] depends_on=[module.vpc] }
source_modules/vpc/main.tf
resource "aws_vpc" "main" { cidr_block = var.vpc_cidr tags = { Name = var.vpc_name } } resource "aws_iam_role" "example" { name = "example111" assume_role_policy = data.aws_iam_policy_document.assume_role.json } resource "aws_iam_role_policy" "example" { name = "example111" role = aws_iam_role.example.id policy = data.aws_iam_policy_document.example.json } resource "aws_flow_log" "example" { iam_role_arn = aws_iam_role.example.arn log_destination = data.aws_cloudwatch_log_group.test_clg.arn traffic_type = "ALL" vpc_id = aws_vpc.main.id }
source_modules/vpc/variables.tf
variable "data_cloud_watch_name" { type = string } variable "vpc_name" { type = string } variable "vpc_cidr" { type = string }
source_modules/vpc/data.tf
data "aws_cloudwatch_log_group" "test_clg" { name = var.data_cloud_watch_name } data "aws_iam_policy_document" "assume_role" { statement { effect = "Allow" principals { type = "Service" identifiers = ["vpc-flow-logs.amazonaws.com"] } actions = ["sts:AssumeRole"] } } data "aws_iam_policy_document" "example" { statement { effect = "Allow" actions = [ "logs:CreateLogGroup", "logs:CreateLogStream", "logs:PutLogEvents", "logs:DescribeLogGroups", "logs:DescribeLogStreams", ] resources = ["*"] } }
source_modules/subnet/main.tf
resource "aws_subnet" "main" { vpc_id = data.aws_vpc.vpcid.id cidr_block = var.subnet_cidr tags = { Name = var.subnet_name } }
source_modules/subnet/data.tf
data "aws_vpc" "vpcid" { tags = { Name=var.data_vpc_name } }
source_modules/subnet/variables.tf
variable "subnet_cidr" { type = string } variable "subnet_name" { type = string } variable "data_vpc_name" { type = string }
source_modules/cloudwatch/main.tf
resource "aws_cloudwatch_log_group" "test" { name = var.cloud_watch_group_name retention_in_days = var.retention_days }
source_modules/cloudwatch/variables.tf
variable "cloud_watch_group_name" { type = string } variable "retention_days" { type = number }
Terraform Plan执行结果
**Terraform Plan 结果** terraform plan module.cloudwatch["logging_01"].aws_cloudwatch_log_group.test: 刷新状态中... [id=testdataissuecwg1211q] module.vpc["vpc_01"].aws_vpc.main: 刷新状态中... [id=vpc-0dba4643e6f19061a] module.vpc["vpc_01"].aws_iam_role.example: 刷新状态中... [id=stackissue1qw] module.vpc["vpc_01"].aws_iam_role_policy.example: 刷新状态中... [id=stackissue1qw:stackissue1qw] module.vpc["vpc_01"].aws_flow_log.example: 刷新状态中... [id=fl-0bf918ffe07281dd4] module.subnet["subnet_01"].aws_subnet.main: 刷新状态中... [id=subnet-0ec569f1c4cb9ef15] Terraform 使用选定的提供商生成以下执行计划。资源操作使用以下符号表示: ~ 原地更新 -/+ 销毁后重新创建 <= 读取(数据资源) Terraform 将执行以下操作: # module.cloudwatch["logging_01"].aws_cloudwatch_log_group.test 将被原地更新 ~ resource "aws_cloudwatch_log_group" "test" { id = "testdataissuecwg1211q" name = "testdataissuecwg1211q" ~ retention_in_days = 30 -> 60 tags = {} # (4 个未变更属性被隐藏) } # module.subnet["subnet_01"].data.aws_vpc.vpcid 将在应用阶段读取 # (依赖于存在待变更的资源或模块) <= data "aws_vpc" "vpcid" { + arn = (应用后确定) + cidr_block = (应用后确定) + cidr_block_associations = (应用后确定) + default = (应用后确定) + dhcp_options_id = (应用后确定) + enable_dns_hostnames = (应用后确定) + enable_dns_support = (应用后确定) + enable_network_address_usage_metrics = (应用后确定) + id = (应用后确定) + instance_tenancy = (应用后确定) + ipv6_association_id = (应用后确定) + ipv6_cidr_block = (应用后确定) + main_route_table_id = (应用后确定) + owner_id = (应用后确定) + state = (应用后确定) + tags = { + "Name" = "testvpc0111" } } # module.subnet["subnet_01"].aws_subnet.main 必须被替换 -/+ resource "aws_subnet" "main" { ~ arn = "arn:aws:ec2:us-east-1:************:subnet/subnet-0ec569f1c4cb9ef15" -> (应用后确定) ~ availability_zone = "us-east-1c" -> (应用后确定) ~ availability_zone_id = "use1-az4" -> (应用后确定) - enable_lni_at_device_index = 0 -> null ~ id = "subnet-0ec569f1c4cb9ef15" -> (应用后确定) + ipv6_cidr_block_association_id = (应用后确定) - map_customer_owned_ip_on_launch = false -> null ~ owner_id = "************" -> (应用后确定) ~ private_dns_hostname_type_on_launch = "ip-name" -> (应用后确定) tags = { "Name" = "testdatasnet1" } ~ vpc_id = "vpc-0dba4643e6f19061a" # 强制替换 -> (应用后确定) # 强制替换 # (8 个未变更属性被隐藏) } # module.vpc["vpc_01"].data.aws_cloudwatch_log_group.test_clg 将在应用阶段读取 # (依赖于存在待变更的资源或模块) <= data "aws_cloudwatch_log_group" "test_clg" { + arn = (应用后确定) + creation_time = (应用后确定) + id = (应用后确定) + kms_key_id = (应用后确定) + log_group_class = (应用后确定) + name = "testdataissuecwg1211q" + retention_in_days = (应用后确定) + tags = (应用后确定) } # module.vpc["vpc_01"].data.aws_iam_policy_document.assume_role 将在应用阶段读取 # (依赖于存在待变更的资源或模块) <= data "aws_iam_policy_document" "assume_role" { + id = (应用后确定) + json = (应用后确定) + statement { + actions = [ + "sts:AssumeRole", ] + effect = "Allow" + principals { + identifiers = [ + "vpc-flow-logs.amazonaws.com", ] + type = "Service" } } } # module.vpc["vpc_01"].data.aws_iam_policy_document.example 将在应用阶段读取 # (依赖于存在待变更的资源或模块) <= data "aws_iam_policy_document" "example" { + id = (应用后确定) + json = (应用后确定) + statement { + actions = [ + "logs:CreateLogGroup", + "logs:CreateLogStream", + "logs:DescribeLogGroups", + "logs:DescribeLogStreams", + "logs:PutLogEvents", ] + effect = "Allow" + resources = [ + "*", ] } } # module.vpc["vpc_01"].aws_flow_log.example 必须被替换 -/+ resource "aws_flow_log" "example" { ~ arn = "arn:aws:ec2:us-east-1:************:vpc-flow-log/fl-0bf918ffe07281dd4" -> (应用后确定) ~ id = "fl-0bf918ffe07281dd4" -> (应用后确定) ~ log_destination = "arn:aws:logs:us-east-1:************:log-group:testdataissuecwg1211q" # 强制替换 -> (应用后确定) # 强制替换 ~ log_format = "${version} ${account-id} ${interface-id} ${srcaddr} ${dstaddr} ${srcport} ${dstport} ${protocol} ${packets} ${bytes} ${start} ${end} ${action} ${log-status}" -> (应用后确定) ~ log_group_name = "testdataissuecwg1211q" -> (应用后确定) - tags = {} -> null ~ tags_all = {} -> (应用后确定) # (5 个未变更属性被隐藏) } # module.vpc["vpc_01"].aws_iam_role.example 将被原地更新 ~ resource "aws_iam_role" "example" { ~ assume_role_policy = jsonencode( { - Statement = [ - { - Action = "sts:AssumeRole" - Effect = "Allow" - Principal = { - Service = "vpc-flow-logs.amazonaws.com" } }, ] - Version = "2012-10-17" } ) -> (应用后确定) id = "stackissue1qw" name = "stackissue1qw" tags = {} # (8 个未变更属性被隐藏) # (1 个未变更块被隐藏) } # module.vpc["vpc_01"].aws_iam_role_policy.example 将被原地更新 ~ resource "aws_iam_role_policy" "example" { id = "stackissue1qw:stackissue1qw" name = "stackissue1qw" ~ policy = jsonencode( { - Statement = [ - { - Action = [ - "logs:PutLogEvents", - "logs:DescribeLogStreams", - "logs:DescribeLogGroups", - "logs:CreateLogStream", - "logs:CreateLogGroup", ] - Effect = "Allow" - Resource = "*" }, ] - Version = "2012-10-17" } ) -> (应用后确定) # (1 个未变更属性被隐藏) } 计划:添加 2 个资源,变更 3 个资源,销毁 2 个资源。
内容的提问来源于stack exchange,提问作者Malathi Dhandapani
相关产品推荐
相关产品推荐

