Skip to content
šŸŽ‰ Terragrunt v1.0 is here! Read the announcement to learn more.

Importing Existing Resources

Infrastructure often exists before the code that manages it: a bucket someone created in the console, a VPC that predates your adoption of OpenTofu/Terraform, or resources you are moving out of a Terralith. Importing writes those resources into state so the unit that should own them stops planning to recreate them.

Importing is an OpenTofu/Terraform operation. What Terragrunt adds is that state is segmented per unit, so the unit you run the import from determines which state file the resource lands in.

Run import from inside the unit that should own the resource:

Terminal window
cd live/prod/vpc
terragrunt import aws_vpc.this vpc-0123456789abcdef0

import is one of the OpenTofu shortcuts, so the command above is equivalent to:

Terminal window
terragrunt run -- import aws_vpc.this vpc-0123456789abcdef0

The resource address is resolved against the module the unit sources in its terraform block, not against your terragrunt.hcl. If the unit sources a module that wraps the resource in a submodule, address it through that path:

Terminal window
terragrunt import 'module.vpc.aws_vpc.this' vpc-0123456789abcdef0

Quote addresses that contain square brackets so your shell does not expand them:

Terminal window
terragrunt import 'aws_subnet.private[0]' subnet-0123456789abcdef0

Terragrunt runs the underlying command in the unit’s working directory, which is inside .terragrunt-cache rather than the directory you invoked from. Inputs, generated files, and backend configuration are all applied first, so the import sees the same configuration a plan would. Resource IDs are passed through untouched.

A successful import is one that produces no diff:

Terminal window
terragrunt plan

If the plan still wants to create the resource, the address you imported to does not match the address in your configuration. If it wants to change or replace the resource, your configuration and the real resource disagree, and you should reconcile the configuration to match what exists before applying anything.

A unit that reads outputs from a dependency needs those outputs to resolve before any command runs, including import. When the dependency has already been applied this is automatic. When it has not, add import to the commands that are allowed to use mocks:

terragrunt.hcl
dependency "vpc" {
config_path = "../vpc"
mock_outputs = {
vpc_id = "vpc-mock"
}
mock_outputs_allowed_terraform_commands = ["validate", "plan", "import"]
}

Without import in that list, Terragrunt errors out while resolving the dependency rather than while importing.

The command above is a one-off: it changes state on the machine that runs it and leaves no record. OpenTofu/Terraform import blocks declare imports in configuration instead, so they show up in a plan, get reviewed, and run the same way in CI.

If the unit sources a local module you control, add the block to the module’s .tf files. If it sources a remote module you do not control, use a generate block to write the file into the working directory:

terragrunt.hcl
terraform {
source = "git::git@github.com:acme/modules.git//vpc?ref=v1.2.3"
}
generate "imports" {
path = "imports.tf"
if_exists = "overwrite_terragrunt"
contents = <<-EOF
import {
to = aws_vpc.this
id = "vpc-0123456789abcdef0"
}
EOF
}

Run terragrunt plan to preview what will be imported, then terragrunt apply to perform it.

An import block is only active while the address in to is absent from state, so once the import has landed the block goes inert and later runs ignore it. Terragrunt regenerating the file into each new working directory does not re-import anything. Removing the generate block afterwards is therefore optional: drop it to keep a hardcoded resource ID out of your configuration, or keep it as a record of where the resource came from.

For anything beyond a handful of resources, generate the imports rather than typing them. Both of these help:

  • -generate-config-out writes configuration for resources named in import blocks that have no matching resource in your code, which you then edit into shape. Give it an absolute path so the file lands next to your terragrunt.hcl:

    Terminal window
    terragrunt run -- plan -generate-config-out=$PWD/generated.tf

    OpenTofu resolves this path against its own working directory, which is inside .terragrunt-cache. A bare -generate-config-out=generated.tf therefore writes into scratch space that you can delete at any time. $PWD is expanded by your shell before Terragrunt runs, so it points at the unit directory you invoked from.

  • Your cloud provider’s CLI can list resource IDs, which you can turn into import blocks with a short script.

Import into small units rather than one large one. A unit holding a hundred imported resources has the same problems as the Terralith you are trying to escape, and splitting state after the fact is more work than importing into the right shape to begin with.