Terraform import is the process of bringing existing cloud resources created manually, by another tool, or before you adopted Terraform under Terraform management by recording them in your state file. After import, Terraform manages that resource the same as if it had been created by terraform apply from the start. You can then update it, plan changes against it, and destroy it through code.
The process reads an existing remote resource and writes an entry for it into your Terraform state file. The cloud resource itself is not modified. Terraform import does not generate the corresponding HCL configuration on its own. The legacy CLI command never does; you write that by hand or use terraform plan -generate-config-out with an import block to generate a starting point.
Terraform import is the process of bringing existing cloud resources under Terraform management by recording them in state file. There are two ways to do it: the legacy terraform import command and the modern import {} block introduced in Terraform 1.5.
This guide covers both: when to use each, complete examples for AWS, Azure, and GCP, how to generate Terraform code from existing resources, how to import multiple resources at once, and the common errors that trip people up.
Why Import Matters With Terraform Cloud
Terraform Cloud and HCP Terraform manage state remotely. Importing resources is useful in the following scenarios:
- When a terraform apply operation fails partially, leaving the state file in a condition where it does not reflect reality.
- When a resource was created manually or outside of the current Terraform workspace and you need to bring it under management.
The terraform import command finds an existing resource in your infrastructure and adopts it into your Terraform state file. This allows Terraform to manage the resource moving forward. Not all resources support import. To verify that a resource can be imported, refer to the provider's documentation for that resource. Supported resources will have terraform import syntax listed at the bottom of their documentation page.
A managed Terraform automation platform gives those imports a consistent plan/apply pipeline with approvals, RBAC, and an audit trail out of the box.
Even though Terraform is now a mature and widely adopted IaC tool, many organizations still have years of ClickOps and manually provisioned infrastructure behind them. The lack of human resources and the steep learning curve involved in using Terraform effectively causes teams to start using cloud infrastructure directly via their respective web consoles.
For that matter, any kind of IaC method requires some training and real-time scenario handling experience. Things get especially complicated when dealing with concepts like states and remote backends. In a worst-case scenario, you can lose the terraform.tfstate file.
Two Import Methods Available
There are two ways to import existing resources into Terraform:
- terraform import CLI command: available since Terraform 0.7. Imperative, one-off, writes to state directly without a plan preview.
- import {} block: introduced in v1.5.0
The classic terraform import CLI only updates the state locally – it doesn’t generate configuration. Starting in Terraform 1.5, you can also use declarative import blocks and optionally generate configuration with terraform plan -generate-config-out=... We cover this workflow later in the article.
Terraform supports multiple ways to import resources:
- One at a time by using the terraform import subcommand.
- In bulk by including an import block in the configuration, requires Terraform version 1.5 or later.
- In bulk by using a Google Cloud feature that lets you import resources after doing a bulk export.
Legacy CLI Import
The terraform import command takes the following arguments:
- ADDR – the address of the resource in terraform e.g. awsinstance.instancename
- ID – the resource ID in the cloud provider/k8s/database service/vcs service/etc
The basic syntax is terraform import
An example terraform import would look like this:
terraform import aws_iam_role.role_name my_role
Run terraform import, passing the resource address e.g., awss3bucket.example and the unique identifier e.g., the bucket name or resource ID as arguments. Terraform queries the cloud provider’s API for the resource’s details. Terraform stores the live resource’s attributes in the state file and associates them with the resource block in the config file which was also passed as an argument in the import command.
Now, you can manage the lifecycle of this cloud resource with Terraform just like other configured resources.
Prerequisites for Terraform Cloud imports: The import command runs locally and authenticates to your providers before writing to the remote state file. Unlike operations such as plan or apply, it does not run directly within the HCP Terraform or Terraform Enterprise workspace environment. Because of this, the import command cannot access workspace variables, which may include provider credentials.
Import Block Workflow
Import block is declarative. You can import the state for any Google Cloud resource.
The import command takes two arguments—the resource address and ID.
The resource address is an identifier that points to a resource instance within a configuration.
The ID is an identifier that identifies a resource in Google Cloud that is being imported. Format for the ID differs based on resource type and is documented for each resource supported by the provider. We recommend using the full identifier, which includes the project ID when supported.
Identify the resource address to be imported.
resource "google_storage_bucket" "sample" {
name = "my-bucket"
project = "sample-project"
location = "US"
force_destroy = true
}
For a sample resource such as Cloud Storage bucket defined earlier, this is googlestoragebucket.sample.
To identify the resource ID format, see the provider import documentation for the googlestoragebucket resource.
Import blocks allow bulk import and code generation. The workflow is:
- Define an import block in configuration alongside the resource.
- Run terraform plan -generate-config-out=... to generate a starting point for the resource block.
- Run terraform apply to import the S3 bucket, which will update the Terraform state file.
Using the Cloud Compass helps you identify the severity of risks and ensure that your infrastructure is secure and compliant with access settings, policies, and other guardrails you’ve defined within the env0 platform, for all imported resources.
By now, you know how to import resources in your Terraform configuration using terraform import command and import block for managing existing cloud resources and which approach to use when.
Resource Address and ID Reference
Correct address and ID pairing is critical. A mismatch causes import to fail or associate the wrong resource.
Table: Import method comparison
| Feature | terraform import CLI | import {} block |
|---|---|---|
| Terraform version | Since 0.7 | Introduced v1.5.0 |
| Style | Imperative, one-off | Declarative, bulk capable |
| Generates config | No | Can generate config with terraform plan -generate-config-out |
| Plan preview | No | Yes via plan |
| State write | Direct write to state | Applied via apply |
Table: Common import examples
| Provider | Resource type | Resource address example | ID example |
|---|---|---|---|
| AWS | S3 Bucket | awss3bucket.example | my-existing-bucket |
| AWS | Security Group | awssecuritygroup.ssh_access | sg-0123456789abcdef0 |
| AWS | IAM Role | awsiamrole.role_name | my_role |
| AWS | EC2 Instance | aws_instance.web | i-1234567890abcdef0 |
| GCP | Storage Bucket | googlestoragebucket.sample | my-bucket |
Practical Import Examples
Importing an AWS S3 Bucket
Suppose you have an S3 bucket named my-existing-bucket. To manage it with Terraform:
resource "aws_s3_bucket" "example" {
bucket = "my-existing-bucket"
}
Run terraform import awss3bucket.example my-existing-bucket. Terraform maps the S3 bucket’s state to the configuration.
After importing, run terraform plan to verify that no unintended changes will be applied.
Importing an AWS Security Group
Suppose you have an existing security group with the ID sg-0123456789abcdef0 that allows SSH access. To manage it with Terraform:
resource "aws_security_group" "ssh_access" {
name = "ssh-access-group"
description = "Allow SSH access"
vpc_id = "vpc-0abc12345"
ingress {
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
}
Run terraform import awssecuritygroup.ssh_access sg-0123456789abcdef0
The terraform import command takes an existing resource an EC2 instance, an S3 bucket, an Azure VM, a GCS bucket, or any resource type your provider supports importing and records it in your Terraform state file. After import, Terraform manages that resource the same as if it had been created by terraform apply from the start.
Importing into a Terraform Module
To put import commands into a Terraform module, specify the module path in the import command.
For example, you specify the module path in the import command to target a resource inside a module.
Common Errors and Safeguards
Two important caveats up front:
- generate-config-out flag covered below. You still have to write the matching resource block by hand, or use code generation.
- There are two ways to import existing resources into Terraform: terraform import CLI command available since Terraform 0.7. Imperative, one-off, writes to state directly without a plan preview. import {} block introduced in v1.5.0
When performing the import action, you might face challenges such as using exact resource IDs, which helps ensure you have an accurate state file.
sensitive = true only suppresses values in plan/apply output; it does not redact them from the state file. Restrict state access, encrypt remote state, and avoid committing generated files that expose credentials.
The cloud resource itself is not modified by import. The state file is updated to reflect reality. If the HCL does not match the live resource, terraform plan will show drift and you can reconcile.
Conclusion
Importing existing infrastructure into Terraform Cloud is the bridge between brownfield reality and declarative control. The legacy terraform import CLI remains fast for one-off adoptions, writing directly to remote state from a local run that must authenticate to providers itself. The import {} block introduced in Terraform 1.5 enables bulk, declarative imports with optional code generation via terraform plan -generate-config-out, giving teams a safer preview before state mutation.
Both methods require the resource block to exist in configuration for CLI imports, and correct resource address and ID pairing for the provider in question. For Google Cloud, the ID format varies by resource and often includes project ID. For AWS, the ID is typically the name or identifier such as bucket name or sg-xxxx.
When using HCP Terraform or Terraform Enterprise, remember imports run locally, not inside the workspace, so workspace variables and provider credentials stored there are not available. Validate with terraform plan after import to confirm no unintended changes will be applied, and protect state files with encryption and access controls.
Adopting ClickOps resources via import gives you plan/apply governance, audit trails, and RBAC without rebuilding infrastructure. Choose CLI for quick single resource adoption and import blocks for repeatable bulk migrations and generated starting configs.
Sources
- Scalr Ultimate Guide to Terraform Import
- Google Cloud Terraform Resource Management Import
- HashiCorp Support How to Import Resources into a Remote State Managed by Terraform Cloud
- Spacelift Blog Importing Existing Infrastructure Into Terraform
- Terramate Comprehensive Guide to Importing Existing Infrastructure Into Terraform and OpenTofu
- env0 Blog Terraform Import Commands Example Tips and Best Practices