{"id":4626,"date":"2025-08-31T11:57:22","date_gmt":"2025-08-31T11:57:22","guid":{"rendered":"https:\/\/geekmungus.co.uk\/?p=4626"},"modified":"2025-08-31T18:12:51","modified_gmt":"2025-08-31T18:12:51","slug":"aws-ec2-instance-replacement-with-no-modifications-terraform","status":"publish","type":"post","link":"https:\/\/geekmungus.co.uk\/?p=4626","title":{"rendered":"AWS EC2 Instance Replacement with No Modifications (Terraform)"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">During my learning of AWS and its deployment via Terraform, I came across an issue where my EC2 instances kept getting destroyed, even when no changes had been made to them. For example in the plan output:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># aws_instance.spoke2-ec2 must be replaced\n-\/+ resource \"aws_instance\" \"spoke2-ec2\" {\n      ~ arn                                  = \"arn:aws:ec2:eu-west-2:97123148471:instance\/i-04c0e1bc6b05b8b57\" -&gt; (known after apply)\n      ~ associate_public_ip_address          = false -&gt; (known after apply)\n      ~ disable_api_stop                     = false -&gt; (known after apply)\n      ~ disable_api_termination              = false -&gt; (known after apply)\n      ~ ebs_optimized                        = false -&gt; (known after apply)\n      + enable_primary_ipv6                  = (known after apply)\n      - hibernation                          = false -&gt; null\n      + host_id                              = (known after apply)\n      + host_resource_group_arn              = (known after apply)\n      ~ id                                   = \"i-04c0e1bc6b05b8b57\" -&gt; (known after apply)\n      ~ instance_initiated_shutdown_behavior = \"stop\" -&gt; (known after apply)\n      + instance_lifecycle                   = (known after apply)\n      ~ instance_state                       = \"running\" -&gt; (known after apply)\n      ~ ipv6_address_count                   = 0 -&gt; (known after apply)\n      ~ ipv6_addresses                       = &#91;] -&gt; (known after apply)\n      + key_name                             = (known after apply)\n      ~ monitoring                           = false -&gt; (known after apply)\n      + outpost_arn                          = (known after apply)\n      + password_data                        = (known after apply)\n      + placement_group                      = (known after apply)\n      ~ placement_partition_number           = 0 -&gt; (known after apply)\n      ~ primary_network_interface_id         = \"eni-048c35632b8cb0cca\" -&gt; (known after apply)\n      ~ private_dns                          = \"ip-10-192-4-11.eu-west-2.compute.internal\" -&gt; (known after apply)\n      ~ private_ip                           = \"10.192.4.11\" -&gt; (known after apply)\n      + public_dns                           = (known after apply)\n      + public_ip                            = (known after apply)\n      ~ secondary_private_ips                = &#91;] -&gt; (known after apply)\n      ~ security_groups                      = &#91; # forces replacement\n          + \"sg-0a015bbb1dc6abac9\",\n        ]\n      + spot_instance_request_id             = (known after apply)\n        tags                                 = {\n            \"Name\" = \"spoke2-ec2\"\n        }\n      + user_data_base64                     = (known after apply)\n      ~ vpc_security_group_ids               = &#91;\n          - \"sg-0a015bbb1dc6abac9\",\n        ] -&gt; (known after apply)<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Notice the &#8220;# forces replacement&#8221; on the security_groups directive. <\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Turns it it was quite a simple fix, although there are lifecycle directives you can add to avoid things being destroyed (or destroyed when you don&#8217;t expect them to be), in my case I was using the wrong directive for adding security groups. Instead of &#8220;security_groups&#8221;, I should have been using &#8220;vpc_security_groups_ids&#8221; instead. Swapping these directives in the Terraform allowed me to plan and apply without destroying my EC2 instances (when no modifications has been made).<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>resource \"aws_instance\" \"spoke1-ec2\" {\n  provider             = aws.spoke1\n  ami                  = \"ami-008ea0202116dbc56\"\n  instance_type        = \"t2.micro\"\n  tenancy              = \"default\"\n  availability_zone    = \"eu-west-2a\"\n  key_name             = \"\"\n  subnet_id            = aws_subnet.spoke1.id\n  #security_groups      = &#91;aws_security_group.spoke1-ec2-sg.id] \n  vpc_security_group_ids = &#91;aws_security_group.spoke1-ec2-sg.id] # avoids replacement each time.\n  iam_instance_profile = aws_iam_instance_profile.ec2_profile1.name\n\n  tags = {\n    Name = \"spoke1-ec2\"\n  }\n}<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">I should be using vpc_security_group_ids, instead of security_groups. The latter is only for default VPC and EC2-Classic.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>During my learning of AWS and its deployment via Terraform, I came across an issue where my EC2 instances kept getting destroyed, even when no changes had been made to them. For example in the plan output: Notice the &#8220;# forces replacement&#8221; on the security_groups directive. Turns it it was quite a simple fix, although &#8230; <a title=\"AWS EC2 Instance Replacement with No Modifications (Terraform)\" class=\"read-more\" href=\"https:\/\/geekmungus.co.uk\/?p=4626\" aria-label=\"Read more about AWS EC2 Instance Replacement with No Modifications (Terraform)\">Read more<\/a><\/p>\n","protected":false},"author":4,"featured_media":4335,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[28,45],"tags":[],"class_list":["post-4626","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-aws","category-terraform"],"_links":{"self":[{"href":"https:\/\/geekmungus.co.uk\/index.php?rest_route=\/wp\/v2\/posts\/4626","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/geekmungus.co.uk\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/geekmungus.co.uk\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/geekmungus.co.uk\/index.php?rest_route=\/wp\/v2\/users\/4"}],"replies":[{"embeddable":true,"href":"https:\/\/geekmungus.co.uk\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=4626"}],"version-history":[{"count":2,"href":"https:\/\/geekmungus.co.uk\/index.php?rest_route=\/wp\/v2\/posts\/4626\/revisions"}],"predecessor-version":[{"id":4628,"href":"https:\/\/geekmungus.co.uk\/index.php?rest_route=\/wp\/v2\/posts\/4626\/revisions\/4628"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/geekmungus.co.uk\/index.php?rest_route=\/wp\/v2\/media\/4335"}],"wp:attachment":[{"href":"https:\/\/geekmungus.co.uk\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=4626"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/geekmungus.co.uk\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=4626"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/geekmungus.co.uk\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=4626"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}