Migrating Azure Storage Account Deployed Using Terraform- ‘Terraform Import Block’

Venkata Sri Rama Sai Chodapaneedi
7 min readMay 26, 2024

--

As a DevOps Engineer, I understand how hectic it can be to perform a migration. For example, when a project requires migrating an Azure storage account provisioned using Terraform modules from one Resource Group (RG-1) to another (RG-2) with same name, several critical steps must be followed. We need to migrate this storage account to the new resource group with the same name and ensure that all dependent resources, initially deployed using Terraform modules, are also recreated in RG-2. Additionally, it is essential to follow DevOps best practices and ensure that the state files for both resource groups are up to date.

module "migration_poc_storage_account" {
source = "./module/storage_account"
for_each = toset(["pocsa"])
tags = {
"Environment" : "Dev",
}
location = "eastus2"
storage_account_name = "${lower("my${each.key}${local.location_abbr}")}devsa"
resource_group_name = "RG-1"
dfs_subnet = "mySubnet1"
lock_resource = false
}
Storage Account in RG-1
Dependent Resources
RG-2

Prerequisites:

  1. Ensure you have Terraform installed (>= v1.5.0) and configured with appropriate permissions to manage Azure resources (Backend Configuration)
  2. Login to azure using “az login” (Service Principle)
  3. Install Git, configure and clone the repository containing your Terraform configuration files.

Pre-Migration Considerations:

  1. Assess Current Environment: Understand the current setup of your storage account, including configurations, data, access controls, and dependencies.
  2. Expected Downtime: Plan for expected downtime during the migration process. Inform stakeholders and users about the maintenance window to minimize disruptions.
  3. Backup Data: Ensure that all data in the existing storage account is backed up before starting the migration. This can prevent data loss in case of any issues during the migration process.
  4. Check Dependencies: Identify all applications and services dependent on the storage account. Ensure these dependencies are properly managed and will function as expected post-migration.
  5. Network Configurations: Review and document existing network configurations, including private endpoint connections, firewall rules, and VNET integrations. These configurations will need to be re-established post-migration.
  6. Access Controls and Permissions: Document current access controls and permissions. Ensure that these settings are replicated in the new storage account setup.
  7. Update DNS and Connection Strings: Plan to update DNS settings, connection strings, and any other references to the storage account in your applications and services post-migration.
  8. Test Migration Plan: If possible, conduct a test migration in a staging environment to identify potential issues and ensure a smooth transition.

Introducing the import block feature from Terraform

Use the import block to add existing infrastructure resources to Terraform's management. Unlike the terraform import command, import blocks are predictable, work well with CI/CD pipelines, and let you preview changes before updating the state.

Once imported, Terraform tracks the resource in your state file, allowing you to manage it like any other resource. This includes updating its attributes and destroying it as part of its lifecycle.

The import block shows that Terraform imported the resource rather than creating it. You can remove import blocks from your configuration after importing, or keep them as a record of the resource's origin.

This is what the block look like, Will Explain in detail

import {
to =
id =
}

The import block has the follwing arguments

  • to — The Instance address this resource will have in your state file.
  • id — A string with the import ID of the resource. For example, you can provide a resource id of Azure resource
  • provider — An optional custom resource provider. This is useful when you’re using multiple providers. If you do not set the provider argument, Terraform attempts to import from the default provider.

Note: Import blocks are only available in Terraform v1.5.0 and later.

Let’s go through the steps like how can we achieve this.

Migration Steps:

  1. Terraform Refresh:
  • Open a new Terminal
  • Change the Directory where you terraform code lies on RG-1
  • Run terraform init
  • Run terraform workspace list
  • Run terraform workspace select <workspace_name>in my case the workspace name is dev
  • Run terraform refresh— to synchronize the Terraform state with the current state of your infrastructure.

2. Remove Remote Object Binding:

  • Run terraform state list — to get the resources listed according to module depth order followed by alphabetical order. (Lists out all the resources that are tracked in the current state file.)
    Here you can see all resources are linked to terraform state file
Resources Binding to Terraform State File in RG-1
  • Use terraform state rmto remove the binding to an existing remote object. terraform state rm <module.module_name>
  • Run terraform state rm module.migration_poc_storage_account
Remove Binding between terraform and Azure resource in RG-1

This command will remove all instances of the resources defined within the module named migration_poc_storage_account from the Terraform state.

