Discover existing cloud resources using Terraform Search queries and bulk import them into Terraform management. Use when bringing unmanaged infrastructure under Terraform control, auditing cloud resources, or migrating to IaC.
66
78%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
View guide
Passed
No findings from the security scan
Fix and improve this skill with Tessl
tessl review fix ./plugins/terraform/skills/terraform-search-import/SKILL.mdDiscover existing cloud resources using declarative queries and generate configuration for bulk import into Terraform state.
References:
BEFORE starting, you MUST verify the target resource type is supported:
# Check what list resources are available
./scripts/list_resources.sh aws # Specific provider
./scripts/list_resources.sh # All configured providersIdentify target resource type (e.g., aws_s3_bucket, aws_instance)
Check if supported: Run ./scripts/list_resources.sh <provider>
Choose workflow:
Note: The list of supported resources is rapidly expanding. Always verify current support before using manual import.
Before writing queries, verify the provider supports list resources for your target resource type.
Run the helper script to extract supported list resources from your provider:
# From a directory with provider configuration (runs terraform init if needed)
./scripts/list_resources.sh aws # Specific provider
./scripts/list_resources.sh # All configured providersOr manually query the provider schema:
terraform providers schema -json | jq '.provider_schemas | to_entries | map({key: (.key | split("/")[-1]), value: (.value.list_resource_schemas // {} | keys)})'Terraform Search requires an initialized working directory. Ensure you have a configuration with the required provider before running queries:
# terraform.tf
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 6.0"
}
}
}Run terraform init to download the provider, then proceed with queries.
.tfquery.hcl files with list blocks defining search queriesterraform query to discover matching resources-generate-config-out=<file>resource and import blocksterraform plan and terraform apply to importQuery files use .tfquery.hcl extension and support:
provider blocks for authenticationlist blocks for resource discoveryvariable and locals blocks for parameterization# discovery.tfquery.hcl
provider "aws" {
region = "us-west-2"
}
list "aws_instance" "all" {
provider = aws
}list "<list_type>" "<symbolic_name>" {
provider = <provider_reference> # Required
# Optional: filter configuration (provider-specific)
# The `config` block schema is provider-specific. Discover available options using `terraform providers schema -json | jq '.provider_schemas."registry.terraform.io/hashicorp/<provider>".list_resource_schemas."<resource_type>"'`
config {
filter {
name = "<filter_name>"
values = ["<value1>", "<value2>"]
}
region = "<region>" # AWS-specific
}
# Optional: limit results
limit = 100
}Provider support for list resources varies by version. Always check what's available for your specific provider version using the discovery script.
# Find all EC2 instances in configured region
list "aws_instance" "all" {
provider = aws
}# Find instances by tag
list "aws_instance" "production" {
provider = aws
config {
filter {
name = "tag:Environment"
values = ["production"]
}
}
}
# Find instances by type
list "aws_instance" "large" {
provider = aws
config {
filter {
name = "instance-type"
values = ["t3.large", "t3.xlarge"]
}
}
}provider "aws" {
region = "us-west-2"
}
locals {
regions = ["us-west-2", "us-east-1", "eu-west-1"]
}
list "aws_instance" "all_regions" {
for_each = toset(local.regions)
provider = aws
config {
region = each.value
}
}variable "target_environment" {
type = string
default = "staging"
}
list "aws_instance" "by_env" {
provider = aws
config {
filter {
name = "tag:Environment"
values = [var.target_environment]
}
}
}# Execute queries and display results
terraform query
# Generate configuration file
terraform query -generate-config-out=imported.tf
# Pass variables
terraform query -var='target_environment=production'list.aws_instance.all account_id=123456789012,id=i-0abc123,region=us-west-2 web-serverColumns: <query_address> <identity_attributes> <name_tag>
The -generate-config-out flag creates:
# __generated__ by Terraform
resource "aws_instance" "all_0" {
ami = "ami-0c55b159cbfafe1f0"
instance_type = "t2.micro"
# ... all attributes
}
import {
to = aws_instance.all_0
provider = aws
identity = {
account_id = "123456789012"
id = "i-0abc123"
region = "us-west-2"
}
}Generated configuration includes all attributes. Clean up by:
# Before: generated
resource "aws_instance" "all_0" {
ami = "ami-0c55b159cbfafe1f0"
instance_type = "t2.micro"
arn = "arn:aws:ec2:..." # Remove - computed
id = "i-0abc123" # Remove - computed
# ... many more attributes
}
# After: cleaned
resource "aws_instance" "web_server" {
ami = var.ami_id
instance_type = var.instance_type
subnet_id = var.subnet_id
tags = {
Name = "web-server"
Environment = var.environment
}
}Generated imports use identity-based import (Terraform 1.12+):
import {
to = aws_instance.web
provider = aws
identity = {
account_id = "123456789012"
id = "i-0abc123"
region = "us-west-2"
}
}After running terraform apply to import resources, verify what actually landed in state.
Prefer the documented, stable, and more token-efficient commands over reading the raw state
file:
# Confirm resources are now managed (also confirms addresses)
terraform state list
# Inspect resolved attribute values for imported resources
terraform show -json | jq '.values.root_module.resources[] | {address, type, name}'terraform show -json requires providers to be installed (terraform init), since it
renders values against provider schemas. Fall back to the raw state
(terraform state pull / terraform.tfstate) only when providers aren't available and
init can't run, you need only coarse info (addresses, outputs, serial/lineage), or you
must avoid executing Terraform. Avoid parsing the raw version-4 state format as a stable
interface. Note: state contains sensitive values in plaintext in every format — never
echo state contents into logs or output.
limit to prevent overwhelming output| Issue | Solution |
|---|---|
| "No list resources found" | Check provider version supports list resources |
| Query returns empty | Verify region and filter values |
| Generated config has errors | Remove computed attributes, fix deprecated arguments |
| Import fails | Ensure resource not already in state |
# main.tf - Initialize provider
terraform {
required_version = ">= 1.14"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 6.0" # Always use latest version
}
}
}
# discovery.tfquery.hcl - Define queries
provider "aws" {
region = "us-west-2"
}
list "aws_instance" "team_instances" {
provider = aws
config {
filter {
name = "tag:Owner"
values = ["platform"]
}
filter {
name = "instance-state-name"
values = ["running"]
}
}
limit = 50
}# Execute workflow
terraform init
terraform query
terraform query -generate-config-out=generated.tf
# Review and clean generated.tf
terraform plan
terraform apply3268468
If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.