hashicorp / terraform-provider-aws

The AWS Provider enables Terraform to manage AWS resources.
https://registry.terraform.io/providers/hashicorp/aws
Mozilla Public License 2.0
9.75k stars 9.1k forks source link

Import of EC2 instance always replaces resource on next apply #9279

Open hobbyhorse opened 5 years ago

hobbyhorse commented 5 years ago

I am trying to figure out how to import an EC2 instance into Terraform. Whenever I import the instance, if I run "terraform plan" it always wants to replace the instance.

I have tried an extremely minimal import that just has the AMI id and instance size, and I have tried a complete import where every possible value is specified. Neither seem to work, with the same results.

The initial state:

[ec2-user@ip-172-31-34-85 terraform]$ ls
provider.aws.tf  test1.tf

[ec2-user@ip-172-31-34-85 terraform]$ cat provider.aws.tf
provider "aws" {
  region = "eu-west-1"
}

[ec2-user@ip-172-31-34-85 terraform]$ cat test1.tf
resource "aws_instance" "web" {
  ami           = "ami-0bbc25e23a7640b9b"
  instance_type = "t3.medium"
}

Plan:

[ec2-user@ip-172-31-34-85 terraform]$ terraform plan
Refreshing Terraform state in-memory prior to plan...
The refreshed state will be used to calculate this plan, but will not be
persisted to local or remote state storage.

------------------------------------------------------------------------

An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # aws_instance.web will be created
  + resource "aws_instance" "web" {
      + ami                          = "ami-0bbc25e23a7640b9b"
      + arn                          = (known after apply)
      + associate_public_ip_address  = (known after apply)
      + availability_zone            = (known after apply)
      + cpu_core_count               = (known after apply)
      + cpu_threads_per_core         = (known after apply)
      + get_password_data            = false
      + host_id                      = (known after apply)
      + id                           = (known after apply)
      + instance_state               = (known after apply)
      + instance_type                = "t3.medium"
      + ipv6_address_count           = (known after apply)
      + ipv6_addresses               = (known after apply)
      + key_name                     = (known after apply)
      + network_interface_id         = (known after apply)
      + password_data                = (known after apply)
      + placement_group              = (known after apply)
      + primary_network_interface_id = (known after apply)
      + private_dns                  = (known after apply)
      + private_ip                   = (known after apply)
      + public_dns                   = (known after apply)
      + public_ip                    = (known after apply)
      + security_groups              = (known after apply)
      + source_dest_check            = true
      + subnet_id                    = (known after apply)
      + tenancy                      = (known after apply)
      + volume_tags                  = (known after apply)
      + vpc_security_group_ids       = (known after apply)

      + ebs_block_device {
          + delete_on_termination = (known after apply)
          + device_name           = (known after apply)
          + encrypted             = (known after apply)
          + iops                  = (known after apply)
          + snapshot_id           = (known after apply)
          + volume_id             = (known after apply)
          + volume_size           = (known after apply)
          + volume_type           = (known after apply)
        }

      + ephemeral_block_device {
          + device_name  = (known after apply)
          + no_device    = (known after apply)
          + virtual_name = (known after apply)
        }

      + network_interface {
          + delete_on_termination = (known after apply)
          + device_index          = (known after apply)
          + network_interface_id  = (known after apply)
        }

      + root_block_device {
          + delete_on_termination = (known after apply)
          + iops                  = (known after apply)
          + volume_id             = (known after apply)
          + volume_size           = (known after apply)
          + volume_type           = (known after apply)
        }

      + timeouts {}
    }

Plan: 1 to add, 0 to change, 0 to destroy.

------------------------------------------------------------------------

Note: You didn't specify an "-out" parameter to save this plan, so Terraform
can't guarantee that exactly these actions will be performed if
"terraform apply" is subsequently run.

Apply:


----snip----

Plan: 1 to add, 0 to change, 0 to destroy.

Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value: yes

aws_instance.web: Creating...
aws_instance.web: Still creating... [10s elapsed]
aws_instance.web: Creation complete after 11s [id=i-0875a3e74f53ed82c]

Apply complete! Resources: 1 added, 0 changed, 0 destroyed.

Post creation, lets check plan:

[ec2-user@ip-172-31-34-85 terraform]$ terraform plan
Refreshing Terraform state in-memory prior to plan...
The refreshed state will be used to calculate this plan, but will not be
persisted to local or remote state storage.

aws_instance.web: Refreshing state... [id=i-0875a3e74f53ed82c]

------------------------------------------------------------------------

