Import Terraform Configuration
In this tutorial, you will import an existing Docker container and image into a Terraform project. By doing so, you will learn strategies and considerations for importing real-world infrastructure into Terraform.
When you create new infrastructure with Terraform, you will usually use the following workflow:
Write Terraform configuration that defines the infrastructure you want to create.
Review the Terraform plan to ensure the configuration will result in the expected infrastructure.
Apply the configuration to have Terraform create your infrastructure.
When you create infrastructure with Terraform, it stores information about your infrastructure in its state file. You can update your infrastructure by first changing your configuration, and then using Terraform to plan and apply the required changes. Terraform uses the information it stores in your state file to plan the changes it will make to your infrastructure.
Terraform also supports bringing existing infrastructure under its management. To do so, you can use the import
command to migrate resources into your Terraform state file. The import
command does not currently generate the configuration for the imported resource, so you must write the corresponding configuration block to map the imported resource to it.
Bringing existing infrastructure under Terraform's control involves five steps:
- Identify the existing infrastructure you will import.
- Import infrastructure into your Terraform state.
- Write Terraform configuration that matches that infrastructure.
- Review the Terraform plan to ensure the configuration matches the expected state and infrastructure.
- Apply the configuration to update your Terraform state.
In this tutorial, first you will create a Docker container with the Docker CLI. Next, you will import it into a new Terraform project. Then you will update the container's configuration using Terraform before finally destroying it when you are done.
Warning: Importing infrastructure manipulates Terraform state in ways
that could leave existing Terraform projects in an invalid state. Make a backup
of your terraform.tfstate
file and .terraform
directory before using
Terraform import on a real Terraform project, and store them securely.
Prerequisites
In order to follow this tutorial you will need the following.
The Terraform CLI.
Docker installed and running.
The git CLI.
Create a Docker container
Create a container named hashicorp-learn
using the latest NGINX
image from Docker Hub, and publish that container's port 80 (HTTP) to your local
host system's port 8080. You will import this container in this tutorial.
Docker will output a message similar to the following as it downloads and runs the nginx image.
Verify that the container is running with docker ps
.
Visit the address 0.0.0.0:8080
in your web browser to see the NGINX default
index page.
Now you have a Docker image and container to import into your project and manage with Terraform.
Import the container into Terraform
Now, clone the example repository.
Next, change the directory.
In this directory are three Terraform configuration files that you will use in this tutorial:
terraform.tf
configures Terraform and provider versionsmain.tf
configures the Docker providerdocker.tf
will contain the configuration necessary to manage the Docker container you created in the previous step
Initialize your Terraform project with terraform init
.
Next, define an empty docker_container
resource in your docker.tf
file,
which represents a Docker container with the Terraform resource ID
docker_container.web
.
Next, run docker ps
to find the name of the container you want to import - in
this case, the container you created in the previous step.
Now run terraform import
to attach the existing Docker container to the
docker_container.web
resource you just created. Terraform import requires this
Terraform resource ID and the full Docker container ID. In the following
example, the command docker inspect --format="{{.ID}}" hashicorp-learn
returns the
full SHA256 container ID.
Note: The ID accepted by terraform import
varies by resource type and
is documented in the provider documentation for any resource that can be
imported to Terraform. For this example, consult the Docker provider
documentation.
Now verify that the container has been imported into your Terraform state by
running terraform show
.
This state contains everything that Terraform knows about the Docker container you just imported. However, Terraform import does not create the configuration for the resource.
Create configuration
You'll need to create Terraform configuration before you can use Terraform to manage this container.
Run terraform plan
. Terraform will show errors for the missing required
arguments image
and name
. Terraform cannot generate a plan for a resource
that is missing required arguments.
There are two approaches to update the configuration in docker.tf
to match the
state you imported. You can either accept the entire current state of the
resource into your configuration as-is or cherry-pick the required attributes
into your configuration one at a time. You may find both of these approaches
useful in different circumstances.
- Using the current state is often faster, but can result in an overly verbose configuration since every attribute is included in the state, whether it is necessary to include in your configuration or not.
- Cherry-picking the required attributes can lead to more manageable configuration, but requires you to understand which attributes need to be set in the configuration.
To use current state as configuration, you will:
After importing the resource, copy the Terraform state into a configuration file. You will base the configuration for the resource on its definition in state.
Run
terraform plan
to identify and remove read-only configuration arguments.Re-run
terraform plan
to confirm the configuration is correct.Run
terraform apply
to finish synchronizing your configuration, state, and infrastructure.
Use terraform show
to copy your Terraform state into your docker.tf
file.
Warning: The >
symbol will replace the entire contents of docker.tf
with the output of the terraform show
command. While this works for this
example, importing a resource into a configuration that already manages
resources will require you to edit the output of terraform show
to remove
existing resources whose configuration you do not want to replace wholesale, and
merge the new resources into your existing configuration.
Inspect the docker.tf
file to see that its contents have been replaced with
the output of the terraform show
command you just ran.
Now run terraform plan
. Terraform will show warnings and errors about a
deprecated argument ('links'), and several read-only arguments (ip_address
,
network_data
, gateway
, ip_prefix_length
, id
).
These read-only arguments are values that Terraform stores in its state for
Docker containers but that it cannot set via configuration since they are
managed internally by Docker. Terraform can set the links
argument with
configuration, but still throws a warning because it is deprecated and may not
be supported by future versions of the Docker provider.
Remove all six of these attributes from your docker.tf
configuration file
before continuing with the next step.
When importing real infrastructure, consult the provider documentation to learn
what each argument does. This will help you to determine how to handle any
errors or warnings from the plan step. For instance, the documentation for the
links
argument is in the Docker provider
documentation.
Now verify that the errors have been resolved by re-running terraform plan
.
Notice that Terraform plans to destroy then recreate your Docker container
because env
is not defined.
Add env
to your docker_container.web
resource.
Now verify that the errors have been resolved by re-running terraform plan
.
The plan should now execute successfully. Notice that the plan indicates that
Terraform will update the container in place to add the attach
, logs
,
must_run
, and start
attributes.
Terraform uses these attributes to create Docker containers, but Docker doesn't
store them. As a result, terraform import
didn't load their values into state.
When you plan and apply your configuration, the Docker provider will assign the
default values for these attributes and save them in state, but they won't
affect the running container.
Note: It is not always clear when changes are safe just by reading the provider documentation. You must understand the lifecycle of the underlying resource in order to know if a given change is safe to apply.
Apply the changes and finish the process of syncing your Terraform configuration
and state with the Docker container they represent. Remember to confirm the
apply step with a yes
.
Now your configuration file, Terraform state, and the container are all in sync, and you can use Terraform to manage the Terraform container as you normally would. Because this apply step changed the container's state rather than destroying and recreating it, the container ID didn't change, and the container continued running normally during the process.
Since the approach shown here loads all of the attributes represented in Terraform state, your configuration includes optional attributes whose values are the same as their defaults. Which attributes are optional, and their default values, will vary from provider to provider, and can be found in the provider documentation.
Optionally, you can remove all of these attributes, keeping only the required attributes and those for whom your container differs from the default values. After removing these unnecessary attributes, your configuration should match the following.
Note: Your image ID may be different from the one shown here.
At this point, running terraform plan
or terraform apply
will show no
changes, and your configuration only includes the minimum set of attributes
needed to recreate the container as-is.
If you want to try the other method for generating configuration before moving on, use the following steps to revert the changes you made in the previous section, then switch to the other tab.
Remove everything inside the
"docker_container" "web"
block indocker.tf
, so that the file only containsresource "docker_container" "web" { }
.Remove the container from your Terraform project's state by running:
terraform state rm "docker_container.web"
.Import the container to Terraform state again by running the command
terraform import docker_container.web $(docker inspect -f {{.ID}} hashicorp-learn)
.
Otherwise, proceed with the next step to verify your configuration.
Verify import
Regardless of which method you used, your Docker container is now managed by Terraform. Use the Docker CLI to inspect the container.
Note the "Status" — the container has been up and running since it was created, so you know that it was not restarted when you imported it into Terraform. The ID has not changed either — this is the same container you created at the beginning of this tutorial.
Visit 0.0.0.0:8080
in your web browser to verify that the container is still
working as intended.
Create image resource
In some cases, you can bring resources under Terraform's control
without using the terraform import
command. This is often the case for
resources that are defined by a single unique ID or tag, such as Docker images.
In your docker.tf
file, the docker_container.web
resource specifies the
SHA256 hash ID of the image used to create the container. This is how Docker
stores the image ID internally, and so terraform import
loaded the image ID
directly into your state. However the image ID is not as human readable as the
image tag or name, and it may not match your intent. For example, you might want
to use the latest version of the "nginx" image.
Retrieve the image's tag name by running the following command. Replace the
image ID with the image ID from docker.tf
.
Then add the following configuration to your docker.tf
file to represent this
image as a resource.
Warning: Do not replace the image
value in the docker_container.web
resource yet, or Terraform will destroy and recreate your container. Since
Terraform hasn't loaded the docker_image.nginx
resource into state yet, it
does not have an image ID to compare with the hardcoded one, which will cause
Terraform to assume the container must be replaced. You can work around this
situation by creating the image first, then updating the container to use it, as
shown in this tutorial.
Run terraform apply
to create an image resource in state. Remember to confirm
the apply step with a yes
.
Now that Terraform has created a resource for the image, you can reference it in
your container's configuration. Change the image
value for
docker_container.web
to reference the new image resource.
Since docker_image.nginx.latest
will match the hardcoded image ID you
replaced. Running terraform apply
at this point will show no changes.
Note: If the image ID for the tag nginx:latest
changed between the time
you first created the Docker container and when you run this command, Docker will
destroy the container and then recreate it with the new image.
Manage the container with Terraform
Now that Terraform manages the Docker container, use Terraform to change the its configuration.
In your docker.tf
file, change the container's external port from 8080
to
8081
.
Apply the change. This will cause Terraform to destroy and recreate the
container with the new port configuration. Remember to confirm the apply step
with a yes
.
Now verify that the container has been replaced with a new one with the new
configuration by running docker ps
or visiting 0.0.0.0:8081
in your web
browser.
Notice that the container ID has changed. Because changing the port configuration required destroying and recreating it, this is a completely new container.
Destroy infrastructure
You have now imported your Docker container and the image used to create it into Terraform.
Destroy the container and image by running terraform destroy
. Remember to
confirm the destroy step by responding yes
when prompted.
Finally, run docker ps
to validate that the container was destroyed.
Tip: Since you added the image to your Terraform configuration as well as the container, the image will be removed from Docker as well as the container. If there were another container using the same image, the destroy step would fail. Remember that importing a resource into Terraform means that Terraform will manage the entire lifecycle of the resource, including destruction.
Limitations and other considerations
There are several important things to consider when importing resources into Terraform.
Terraform import can only know the current state of infrastructure as reported by the Terraform provider. It does not know:
- whether the infrastructure is working correctly
- the intent of the infrastructure
- changes you've made to the infrastructure that aren't controlled by Terraform — for example, the state of a Docker container's filesystem.
Importing involves manual steps which can be error prone, especially if the person importing resources lacks the context of how and why those resources were created in the first place.
Importing manipulates the Terraform state file, you may want to create a backup before importing new infrastructure.
Terraform import doesn't detect or generate relationships between infrastructure.
Terraform doesn't detect default attributes that don't need to be set in your configuration.
Not all providers and resources support Terraform import.
Just because infrastructure has been imported into Terraform does not mean that it can be destroyed and recreated by Terraform. For example, the imported infrastructure could rely on other unmanaged infrastructure or configuration.
You may need to set local variables equivalent to the remote workspace variables to import to a remote backend. The
import
command always runs locally—unlike commands likeapply
, which run inside your Terraform Cloud environment. Because of this,import
will not have access to information from the remote backend, such as workspace variables, unless you set them locally.
Following Infrastructure as Code (IaC) best practices such as immutable infrastructure can help prevent many of these problems, but infrastructure created by hand is unlikely to follow IaC best practices.
Tools such as Terraformer to automate some manual steps associated with importing infrastructure. However, these tools are not part of Terraform itself, and not endorsed or supported by HashiCorp.
Next steps
Now that you have imported infrastructure into Terraform, you may like to: