The popular HashiCorp Terraform, open-source, Infrastructure as Code (IaC) tool, empowers DevOps and SRE teams to manage and provision resources more efficiently. One of Terraform’s powerful features is the ability to import existing infrastructure into a Terraform project. This capability allows you to bring already-deployed resources under Terraform’s control, providing visibility, management, and automation. In this article, we will explore how to import existing infrastructure into a Terraform project.

Why Import Existing Infrastructure into a Terraform Project?

Importing existing infrastructure into a Terraform project is a powerful and practical approach that offers several advantages, making it a valuable practice for organizations looking to manage their cloud resources more efficiently and effectively.

There are several reasons to import existing infrastructure into a Terraform project:

  1. Centralized Management: By importing existing resources, you can manage all your infrastructure, whether newly provisioned or pre-existing, through a single configuration and version control system.
  2. Automation and Consistency: Terraform enables automation, ensuring that infrastructure remains consistent across environments and deployments. Importing existing resources allows you to apply the same infrastructure-as-code principles to your existing assets.
  3. Version Control: You can track changes and updates to your infrastructure over time by maintaining Terraform configuration in version control systems, promoting better collaboration and transparency.
  4. Infrastructure Visualization: Terraform generates an accurate representation of your infrastructure through state files, providing a clear overview of resource dependencies and relationships.
  5. Infrastructure Standardization: Importing existing resources into Terraform provides an opportunity to standardize configurations and settings. This ensures that all resources adhere to the same set of best practices and security guidelines, enhancing the overall quality and security of your infrastructure.
  6. Ease of Audit and Compliance: Terraform’s infrastructure-as-code approach makes it easier to audit, monitor, and maintain compliance with organizational or industry-specific policies and regulations. Importing resources enables you to track changes over time and maintain compliance more effectively.

Terraform import command definition and arguments

The terraform import command is used to import existing infrastructure into a Terraform configuration. Its basic syntax is as follows:

terraform import ADDRESSS ID
  • ADDRESS: This must be a valid Terraform resource address. This is also known as the “Terraform Resource ID” that uniquely identifies the resource within the Terraform project configuration code.
  • ID: The unique identifier of the existing resource you are importing

For resources located within the Microsoft Azure cloud, the ID will be the Azure Resource ID. This can be found easily within the Azure Portal for the individual resource. For other cloud providers, such as Amazon AWS or Google, this will be a similar ID for the resource within that cloud.

Warning: Terraform expects each of the remote objects (i.e. Azure resources, AWS resources, etc.) that it’s managing will be bound to only a single resource address. This ADDRESS is normally guaranteed by Terraform itself when it has created all objects in the project. When importing existing resources in to Terraform, you will need to be sure to import remote objects to only one Terraform resource address. Importing the same object multiple times will result in unexpected behavior, and could cause problems with the Terraform State file.

The terraform import command also provides additional command-line arguments that can be used to configure it’s usage:

  • -config=path: The path to the directory of Terraform configuration files what conigure the provider for import. This defaults to the working directory.
  • -input=true: Whether to ask for input for provider configuration
  • -lock=false: Do not hold a state lock during the operation.
  • -lock-timeout=0s: The duration to retry a state lock.
  • -no-color: When specified, the console output will not contain any color.
  • -parallelism=n: Limit the number of concurrent operations as Terraform walks the graph. This defaults to 10 if not specified.
  • -var 'foo=bar': Set a Terraform input variable for the configuration.
  • -var-file=foo: Set Terraform input variables for the configuration from a variable file.

Example Import of an Azure Storage Account

Let’s go through a step-by-step example of importing an Azure Storage Account into a Terraform project. Whether you’re importing resources from Microsoft Azure, or another cloud provider, the steps to perform the import are the same.

Set ‘azurerm’ Terraform provider in code

First, ensure you have the Microsoft Azure provider (azurerm) defined in your Terraform code. This provider configuration allows you to manage Azure resources.

