Skip to content

Documentation · Infrastructure runs

Moving state from HCP Terraform

Migrating existing state with terraform init -migrate-state, from a cloud block, the remote backend, or another backend entirely.

Your existing state moves with terraform init -migrate-state. The CLI does the copying; what you change is the hostname.

Before you start, have a token (see API tokens) and your organisation name, which is the account on the Terraform card on Settings in the dashboard, and put the token in the environment:

bash
export TF_TOKEN_tf_runners_io=<your token>

There is no terraform login step. That flow needs a browser round trip on a path tf.runners.io does not serve, so tokens come from the dashboard instead — see API tokens.

Terraform will not migrate state while a cloud block is in force, so the route is cloud → remote → migrate → cloud again.

  1. Your configuration looks something like this:

    hcl
    terraform {
      cloud {
        hostname     = "app.terraform.io"
        organization = "your-organization"
    
        workspaces {
          name = "my-workspace"
        }
      }
    }
  2. Change it to use remote, temporarily, leaving the hostname alone:

    hcl
    terraform {
      backend "remote" {
        hostname     = "app.terraform.io"
        organization = "your-organization"
    
        workspaces {
          name = "my-workspace"
        }
      }
    }
  3. Remove the initialised backend so the next init re-resolves it:

    bash
    rm -r .terraform
  4. Continue with If you use the remote backend below.

  1. Change the hostname, and the organisation if the name differs here:

    hcl
    terraform {
      backend "remote" {
        hostname     = "tf.runners.io"
        organization = "your-organization"
    
        workspaces {
          name = "my-workspace"
        }
      }
    }
  2. Migrate:

    bash
    terraform init -migrate-state

    Terraform notices the backend configuration has changed and offers to copy the existing state. Answer yes.

    Initializing the backend...
    Backend configuration changed!
    
    Do you want to copy existing state to the new backend?
      Pre-existing state was found while migrating the previous "remote" backend
      to the newly configured "remote" backend. No existing state was found in
      the newly configured "remote" backend. Do you want to copy this state to
      the new "remote" backend?
    
      Enter a value: yes
  3. Optionally move back to a cloud block, which is what you want if you use workspace tags:

    hcl
    terraform {
      cloud {
        hostname     = "tf.runners.io"
        organization = "your-organization"
    
        workspaces {
          name = "my-workspace"
        }
      }
    }
    bash
    terraform init

    Answer yes again. Despite what the output says about copying state, nothing is copied at this step — your state arrived in the previous one. This prompt is the CLI switching which backend it talks to.

Coming from s3, gcs, azurerm, local or similar, replace the backend block with a cloud block and reinitialise:

hcl
terraform {
  cloud {
    hostname     = "tf.runners.io"
    organization = "your-organization"

    workspaces {
      name = "my-workspace"
    }
  }
}
bash
terraform init

Answer yes when asked whether to migrate the existing state.

Mostly nothing, and the exceptions are worth knowing before rather than after.

  • Runs execute OpenTofu. Your configuration is unchanged; the binary running it is not the one HCP uses. Why.

  • There are no projects. Workspaces belong to the organisation directly. If you use dynamic credentials, the token subject loses its project: segment and your cloud's trust policy needs the same edit — see dynamic credentials.

  • Inviting people is done on the dashboard, not through the organization-memberships API, which answers 501 here on purpose. Adding an existing user to a team over the API works normally. See the Terraform API.

  • email and microsoft-teams notifications are not supported. generic, slack and gcppubsub are. See notifications.

  • No policy-as-code, and drift detection is not available yet. If either is load-bearing for you, that is the thing to weigh before moving.

Move one workspace first, ideally one whose state you could rebuild. Confirm a plan comes back with no unexpected diff — an empty plan against migrated state is the check that the migration worked, and it takes a minute.

Your old state is not deleted by any of this. HCP still holds it until you remove it, which makes the whole of the above reversible right up to the point you tidy up.