It is always more tempting to use Terraform to provision shining, brand-new infrastructure. However, many times you need to tone down your expectations and work with existing infrastructure for various organizational reasons.

The question is – can you manage an existing AWS EC2 instance or any other resource using Terraform?

Yes, you can use Terraform to import existing resources such as an EC2 instance or anything else into a Terraform state. Terraform provides a special command terraform import to facilitate this functionality. Once an EC2 instance is imported, you can use Terraform to make changes to the instance such as modifying the configuration or scaling it up or down or even destroying it completely.

In this post, I’m going to show you how to use Terraform to import an existing EC2 instance in a step-by-step manner.

1 – Why the Need for Terraform Import?

There are a couple of reasons for this:

  • First, Terraform is a relatively new tool compared to cloud platforms such as AWS, Azure, etc. Chances are that when you decide to adopt Terraform for your project or application, you already have some infrastructure in place.
  • Second, teams starting with cloud deployments often tend to provision infrastructure manually due to a lack of time to learn and utilize Terraform.

However, when your application becomes stable, managing the infrastructure becomes a problem and you turn towards infrastructure-as-code tools such as Terraform.

This is where the terraform import command comes in handy. The command helps you read real-world infrastructure and update the local state so that future changes to the same infra can be managed by Terraform.

In the next section, I will walk you through an example of importing an existing resource.

2 – Terraform Import Existing EC2 Example

Let us go through this entire activity in a step-by-step manner.

Create an EC2 Instance Manually

To demonstrate the import capability of Terraform, you need an existing resource. Therefore, we will create an instance through the AWS Console.

terraform import existing ec2

I have named the instance “demo-ec2-instance” and it is of type t2.micro.

Setup the Terraform Provider

The next step is to create the configuration file and set up the Terraform provider.

First, create the project directory.

$ mkdir terraform-existing-ec2-demo
$ cd terraform-existing-ec2-demo

Now, create a file named terraform.tf to configure the AWS Provider.

terraform {
    required_providers {
        aws = {
            source = "hashicorp/aws"
            version = "~> 4.4.0"
        }
    }
    required_version = ">=1.1.0"
}

Moving on, you need to create another file named main.tf with the below code:

provider "aws" {
    region = "us-west-2"
    profile = "terraform-user"
}

resource "aws_instance" "demo-instance" {
  ami           = "unknown"
  instance_type = "unknown"
}

The first block is the provider block. You use it to configure the AWS Provider with the region and profile.

The next block is the resource block. This block defines the actual resource i.e. the AWS EC2 instance.

Though you are trying to import an existing EC2 instance, you still need to define this resource block because Terraform will not automatically generate it for you. Think of it as more of a placeholder. You can provide some arguments such as ami and instance_type. Anyways, their value is currently unknown. But we will align them in the next step.

Next, you need to run the terraform init command to initialize the workspace and download the provider binaries.

If you want to know more about providers, check out my detailed post on creating a new EC2 instance with Terraform.

Import Existing EC2 with Terraform

You can now try importing the EC2 instance. Execute the below command.

$ terraform import aws_instance.demo-instance i-08a6ae26fa5f7e632

If the command was successful, you should see an output like below:

aws_instance.demo-instance: Importing from ID "i-08a6ae26fa5f7e632"...
aws_instance.demo-instance: Import prepared!
  Prepared aws_instance for import
aws_instance.demo-instance: Refreshing state... [id=i-08a6ae26fa5f7e632]

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.

This command basically maps the aws_instance.demo-instance to the existing EC2 instance using the instance ID. In simple terms, it means that the Terraform state is now aware of the EC2 instance.

Terraform also creates the terraform.tfstate file that contains the entire information about the instance.

Check Terraform Plan for Existing EC2

At this point, you should see the output of the terraform plan command without making any changes.

Terraform will perform the following actions:

  # aws_instance.demo-instance must be replaced