terraform {
  required_providers {
    azurerm = {
      source  = "hashicorp/azurerm"
      version = "=3.0.0"
    }
  }
}

provider "azurerm" {
  features {}
}

Write the HCL configuration for the resource to be imported

Next, create a Terraform configuration for the Azure Storage Account you wish to import. This might look like:

resource "azurerm_storage_account" "example" {
  name                     = "mystorageaccount"
  resource_group_name      = "myresourcegroup"
  location                 = "East US"
  account_tier             = "Standard"
  account_replication_type = "LRS"
}

Run the ‘terraform import’ command

Execute the terraform import command, providing the resource type, the name you want to assign, and the existing resource’s ID.

terraform import azurerm_storage_account.example /subscriptions/<subscription_id>/resourceGroups/<resource_group_name>/providers/Microsoft.Storage/storageAccounts/mystorageaccount

Observe state file and plan output

After importing the resource, Terraform updates its state file to reflect the imported resource’s current state. You can use terraform state list to list imported resources and terraform show to view their attributes.

Improve config to avoid replacement or changes

When Terraform detects that the resource configuration has changed in comparison to the current state of the Terraform code, it may suggest in the plan that the resource should be replaced (aka deleted and recreated). Additionally there may be times when there are certain resource attributes that should be set when the resources is created, but not updated / changed over time. The handle these scenarios, the lifecycle block can be used with the prevent_destroy and ignore_changes attributes.

By using these lifecycle block settings, you can fine-tune how Terraform manages the imported resources to align with your specific requirements. This is especially useful for resources that should remain stable and unchanged, or where modifications could lead to data loss or service disruptions.

Keep in mind that while these settings can help avoid accidental changes, they should be used judiciously. You should always carefully consider the impact of preventing changes or destruction, as it can lead to unexpected issues if not properly managed.

Using lifecycle.prevent_destroy

To prevent Terraform from making changes to an imported resource, you can utilize the lifecycle block within the resource’s configuration. The lifecycle block allows you to control various aspects of how Terraform manages a resource, including whether it can be destroyed.

By setting prevent_destroy to true, you ensure that Terraform will not destroy (delete) the resource. This is a crucial safeguard to avoid accidental deletion of important resources. Here’s an example:

resource "azurerm_storage_account" "example" {
  # ...other configuration...

  lifecycle {
    prevent_destroy = true
  }
}

With this configuration, if you attempt to run terraform destroy on this resource, Terraform will halt and notify you that the resource cannot be destroyed.

Using lifecycle.ignore_changes

To prevent Terraform from modifying specific attributes on a resource, the ignore_changes block within lifecycle can be specified on the resource to explicitly state the attributes that Terraform should not consider when planning changes to that resource.

For example, you might want to prevent Terraform from changing the sku attribute of an Azure Storage Account:

resource "azurerm_storage_account" "example" {
  # ...other configuration...

  lifecycle {
    ignore_changes = [
      sku
    ]
  }
}

In this case, even if the sku attribute of the imported Azure Storage Account changes outside of Terraform, Terraform will not consider it a change when you run terraform plan.

How to import Terraform modules with multiple resources

Importing Terraform modules with multiple resources can be a complex task, but it’s often necessary when managing infrastructure that consists of interconnected components. However, importing a resource within a module is similar to importing individual resources.

What are Terraform Modules? Terraform modules are a way to encapsulate and organize resources, variables, and outputs within reusable, shareable components. Modules help you manage infrastructure more efficiently and maintain a clean and modular project structure.

Before importing a Terraform module, you need to understand the resources it includes. Modules are typically organized with multiple resource blocks. Each resource block within the module is a candidate for import. You should know the module’s structure and which resources you want to import.

To import a specific resource within a module, you use the terraform import command with the module name and the resource type and name. The syntax is as follows:

terraform import module.MODULE_NAME.RESOURCE_TYPE.NAME ID
  • MODULE_NAME: The name of the module as defined in your Terraform configuration.
  • RESOURCE_TYPE: The type of resource you want to import within the module.
  • NAME: The name you want to assign to the imported resource within the module.
  • ID: The unique identifier of the existing resource you are importing.

