Infrastructure evolution often leads to a state of "ClickOps," where resources are provisioned rapidly via the Google Cloud Console to meet immediate deadlines. While this allows for speed, it creates a significant gap in visibility, version control, and reproducibility. The terraform import functionality is the primary mechanism for bridging this gap, allowing platform engineers to bring existing Google Cloud Platform (GCP) resources under the strict management of Infrastructure as Code (IaC).
Importing resources is not merely about updating a state file; it is about aligning the actual state of the cloud environment with a declared configuration. Whether you are migrating a legacy project to a GitOps workflow or recovering from a scenario where the state file was lost, understanding the nuances of the import process—both via the Command Line Interface (CLI) and the modern declarative import blocks—is critical for maintaining infrastructure stability.
The Mechanics of Terraform Import
At its core, Terraform operates by maintaining a state file (terraform.tfstate) that acts as a source of truth, mapping your configuration files to real-world resources. When a resource is created manually in the GCP Console, Terraform has no knowledge of its existence. The import process tells Terraform: "This specific remote object in Google Cloud corresponds to this specific resource address in my code."
When an import is successful, Terraform generates a state entry for the resource. This entry allows you to manage the resource's entire lifecycle—including updates and deletion—using your Terraform configuration. Without this mapping, any terraform apply operation would attempt to create a new resource with the same name, leading to "Resource Already Exists" errors from the Google Cloud API.
CLI Import vs. Declarative Import Blocks
Terraform has evolved significantly in how it handles the adoption of existing infrastructure. Historically, the terraform import CLI command was the only option. Starting with Terraform version 1.5, the introduction of the import block provided a more scalable, declarative approach.
The Classic CLI Approach (terraform import)
The terraform import command is a procedural operation. It takes two primary arguments: the resource address (how it is named in your .tf files) and the resource ID (how Google Cloud identifies the object).
The critical limitation of the CLI command is that it only updates the state file. It does not generate the corresponding HCL (HashiCorp Configuration Language) code. If you run a CLI import without first writing the resource block in your configuration, Terraform will import the state, but the next plan or apply will signal that the resource needs to be destroyed and recreated because it is missing from your code.
The Modern Declarative Approach (import block)
Introduced in version 1.5, the import block allows you to define the import operation as part of your configuration. This shifts the import process into the standard Terraform workflow (plan and apply).
The primary advantage of the import block is the ability to generate configuration. By using the command terraform plan -generate-config-out=..., Terraform can examine the remote resource and automatically write the HCL code required to represent that resource. This eliminates the guesswork involved in manually writing resource blocks to match existing cloud settings.
Comparison of Import Methods
| Feature | terraform import (CLI) |
import Block (Declarative) |
|---|---|---|
| State Update | Immediate upon command execution | Occurs during terraform apply |
| Config Generation | None (Manual writing required) | Supported via -generate-config-out |
| Workflow Integration | Out-of-band / Manual | Part of plan and apply cycle |
| Bulk Import | One resource at a time | Multiple resources per block/file |
| Minimum Version | All versions | Terraform 1.5.0+ |
Preparing the GCP Provider Environment
Before attempting to import any GCP resources, the Terraform environment must be correctly initialized. This ensures that the correct provider versions are used and that the authentication credentials allow Terraform to query the GCP APIs.
The following configuration represents a standard provider setup required for GCP imports, ensuring compatibility with the google provider version 5.0 and later.
```hcl
terraform {
requiredversion = ">= 1.5.0"
requiredproviders {
google = {
source = "hashicorp/google"
version = "~> 5.0"
}
}
}
provider "google" {
project = var.project_id
region = var.region
}
variable "project_id" {
type = string
}
variable "region" {
type = string
default = "us-central1"
}
```
Detailed Import Workflows for Common GCP Resources
Importing into GCP requires an understanding of the "Resource ID" format. Every resource type in the Google Cloud provider has a unique ID structure. Using the full identifier, including the project ID, is strongly recommended to avoid ambiguity.
Compute Engine Instances
To import a Virtual Machine (VM), you must provide the full path to the instance. The format follows: projects/{project}/zones/{zone}/instances/{name}.
Manual Resource Block:
hcl
resource "google_compute_instance" "app" {
name = "app-server"
machine_type = "e2-standard-4"
zone = "us-central1-a"
boot_disk {
initialize_params {
image = "debian-cloud/debian-12"
size = 50
}
}
network_interface {
network = "default"
access_config {}
}
}
Declarative Import Block:
hcl
import {
to = google_compute_instance.app
id = "projects/${var.project_id}/zones/us-central1-a/instances/app-server"
}
CLI Import Command:
bash
terraform import google_compute_instance.app projects/your-gcp-project-id/zones/us-central1-a/instances/app-server
Cloud Storage (GCS) Buckets
Unlike Compute Engine instances, GCS buckets use a simpler ID format: they are identified solely by their globally unique bucket name.
Manual Resource Block:
hcl
resource "google_storage_bucket" "data" {
name = "${var.project_id}-data-bucket"
location = "US"
force_destroy = false
versioning {
enabled = true
}
lifecycle_rule {
action {
type = "Delete"
}
condition {
age = 365
}
}
}
Declarative Import Block:
hcl
import {
to = google_storage_bucket.data
id = "${var.project_id}-data-bucket"
}
Strategic Considerations and Technical Constraints
Importing infrastructure is rarely a linear process. There are several architectural constraints and "gotchas" that engineers must navigate to avoid corrupting the state or causing unintended downtime.
The One-to-One Binding Rule
Terraform enforces a strict rule: each remote object must be bound to exactly one resource address in the state. Attempting to import the same GCP resource into multiple Terraform resource addresses can lead to unpredictable behavior and state conflicts.
API Management and Private APIs
When importing resources, you may notice references to specific APIs in logs—for example, dataproc-control.googleapis.com (used for Managed Service for Apache Spark). A common point of confusion is whether these "private" or "internal" APIs need to be explicitly enabled via a google_project_service resource or imported into the state.
The answer is no. If an API is identified as a private or internal Google Cloud API, it is managed internally by Google. No customer action is required; you do not need to enable or import these APIs to successfully manage the associated resources.
HCP Terraform and Local Execution
When using HCP Terraform (the cloud-hosted version of Terraform), there is a critical distinction between how apply and import (CLI) function:
terraform apply: Runs within the remote HCP Terraform environment.terraform import(CLI): Runs locally on your machine.
Because the CLI import runs locally, it does not have direct access to remote workspace variables stored in HCP Terraform. To resolve this, you must set local environment variables that mirror the remote workspace variables to ensure the import command can authenticate and locate the GCP resources.
Advanced Import Strategy: Phased Adoption
For organizations with massive amounts of legacy infrastructure, a "big bang" import approach is dangerous. Instead, a phased adoption strategy is recommended.
- Small Batch Start: Identify a small subset of non-critical resources (e.g., a few storage buckets) to import. This allows the team to familiarize themselves with the ID formats and the config generation process.
- State Alignment: After importing, run
terraform plan. If the plan shows changes (meaning the code doesn't perfectly match the cloud settings), update the HCL until the plan shows "No changes." This confirms the state is perfectly aligned. - Expansion: Gradually move toward critical infrastructure like VPC networks and Cloud SQL databases.
- Governance: Once the import is complete, remove the
importblocks from the configuration. They are only necessary for the initial adoption phase; once the resource is in the state, theimportblock is redundant.
Summary of GCP Import ID Formats
The following table summarizes the typical ID formats required for the most common GCP resources during the import process.
| Resource Type | Required ID Format | Example ID |
|---|---|---|
| Compute Instance | projects/{project}/zones/{zone}/instances/{name} |
projects/my-proj/zones/us-central1-a/instances/web-vm |
| GCS Bucket | {bucket_name} |
my-company-assets-bucket |
| VPC Network | projects/{project}/global/networks/{name} |
projects/my-proj/global/networks/main-vpc |
| Cloud SQL Instance | projects/{project}/instances/{name} |
projects/my-proj/instances/prod-db |
Conclusion
The ability to import existing Google Cloud resources into Terraform transforms a chaotic "ClickOps" environment into a disciplined, version-controlled infrastructure. While the classic terraform import CLI remains a viable tool for quick, single-resource additions, the import block introduced in version 1.5 represents a paradigm shift toward declarative infrastructure adoption. By leveraging configuration generation and following a phased migration strategy, teams can eliminate the risks associated with manual provisioning.
The most critical takeaways for a successful GCP import are the strict adherence to resource ID formats, the necessity of aligning the HCL configuration with the imported state to avoid "drift," and the understanding that private Google APIs do not require manual intervention. When these technical requirements are met, Terraform becomes not just a tool for creating new infrastructure, but a powerful mechanism for auditing and governing existing cloud estates.