Terraform – A Real-Life Example of Safely Moving Resources in HCP Terraform

Reading Time: 7 minutes

When working with Terraform, almost everyone eventually runs into challenges with state management. As your project grows, resources evolve, modules get restructured, and naming conventions change. Suddenly you run a terraform plan and Terraform decides it wants to destroy production resources, not because anything is wrong, but simply because Terraform can’t match the new configuration to what’s already deployed.

This problem becomes even more interesting when you’re using HCP Terraform in remote execution mode, where state is stored and managed remotely and you don’t manipulate the state file directly. In these environments, handling resource renames or refactors requires a careful and structured approach to avoid disruption.

This post will walk you through how to safely move or rename Terraform resources within HCP Terraform workspaces, ensuring smooth state transitions and avoiding those heart-stopping “destroy” plans for production resources.

Why Resource Moves Matter in Terraform

Terraform tracks every single cloud resource via its state, mapping real infrastructure to resource addresses in your code. When you change something fundamental ike a resource name, module path, or structure Terraform no longer recognizes that the existing resource belongs to that logical address. It assumes a new resource should be created and the old one destroyed.

Typical scenarios include:

  • Renaming a resource
    (azurerm_storage_account.this → azurerm_storage_account.primary)
  • Refactoring modules
    Splitting or merging modules, reorganizing directories, or introducing new module abstractions
  • Introducing count or for_each
    Which changes resource addressing and index paths

In growing production environments, changes like these are unavoidable. Without proper handling, they lead to unwanted deletes, forced redeployments, or even downtime.

That’s why Terraform introduced resource move workflows (moved blocks) and continues to support state operations like terraform state mv. Understanding how and when to use them especially in HCP Terraform remote workspaces is critical for safe and efficient infrastructure evolution.

Understanding State in HCP Terraform (Remote Execution Mode)

Before you start moving or renaming resources, it’s important to understand how state behaves when working with HCP Terraform. Unlike local Terraform, where you directly interact with terraform.tfstate on disk, HCP Terraform abstracts state management behind a remote backend with strict guardrails. This improves security and collaboration but also changes how you approach state operations.

How HCP Terraform Stores and Locks State

HCP Terraform maintains state remotely and securely inside your workspace:

  • State is stored server-side, not on your local machine.
  • Each run automatically locks the state to prevent concurrent modifications.
    This ensures that two people (or automation pipelines) can’t accidentally modify the same infrastructure at the same time.
  • Every apply creates a state version, giving you full version history and allowing you to:
    • Inspect previous states
    • Compare versions
    • Roll back if needed

In short: you never touch the actual state file. HCP Terraform handles it for you.

Why State Edits Are Different compared to Non-HCP Environments

If your workspace is configured for remote execution, which is often the case because of its many benefits, state management works very differently than in “traditional” Terraform environments.

  • In HCP Terraform remote execution, the state file is fully managed by the backend, and you do not have direct access to it.
  • As a result, you cannot run commands like terraform state mv or terraform import directly from your CLI against the remote state. These operations require a local configured execution workspace.

In contrast, in non-HCP environments (for example, a traditional azurerm backend):

  • The state is stored in a remote blob (Azure Storage), but Terraform CLI can directly access it.
  • Commands like terraform state mv and terraform import can be run locally or in a pipeline without restrictions.

Real-Life Example: Move Resources with HCP Remote State

Enough theory, let me walk you through a real-world scenario I faced last week, which inspired this blog post.

Scenario

  • An existing Terraform backup template was used to provision Azure Recovery Services Vaults for multiple departments, primarily to back up Azure File Shares.
  • Recently, new requirements came in: the template also needed to back up Blob Storage from Azure Storage Accounts (Backup Vault)
  • Support the creation of either a Recovery Service Vault or a Backup Vault.
  • This change would be breaking for existing consumers of the template, as it affects the resource structure and introduces potential state conflicts.

Code Snippet (Before)

Code Snippet (After)

To meet the new requirements, the Terraform template was updated to provision both Azure Recovery Services Vaults (RSV) and an Azure Backup Vault backups in a single template. The modules now include conditional creation using count:

The problem

  • Introducing count for conditional creation changes the resource addresses in Terraform state.
  • Terraform now sees the existing RSV resources as “to be deleted” if count evaluates to 0, because the indexed addresses no longer match the previous state.
  • Without careful handling, a normal terraform plan will propose deleting live RSV resources, which would be catastrophic for existing resources.