For example, if you have a module named “my_module” and you want to import a resource of type “azurerm_virtual_network” with the name “example,” the command would be:

terraform import module.my_module.azurerm_virtual_network.example /subscriptions/<subscription_id>/resourceGroups/<resource_group_name>/providers/Microsoft.Network/virtualNetworks/my-existing-vnet

If the module contains multiple resources you want to import, you should run the terraform import command for each one. This allows you to import all the necessary resources and connect them properly within the module.

Remember that working with Terraform modules with multiple resources can be complex, and careful planning and validation are essential to avoid unexpected issues.

Using ‘terraform plan -generate-config-out’ argument

The terraform plan -generate-config-out argument is a valuable feature in Terraform that simplifies the process of creating Terraform configuration files for existing infrastructure. It can be particularly useful when importing resources into a Terraform project.

FYI, as of writing this, the -generate-config-out argument is “experimental”. If you have alot of resources to import into a Terraform project, this this may still prove to be useful to help reduce the time it takes to write the necessary Terraform HCL code and get those resources imported into your project.

Generating a Configuration Snippet

When you run the following command:

terraform plan -generate-config-out=import.tf

Terraform generates an HCL (HashiCorp Configuration Language) configuration snippet in the specified output file, in this case, import.tf. This file must not already exist, or Terraform will error. The generated Terraform HCL code will include resources imported that don’t already exist within your Terraform project.

Simplifying Resource Import

The primary use case for the -generate-config-out argument is to simplify the process of importing resources into your Terraform project. By generating a configuration snippet, you no longer need to manually write the Terraform code for each imported resource, which can be especially time-consuming for complex setups.

Example -generate-config-out Output

Let’s say you have an existing Azure Virtual Network with subnets, network security groups, and routing tables that you want to import into your Terraform configuration. Instead of manually writing the HCL code for each of these resources, you can use the terraform plan -generate-config-out command to generate the code for you.

After running the command, the “import.tf” file might contain sections of HCL code like the following:

resource "azurerm_virtual_network" "example" {
  name                = "my-existing-vnet"
  location            = "East US"
  address_space       = ["10.0.0.0/16"]
}

resource "azurerm_subnet" "example" {
  name                 = "my-subnet"
  virtual_network_name = azurerm_virtual_network.example.name
  resource_group_name  = azurerm_virtual_network.example.resource_group_name
  address_prefixes     = ["10.0.1.0/24"]
}

# Additional generated resource blocks for NSG and routing tables

While the terraform plan -generate-config-out feature can significantly speed up the process of importing resources, it’s essential to carefully review the generated code and ensure it accurately reflects your intentions. Make any necessary modifications and consider the impact on your infrastructure.

Customization and Refinement

The generated code provides a starting point, but you may need to customize it to match your desired configuration. For example, you may want to adjust names, configurations, or resource dependencies to align with your project’s specific requirements.

Resource Naming and Dependencies

It’s important to note that the generated code will typically use the same names and dependencies as the imported resources in your existing infrastructure. You should review the generated code to ensure it follows your naming conventions and dependencies, and make any necessary adjustments.

Conclusion

In conclusion, importing existing infrastructure into a Terraform project can be a valuable step in managing and automating your resources, even if they were provisioned outside of Terraform. By understanding the terraform import command and its nuances, you can seamlessly incorporate your existing infrastructure into your Terraform workflow, benefiting from consistency, automation, and version control.

Chris Pietschmann is a Microsoft MVP, HashiCorp Ambassador, and Microsoft Certified Trainer (MCT) with 20+ years of experience designing and building Cloud & Enterprise systems. He has worked with companies of all sizes from startups to large enterprises. He has a passion for technology and sharing what he learns with others to help enable them to learn faster and be more productive.
Microsoft MVP HashiCorp Ambassador

Discover more from Build5Nines

Subscribe now to keep reading and get access to the full archive.

Continue reading