No changes. Infrastructure is up-to-date.

This means that Terraform did not detect any differences between your
configuration and real physical resources that exist. As a result, no
actions need to be performed.

Now we delete the state file and import the resource:

[ec2-user@ip-172-31-34-85 terraform]$ terraform import aws_instance.web i-0875a3e74f53ed82c
aws_instance.web: Importing from ID "i-0875a3e74f53ed82c"...
aws_instance.web: Import complete!
  Imported aws_instance
aws_instance.web: Refreshing state... [id=i-0875a3e74f53ed82c]

Import successful!

The resources that were imported are shown above. These resources are now in
your Terraform state and will henceforth be managed by Terraform.

Another plan:

[ec2-user@ip-172-31-34-85 terraform]$ terraform plan
Refreshing Terraform state in-memory prior to plan...
The refreshed state will be used to calculate this plan, but will not be
persisted to local or remote state storage.

aws_instance.web: Refreshing state... [id=i-0875a3e74f53ed82c]

------------------------------------------------------------------------

An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
-/+ destroy and then create replacement

Terraform will perform the following actions:

  # aws_instance.web must be replaced
-/+ resource "aws_instance" "web" {
        ami                          = "ami-0bbc25e23a7640b9b"
      ~ arn                          = "arn:aws:ec2:eu-west-1:341009493741:instance/i-0875a3e74f53ed82c" -> (known after apply)
      ~ associate_public_ip_address  = true -> (known after apply)
      ~ availability_zone            = "eu-west-1b" -> (known after apply)
      ~ cpu_core_count               = 1 -> (known after apply)
      ~ cpu_threads_per_core         = 2 -> (known after apply)
      - disable_api_termination      = false -> null
      - ebs_optimized                = false -> null
        get_password_data            = false
      + host_id                      = (known after apply)
      ~ id                           = "i-0875a3e74f53ed82c" -> (known after apply)
      ~ instance_state               = "running" -> (known after apply)
        instance_type                = "t3.medium"
      + ipv6_address_count           = (known after apply)
      + ipv6_addresses               = (known after apply) # forces replacement
      + key_name                     = (known after apply)
      - monitoring                   = false -> null
      ~ network_interface_id         = "eni-077ae2820afc460a7" -> (known after apply)
      + password_data                = (known after apply)
      + placement_group              = (known after apply)
      ~ primary_network_interface_id = "eni-077ae2820afc460a7" -> (known after apply)
      ~ private_dns                  = "ip-172-31-2-15.eu-west-1.compute.internal" -> (known after apply)
      ~ private_ip                   = "172.31.2.15" -> (known after apply)
      ~ public_dns                   = "ec2-34-255-30-73.eu-west-1.compute.amazonaws.com" -> (known after apply)
      ~ public_ip                    = "34.255.30.73" -> (known after apply)
      ~ security_groups              = [
          - "default",
        ] -> (known after apply)
        source_dest_check            = true
      ~ subnet_id                    = "subnet-25d0f941" -> (known after apply)
      - tags                         = {
          - "Backup" = "True"
        } -> null
      ~ tenancy                      = "default" -> (known after apply)
      + volume_tags                  = (known after apply)
      ~ vpc_security_group_ids       = [
          - "sg-12b9c774",
        ] -> (known after apply)

      - credit_specification {
          - cpu_credits = "unlimited" -> null
        }

      + ebs_block_device {
          + delete_on_termination = (known after apply)
          + device_name           = (known after apply)
          + encrypted             = (known after apply)
          + iops                  = (known after apply)
          + snapshot_id           = (known after apply)
          + volume_id             = (known after apply)
          + volume_size           = (known after apply)
          + volume_type           = (known after apply)
        }

      + ephemeral_block_device { # forces replacement
          + device_name  = (known after apply)
          + no_device    = (known after apply)
          + virtual_name = (known after apply)
        }

      + network_interface {
          + delete_on_termination = (known after apply)
          + device_index          = (known after apply)
          + network_interface_id  = (known after apply)
        }

      ~ root_block_device {
          ~ delete_on_termination = true -> (known after apply)
          ~ iops                  = 100 -> (known after apply)
          ~ volume_id             = "vol-0c46eb9d028b04d9a" -> (known after apply)
          ~ volume_size           = 8 -> (known after apply)
          ~ volume_type           = "gp2" -> (known after apply)
        }

        timeouts {}
    }

Plan: 1 to add, 0 to change, 1 to destroy.
hobbyhorse commented 5 years ago

This is on version 0.12.3. Everything works as expected on version 0.11.14.