Verification: Run “terraform state list” to get the resources listed and check the resources

3. Remove Private Endpoint Connections If Exists:

Manually remove private endpoint connections of existing storage accounts in the RG-1using the Azure portal or Azure CLI.

  • Navigate to Azure Portal, Select Storage Account Service, and select the storage account you want to migrate
  • Scold down to Security + Networking Section on left side and select Networking
  • Click on Private endpoints connections
  • Tick on Connections name which will select all existing Private Endpoints
  • Now click on Remove and Confirm Yes
Remove Private Endpoints Connections

4. Migrate Storage Account Resource:

Using the Azure portal, migrate the storage account resource from the RG-1 to the RG-2.

To migrate the storage account resource from the RG-1to the RG-2using the Azure portal, follow these steps:

  • Navigate to Overview Page:- Go to the Azure portal and navigate to the overview page of the storage account resource you want to migrate.
  • Initiate the Move:- Click on the Movedropdown menu. Select Destination Resource Group, Choose “Move to another resource group” option from the dropdown menu. Select the target resource group as RG-2 .
Move Azure Storage Account to RG-2
Source and target — RG’s
  • Validation:- Click on “Next” and wait for the validation process to complete. Ensure that all validation checks pass successfully.
Validation Pending
  • Proceed with Migration:- Once the validation is successful, click on nextto proceed.
Validation Succeeded
  • Confirmation:- Review the migration details. Tick the checkbox to confirm that you understand the implications of moving the resource. Click on “Move” to initiate the migration process.
Confirmation
  • Monitor Migration: The storage account will start migrating from the RG-1to the RG-2 .Monitor the progress of the migration.You will receive a notification once the migration is completed successfully.
Notification — Moving Resources
Notification — Moved Successfully
  • Verification: Once it moved successfully go to RG-2 and verify if the resource is exist
Verification — Moved Storage Account

5. Update Terraform Configuration:

  • Change working directory to where you terraform code lies for RG-2
  • Run terraform init , terraform refresh command
  • Update the Terraform configuration files (storage_account.tf) in the RG-2directory to include the migrated storage account. Cut the Module block from RG-1and Paste in storage_account.tf file of RG-2directory and make necessary changes to it like Resource Group name to RG-2
  • Utilize the import block for the storage account resource in the RG-2 Terraform files.
import {
to = module.migration_poc_storage_account["pocsalake"].azurerm_storage_account.blob
id = "<resource_id_of_migrates_storage_account_in_RG-2>"
}

# Dependency resources based on your module structure. Only need to import the resources that are migrated along with storage account
import {
to = module.migration_poc_storage_account["pocsalake"].azurerm_storage_management_policy.my_data_cycle
id = "<resource_id_of_migrated_storage_management_policy_in_RG-2>"
}

To get the ID of azurerm_storage_management_policy run az command below

az storage account management-policy show --account-name "<storage_account_name>" --resource-group "<resource_group_name>"
import {
to = module.migration_poc_storage_account["pocsa"].azurerm_storage_account.blob
id = "<resource_id_of_migrates_storage_account_in_RG-2>"
}

import {
to = module.migration_poc_storage_account["pocsa"].azurerm_storage_management_policy.mars_data_cycle
id = "<resource_id_of_migrated_storage_management_policy_in_RG-2>"
}

module "migration_poc_storage_account" {
source = "./module/storage_account"
for_each = toset(["pocsa"])
tags = {
"Environment" : "Dev",
}
location = "eastus2"
storage_account_name = "${lower("my${each.key}${local.location_abbr}")}devsa"
resource_group_name = "RG-2"
dfs_subnet = "mySubnet2"
lock_resource = false
}

6. Verify Changes:

  • Run terraform state list to list the resource binding here (RG-2).
Terraform State list in RG-2
  • Run terraform plan which lets you preview the changes that Terraform plans to make to your infrastructure. Based on the module structure you will see the plan accordingly.
terraform plan

Here the 4 resources are that are adding by terraform are dependent resources from my storage account module

7. Apply Changes:

  • Apply the changes to migrate the storage account resource by running terraform apply .
terraform apply

8. Verify State:

  • After applying the changes, verify the storage account resource’s presence in the Terraform state file by running terraform state list
Verify terraform state in RG-2
  • Verify Dependent resources are deployed successfully in RG-2
Verify Dependent Resources in RG-2

--

--