Terraform为AWS EC2实例加安全组触发重建,如何实现原地更新?
问题
初始Terraform配置用于创建EC2实例及SSH安全组:
resource "aws_instance" "ec2" { ami = "ami-06791f9213cbb608b" instance_type = "t2.micro" key_name = "terraform-key" depends_on = [aws_security_group.ssh_group] security_groups = [aws_security_group.ssh_group.name] } resource "aws_security_group" "ssh_group" { name = "ssh_group" description = "SSH Security Group" ingress { description = "SSH" from_port = 22 to_port = 22 protocol = "tcp" cidr_blocks = ["0.0.0.0/0"] } }
执行后成功创建资源。
当新增HTTPS安全组并附加到EC2实例时,更新后的配置:
resource "aws_instance" "ec2" { ami = "ami-06791f9213cbb608b" instance_type = "t2.micro" key_name = "terraform-key" depends_on = [aws_security_group.ssh_group, aws_security_group.https_group] security_groups = [aws_security_group.ssh_group.name, aws_security_group.https_group.name] } resource "aws_security_group" "ssh_group" { name = "ssh_group" description = "SSH Security Group" ingress { description = "SSH" from_port = 22 to_port = 22 protocol = "tcp" cidr_blocks = ["0.0.0.0/0"] } } resource "aws_security_group" "https_group" { name = "https_group" description = "HTTPs Security Group" ingress { description = "HTTPs" from_port = 443 to_port = 443 protocol = "tcp" cidr_blocks = ["0.0.0.0/0"] } }
此时Terraform会销毁原有EC2实例并重建,但AWS控制台仅需直接附加安全组即可原地更新。
疑问:
- 为何两者行为不一致?是否存在配置疏漏?
- 如何确保实例类型变更时也能实现原地更新?
解答
1. 行为差异原因及配置疏漏
Terraform中的security_groups参数是为EC2-Classic网络设计的,在当前主流的VPC环境下使用该参数时,AWS Provider会将其视为实例的不可变属性——当列表内容变化时,Provider会判定需要销毁并重建实例来完成更新。
而AWS控制台操作的是VPC实例的安全组关联,调用的是AWS「修改实例安全组」的专用API,对应Terraform中专门用于VPC实例的vpc_security_group_ids参数,该参数支持原地更新,不需要重建实例。
你的配置疏漏就在于使用了security_groups而非vpc_security_group_ids(当前AWS默认创建的实例都在VPC环境中)。
2. 修正安全组配置实现原地更新
将aws_instance中的security_groups替换为vpc_security_group_ids,同时可以移除手动添加的depends_on(Terraform会自动通过资源引用关系处理依赖)。修正后的配置如下:
resource "aws_instance" "ec2" { ami = "ami-06791f9213cbb608b" instance_type = "t2.micro" key_name = "terraform-key" vpc_security_group_ids = [aws_security_group.ssh_group.id, aws_security_group.https_group.id] } resource "aws_security_group" "ssh_group" { name = "ssh_group" description = "SSH Security Group" ingress { description = "SSH" from_port = 22 to_port = 22 protocol = "tcp" cidr_blocks = ["0.0.0.0/0"] } } resource "aws_security_group" "https_group" { name = "https_group" description = "HTTPs Security Group" ingress { description = "HTTPs" from_port = 443 to_port = 443 protocol = "tcp" cidr_blocks = ["0.0.0.0/0"] } }
修改后执行terraform apply,Terraform会调用AWS的安全组修改API,直接在原有实例上附加新安全组,无需重建。
3. 实例类型变更实现原地更新
默认情况下,Terraform修改instance_type会触发实例重建。要实现原地更新,需要在aws_instance资源中添加lifecycle块,开启allow_stop_on_update选项——该选项会让Terraform在修改实例类型前先停止实例,完成类型修改后再启动实例,整个过程无需销毁重建。
配置示例:
resource "aws_instance" "ec2" { ami = "ami-06791f9213cbb608b" instance_type = "t3.micro" # 修改实例类型 key_name = "terraform-key" vpc_security_group_ids = [aws_security_group.ssh_group.id, aws_security_group.https_group.id] lifecycle { allow_stop_on_update = true } }
注意:该操作要求实例处于可停止状态,若实例是Spot实例、关联了无法停止的资源(如绑定了禁止停止释放的弹性IP),则无法使用该方式原地更新。
内容的提问来源于stack exchange,提问作者Anmol Vijaywargiya