babbottscott commented 5 years ago

Seeing the same on 0.12.6. This is something that also happened long ago with block_device.

I see this on instances that were imported. I haven't tried with a new instance.

Items I have not set in my aws_instance resource, but must include in ignore_changes to prevent recreation: ipv6_addresses, ebs_optimized, security_groups, ephemeral_block_device,

Update: only on provider.aws 2.11. On provider.aws 2.22, only ebs_optimized still causes an issue.

xacaxulu commented 5 years ago

+1

OhBonsai commented 4 years ago

Seeing the same on

Env

Terraform v0.12.24
+ provider.aws v2.58.0

MacBook Pro (13-inch, 2017, Two Thunderbolt 3 ports)
macos Mojave

Recurrent

resource "aws_instance" "test1" {
  ami           = "ami-0ea0e838b9153f3a9"
  instance_type = "t3.micro"
  security_groups =  ["sg-0846a52c36953458e"]
  subnet_id =  "subnet-0068193f3002512c7"
}
penzaideMacBook-Pro:aws_test penzai$ terraform import aws_instance.test1 i-0ec30eb274f4f98be
aws_instance.test1: Importing from ID "i-0ec30eb274f4f98be"...
aws_instance.test1: Import prepared!
  Prepared aws_instance for import
aws_instance.test1: Refreshing state... [id=i-0ec30eb274f4f98be]

Import successful!

The resources that were imported are shown above. These resources are now in
your Terraform state and will henceforth be managed by Terraform.
penzaideMacBook-Pro:aws_test penzai$ terraform plan
Refreshing Terraform state in-memory prior to plan...
The refreshed state will be used to calculate this plan, but will not be
persisted to local or remote state storage.

aws_instance.test1: Refreshing state... [id=i-0ec30eb274f4f98be]

------------------------------------------------------------------------

An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
  + create
-/+ destroy and then create replacement

Terraform will perform the following actions:

  # aws_instance.test1 must be replaced

I just test on CentOS Linux release 7.5.1804 (Core).Recreate aws_instance which same with macos

angeloudy commented 3 years ago

Is this solved? I am having exactly the same issue now, with terraform 0.12.26

hobbyhorse commented 3 years ago

It seemed to be solved for me later on.

If it is still happening to you after deleting the cached provider files (where they are stored I am not always 100% sure, but https://www.terraform.io/docs/commands/cli-config.html#:~:text=exclusions%20out%20yourself.)-,Provider%20Plugin%20Cache,be%20downloaded%20for%20each%20configuration. ) then come back and post the TF and provider versions you are having the problem with.

kuldeepjain commented 3 years ago

It's happening for me for aws resources such as S3 bucket etc. I posted my question on SO: Terraform (0.12.29) import not working as expected; import succeeded but plan shows destroy & recreate

My Env:

Local machine: macOS v10.14.6

Terraform v0.12.29
+ provider.aws v3.14.1
+ provider.null v2.1.2
+ provider.random v2.3.1
+ provider.template v2.1.2
rish9511 commented 3 years ago

Same behavior when creating a new instance

Workaround is to ignore the changes that are forcing the replacement. Terraform v0.12.24 points out the changes that cause the EC2 instance to be replaced. In my case it was the security_groups attributes that was forcing the replacement.

Following is the code snippet that I had to add

    lifecycle {
        ignore_changes = [security_groups]
   }

Similar solutions have been proposed earlier

Environment

Terraform v0.12.24
+ provider.aws v2.70.0
+ provider.local v1.4.0
+ provider.null v2.1.2
+ provider.template v2.2.0
+ provider.tls v2.2.0
vallard commented 3 years ago

I found that by replacing security_groups with vpc_security_group_ids it doesn't cycle.

nickolashkraus commented 3 years ago

From the Terraform documentation:

security_groups - (Optional, EC2-Classic and default VPC only) A list of security group names (EC2-Classic) or IDs (default VPC) to associate with.

NOTE: If you are creating Instances in a VPC, use vpc_security_group_ids instead.

Therefore, if your EC2 instance is not a classic EC2 instance and not in the default VPC, use vpc_security_group_ids and the Terraform plan will not force a replacement of the imported instance.

kochsecurity commented 2 years ago

For me, the user_data is forcing a replacement of the instance. Anyone knows a way around this ? I don't want to replace the instance. Thanks

jasonpit commented 2 years ago

@kochsecurity - I had the same issue and used the lifecycle feature for the instance

lifecycle { ignore_changes = [ami] }