terraform import
If you're adding Terraform to an environment where Terraform was not being used, often you'll need to import existing resources into your Terraform deployment so they can be managed with your other Terraformed resources. The Terraform documentation describes how to do this and will give you a detailed breakdown of the command; this page provides a tangible example and tries to explain potential pitfalls.
A Word of Warning
Remember when importing resources that you may need to import multiple resources to get everything that might seem like a single component in AWS. If you import an S3 bucket, that does not import its analytics configuration, for instance, which is a completely different Terraform resource.
Importing a resource as a raw Terraform resource
In this case, we're going to be importing an existing S3 bucket from AWS
named my-s3-bucket
into our Terraform deployment as a Terraform
aws_s3_bucket
resource.
Write the Terraform code that will describe this resource in your infrastructure. For our example, we would add this to our Terraform infrastructure:
resource "aws_s3_bucket" "my_s3_bucket" {
bucket = "my-s3-bucket"
tags = {
Name = "My Bucket"
Environment = "Dev"
}
}Run
terraform import
in the namespace that file is in to import the resource. Theimport
command requires two arguments; theADDRESS
and theID
. TheADDRESS
is the terraform resource we just described, soaws_s3_bucket.my_s3_bucket
in the example above. TheID
depends on the resource we're importing; you can find out at the bottom of the documentation for that resource in the Terraform docs. You can see in the docs foraws_s3_bucket
that for that resource, it's just the bucket name. So we would run this command:$ terraform import aws_s3_bucket.my_s3_bucket my-s3-bucket
aws_s3_bucket.my_s3_bucket: Importing from ID "my-s3-bucket"...
aws_s3_bucket.my_s3_bucket: Import prepared!
Prepared aws_s3_bucket for import
aws_s3_bucket.my_s3_bucket: Refreshing state... [id=my-s3-bucket]
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.
Releasing state lock. This may take a few moments...Our resource has been imported. Now if we run a
terraform plan
, we can see that Terraform has added the bucket to the state:$ terraform plan
...
An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
~ update in-place
Terraform will perform the following actions:
# aws_s3_bucket.my_s3_bucket will be updated in-place
~ resource "aws_s3_bucket" "my_s3_bucket" {
+ acl = "private"
arn = "arn:aws:s3:::my-s3-bucket"
bucket = "my-s3-bucket"
bucket_domain_name = "my-s3-bucket.s3.amazonaws.com"
bucket_regional_domain_name = "my-s3-bucket.s3.us-west-2.amazonaws.com"
+ force_destroy = false
hosted_zone_id = "Z3BJ5K6RIION3M"
id = "my-s3-bucket"
region = "us-west-2"
request_payer = "BucketOwner"
~ tags = {
+ "Environment" = "Dev"
"Name" = "My Bucket"
}
versioning {
enabled = false
mfa_delete = false
}
}
Plan: 0 to add, 1 to change, 0 to destroy.Note that it is only changing an existing resource. Also note that the
Name
tag already existed on the existing bucket, so that will not be changed, but that we're adding theEnvironment
tag to the bucket. To make these changes, we'd run aterraform apply
and then we should have a clean plan after that. We can then use this resource as if it had always been in Terraform.
Importing a resource as a component of a module
It's also possible to import an existing resource as a component of a
Terraform module, we just have to change our Terraform code and the
ADDRESS
component of the terraform import
command. So if we wanted
to use the Truss terraform-aws-s3-private-bucket
module for our bucket
instead of the raw S3 resource, we'd have Terraform code like this:
module "my_s3_bucket" {
source = "trussworks/s3-private-bucket/aws"
bucket = "my-s3-bucket"
logging_bucket = "my-logging-bucket"
tags = {
Name = "My Bucket"
Environment = "Dev"
}
}
Then we would run this command:
$ terraform import module.my_s3_bucket.aws_s3_bucket.private_bucket my-s3-bucket
We're getting that ADDRESS
by looking at the code of the module and
seeing where it is actually defining that resource that we want to import;
in this case, it's the
main.tf
file, on this line:
resource "aws_s3_bucket" "private_bucket" {
bucket = local.bucket_id
acl = "private"
...