The Goal

  • The creation of a completely new template needed to be avoided, as the goal was to enhance the existing template and extend its features.
  • To allow provisioning of both Recovery Services Vaults and Blob Storage backup resources using the same template.
  • To make it safe for existing deployments, avoiding accidental destruction while supporting the new requirements.

Steps to Safely move the resources

When introducing count for existing resources in a Terraform template, you need to carefully migrate the state to prevent accidental destruction. Here’s a recommended workflow:

1. Discard Existing Plans

Before making any changes, discard any pending or queued Terraform plans in HCP. This ensures the state lock is released and no conflicting runs interfere with your updates.

2. Change Execution Mode on the Workspace to Local

Switch the workspace from remote execution to local execution. This allows you to run Terraform CLI commands directly against the remote state without restrictions.

3. Add Cloud Configuration Block

Update your Terraform configuration with the cloud block to link your local CLI session to the Terraform Cloud workspace. This ensures that all local operations target the correct remote state.


4. Authenticate to Terraform Cloud

Authenticate the local CLI to Terraform Cloud or HCP Terraform:

This allows your local Terraform CLI to access the workspace state and perform safe operations.

5. Run terraform init

Initialize your workspace locally to download providers and configure the backend:

6. Identify Existing Resources

The next step is to identify the existing resources, in this case, those managed by the “recovery_services_vault” module.

7. Move Resources

You need to run the terraform state mv command for each affected resource to map them to their new addresses.

Important: Pay attention to escaping and quoting.

  • Wrap the resource address in single quotes to protect it from shell expansion.
  • Escape internal quotes (\”) when the resource uses [“name”] syntax.
  • Missing escapes or quotes can break the command, causing Terraform to fail to locate the resource.

8. Verify updated State

After moving the resources, run:

This lets you verify that all resources are correctly mapped to their new addresses, including the count indices. Below is an example of the updated resource names:

9. Run terraform plan

After verifying the updated state, run:

This shows exactly what Terraform plans to do, verifying that existing resources are correctly recognized and that no unintended deletions or changes will occur.

Note: There’s no need to commit changes to your Terraform configuration for the cloud block (Step 3), these can be safely discarded.

10. Change Execution Mode of Terraform Workspace (Remote)

After successfully updating and verifying the state locally, switch your HCP Terraform workspace back to Remote Execution. This re-enables all normal remote workflows and ensures that future plans and applies run securely within the Terraform Cloud environment.

Once remote execution is enabled again, trigger a run or click “Plan” in the workspace to verify that Terraform:

  • correctly recognizes all resources under their new addresses
  • does not attempt to recreate or delete any existing infrastructure
  • aligns fully with the updated configuration

At this point, your workspace is fully restored to its managed remote workflow, and the “migration” is complete.

Alternative: Using moved Blocks Instead of terraform state mv

If you don’t want to (or cannot) run terraform state mv locally, Terraform provides a built-in alternative: the moved block. This method is especially useful in environments where:

  • Direct state manipulation via CLI is restricted
  • You want a configuration-driven, version-controlled way to manage resource moves

What the moved Block Does

A moved block tells Terraform that a resource has changed its address such as when adding a count index, but still refers to the same physical resource. Terraform will then automatically update the state during the next plan/apply without requiring manual commands.

Example: Moving Module Resources to Use count

You can declare this in your configuration:

Terraform will then seamlessly migrate the state entry.

When to Use moved Blocks

  • You want resource moves tracked in version control
  • You’re forced using HCP Terraform remote execution
  • You want to avoid the risk of copy-pasting long state mv commands

A moved block is applied automatically by Terraform during plan and apply, ensuring safe and repeatable migrations.

Limitations

  • Must be committed to your repository, because they are part of configuration
  • Not ideal for temporary or one-off internal refactors (where state mv may be simpler)

Closing Thoughts

Refactoring Terraform configurations, especially when introducing count, new modules, or structural changes can be challenging when existing resources are already in production. With HCP Terraform, these challenges become even more visible due to the managed remote execution model. By temporarily switching to local execution, carefully mapping resources, and verifying the updated state, you can safely evolve your infrastructure without risking unwanted deletions or downtime. Whether you use terraform state mv or the cleaner moved block approach, the key is understanding how Terraform tracks resource addresses and how to guide it through change.

And perhaps the most important lesson: Use feature flags from the very beginning when designing Terraform templates. They make optional modules explicit, enable safe configuration changes, and prevent major headaches once multiple teams are already consuming your templates. With thoughtful design and the right workflow, even complex template enhancements become predictable, safe, and repeatable.

Leave a Reply

Your email address will not be published. Required fields are marked *