-/+ resource "aws_instance" "demo-instance" {
      ~ ami                                  = "ami-095413544ce52437d" -> "unknown" # forces replacement
      ~ arn                                  = "arn:aws:ec2:us-west-2:711857837850:instance/i-08a6ae26fa5f7e632" -> (known after apply)
      ~ associate_public_ip_address          = true -> (known after apply)
      ~ availability_zone                    = "us-west-2a" -> (known after apply)

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

Surprisingly, Terraform wants to destroy and re-create the instance rather than update the resource.

This defeats the purpose of importing existing infrastructure as you can simply ask Terraform to create a completely new instance without worrying about the existing one.

But the reason for this decision by Terraform is the gap between the configuration and existing resources. In our example, the difference in the value of ami id is forcing the replacement.

To fix the issue, you need to align the configuration with the real resource.

Align the Configuration for Existing EC2

Let us update the configuration file with the correct value of the AMI id.

provider "aws" {
    region = "us-west-2"
    profile = "terraform-user"
}

resource "aws_instance" "demo-instance" {
  ami           = "ami-095413544ce52437d"
  instance_type = "unknown"
}

If you run terraform plan now, you should see the below output:

aws_instance.demo-instance: Refreshing state... [id=i-08a6ae26fa5f7e632]

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
  ~ update in-place

Terraform will perform the following actions:

  # aws_instance.demo-instance will be updated in-place
  ~ resource "aws_instance" "demo-instance" {
        id                                   = "i-08a6ae26fa5f7e632"
      ~ instance_type                        = "t2.micro" -> "unknown"
      ~ tags                                 = {
          - "Name" = "demo-ec2-instance" -> null
        }
      ~ tags_all                             = {
          - "Name" = "demo-ec2-instance"
        } -> (known after apply)
        # (26 unchanged attributes hidden)

        # (6 unchanged blocks hidden)
    }

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

Things have changed considerably. Terraform is now modifying our instance instead of destroying and re-creating it.

However, there is still a lot of drift in the configuration. The instance_type and tags are still different and need further alignment.

Update the main.tf file once again.

provider "aws" {
    region = "us-west-2"
    profile = "terraform-user"
}

resource "aws_instance" "demo-instance" {
  ami           = "ami-095413544ce52437d"
  instance_type = "t2.micro"

  tags = {
      "Name": "demo-ec2-instance"
  }
}

If you run terraform plan now, you’ll find that the configuration is completely aligned.

aws_instance.demo-instance: Refreshing state... [id=i-08a6ae26fa5f7e632]

No changes. Your infrastructure matches the configuration.

Terraform has compared your real infrastructure against your configuration and found no differences, so no changes are needed.

Basically, we have successfully imported the existing EC2 instance to Terraform.

Manage the Imported EC2 instance with Terraform

Let us make further changes to the EC2 instance by adding another tag value to the configuration.

provider "aws" {
    region = "us-west-2"
    profile = "terraform-user"
}

resource "aws_instance" "demo-instance" {
  ami           = "ami-095413544ce52437d"
  instance_type = "t2.micro"

  tags = {
      "Name": "demo-ec2-instance",
      "Status": "imported"
  }
}

You can now execute terraform plan once again.

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
  ~ update in-place

Terraform will perform the following actions:

  # aws_instance.demo-instance will be updated in-place
  ~ resource "aws_instance" "demo-instance" {
        id                                   = "i-08a6ae26fa5f7e632"
      ~ tags                                 = {
          + "Status" = "imported"
            # (1 unchanged element hidden)
        }
      ~ tags_all                             = {
          + "Status" = "imported"
            # (1 unchanged element hidden)
        }
        # (27 unchanged attributes hidden)

        # (6 unchanged blocks hidden)
    }

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

As you can see, everything else is aligned. Terraform is going to perform an in-place update to modify the tag values of our EC2 instance.

You can now perform any operations on the EC2 instance using Terraform.

3 – Terraform import existing EC2 best practices

While importing an existing resource using Terraform is an extremely useful feature, you would do well to follow a few best practices:

  • You should test the import process in a non-production environment before looking to implement it on any production resources.
  • Make sure you understand the implications of importing an existing resource such as an EC2 instance. It may cause conflicts with changes made manually outside of Terraform’s scope.
  • Before you import an existing resource, review the Terraform configuration to ensure that it is configured correctly to manage the imported resource. It is also good practice to first verify that the resource exists and that you have the necessary permissions to import it.
  • Use the terraform plan command after importing a resource to properly align the resource state with the configuration file.
  • You should be extra careful when importing resources with dependencies on other resources. Make sure that you import the correct dependencies.

Conclusion

Using Terraform to import an existing resource such as EC2 is a more common thing than you might initially think.

A large percentage of projects that move to infrastructure-as-code start with manual cloud setup. Only after things are a little well-established, do the development teams move towards tools like Terraform to manage the infrastructure.

With this post, you can start exploring the terraform import command to import existing resources to Terraform.

Want to learn more about Terraform features? Check out the below posts:

If you liked this post or found this useful, consider sharing it with friends and colleagues.


Saurabh Dashora

Saurabh is a Software Architect with over 12 years of experience. He has worked on large-scale distributed systems across various domains and organizations. He is also a passionate Technical Writer and loves sharing knowledge in the community.

0 Comments

Leave a Reply

Your email address will not be published. Required fields are marked *