为何在Ingress规则列表中间新增规则后Terraform计划显示强制替换?
问题
我需要为负载均衡器的Ingress规则添加新IP,因此在variables.tf文件的app_ingress_rules列表变量中间位置新增了一条规则详情块,具体如下:
variables.tf
variable "app_ingress_rules" { type = list(object({ from_port = number to_port = number protocol = string cidr_block = string })) default = [ { //已存在的ingress_rule1详情 }, { //已存在的ingress_rule2详情 }, #此处为列表中间新增的规则详情 { protocol = "tcp" from_port = 443 to_port = 443 cidr_block = "10.XXX.XX.XXX/32" }, { //已存在的ingress_rule3详情 }, { //已存在的ingress_rule4详情 } }
该变量用于在main.tf中创建aws_security_group_rule资源,代码如下:
main.tf
resource "aws_security_group_rule" "ingress_rules" { count = length(var.app_ingress_rules) type = "ingress" from_port = var.app_ingress_rules[count.index].from_port to_port = var.app_ingress_rules[count.index].to_port protocol = var.app_ingress_rules[count.index].protocol cidr_blocks = [var.app_ingress_rules[count.index].cidr_block] security_group_id = aws_security_group.app_security_group.id }
原本以为仅新增规则不会影响其他已有规则,但执行terraform plan后结果显示:Plan: 3 to add, 0 to change, 2 to destroy.(示例中原有4条规则,在第3位新增后出现此结果)。为何Terraform会销毁并重建新增位置之后的原有Ingress规则?
原因分析
Terraform 通过count创建资源时,会以列表索引作为资源实例的唯一标识符(例如aws_security_group_rule.ingress_rules[0]对应列表第一个元素)。当你在列表中间插入新元素后:
- 原索引2的规则(ingress_rule3)现在变成了索引3
- 原索引3的规则(ingress_rule4)现在变成了索引4
Terraform 会认为原来的ingress_rules[2]和ingress_rules[3]对应的资源已经不存在,需要销毁;同时会创建新的ingress_rules[2](新增的规则)、ingress_rules[3](原ingress_rule3)、ingress_rules[4](原ingress_rule4),这就对应了你看到的3 to add, 2 to destroy的结果。
解决方案
要避免这种销毁重建的问题,推荐使用for_each代替count,基于规则的唯一属性生成稳定的实例键:
修改后的main.tf
resource "aws_security_group_rule" "ingress_rules" { for_each = { for rule in var.app_ingress_rules : "${rule.protocol}-${rule.from_port}-${rule.cidr_block}" => rule } type = "ingress" from_port = each.value.from_port to_port = each.value.to_port protocol = each.value.protocol cidr_blocks = [each.value.cidr_block] security_group_id = aws_security_group.app_security_group.id }
这样Terraform会根据规则的协议、端口和CIDR组合生成唯一键,无论列表顺序如何变化,只要规则内容不变,对应的资源实例就不会被销毁重建。如果必须使用count,则只能将新规则追加到列表末尾,不要插入到中间位置。
内容的提问来源于stack exchange,提问作者Pranit Vedpathak

