Terraform can manage infrastructure that already exists outside of its state file.

Let’s say you have a database instance created manually in AWS, and you want Terraform to take over its management. You can do this with the terraform import command.

# main.tf
resource "aws_db_instance" "my_rds" {
  allocated_storage    = 20
  engine               = "mysql"
  engine_version       = "8.0"
  instance_class       = "db.t3.micro"
  name                 = "mydb"
  username             = "admin"
  password             = "foobar" # In a real scenario, use a secret manager
  parameter_group_name = "default.mysql8.0"
  skip_final_snapshot  = true
}

First, you define the resource in your Terraform configuration file (main.tf in this example) as if you were creating it anew. The important part here is that the configuration describes the resource you intend to import. Terraform will use this definition to understand what the resource should look like once it’s managed.

Next, you need the unique identifier for the existing resource in your cloud provider. For an AWS RDS instance, this is typically its ARN (Amazon Resource Name) or its instance identifier. You can usually find this in your cloud provider’s console or via their CLI.

Now, you run the terraform import command. The syntax is terraform import <terraform_resource_address> <provider_resource_id>.

In our example, if your RDS instance identifier is my-existing-rds-instance, you would run:

terraform import aws_db_instance.my_rds my-existing-rds-instance

Terraform will then reach out to AWS, find the resource identified by my-existing-rds-instance, and associate it with the aws_db_instance.my_rds resource address in your Terraform state file.

After the import, the resource is now tracked by Terraform. However, the configuration in your main.tf might not perfectly match the actual current configuration of the imported resource. To fix this, you’ll run terraform plan.

terraform plan

Terraform will compare the state file (which now reflects the imported resource) with your configuration file. It will likely show that changes are needed to make the real resource conform to your main.tf definition. These changes are usually additions to your configuration, reflecting the actual attributes of the imported resource.

You’ll see output like:

Terraform will perform the following actions:

  # aws_db_instance.my_rds will be updated in-place
  ~ resource "aws_db_instance" "my_rds" {
      id                                 = "my-existing-rds-instance"
      allocated_storage                  = 20
      # (10 unchanged attributes)

      # (1 block with 1 unchanged attribute)
      tags = {
          "Name" = "my-existing-rds-instance"
      }
    }

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

The key here is that terraform plan after an import doesn’t show additions or deletions; it shows what needs to be changed in your .tf file to match the imported resource’s current state. You then update your main.tf to reflect these attributes.

The most common mistake is assuming terraform import magically makes your .tf file match the resource. It doesn’t. It only updates the state file. The configuration must be manually updated to align with the imported resource’s actual properties. After updating your .tf file to match the output of terraform plan, running terraform plan again should show no changes.

A subtle but crucial point is that terraform import only brings the resource into the state. It doesn’t import data from the resource, like the contents of a database or the files in a storage bucket. It’s about managing the configuration and lifecycle of the resource itself. For complex resources with many attributes, it’s often easier to run terraform plan after import and then copy the discovered attributes into your .tf file, rather than manually writing them all out beforehand.

Once the import is complete and your configuration matches the actual resource, you can manage it like any other Terraform-provisioned resource. The next step is often to import related resources, like database users or security group rules, to fully bring your existing infrastructure under Terraform’s control.

Want structured learning?

Take the full Terraform course →