How to migrate from Terraform Cloud to OpenTofu, one workspace at a time
You can leave Terraform Cloud with ordinary tofu commands and no special import tool. This guide moves one workspace at a time, so every step is small and reversible.
Plan the move
Migrate one workspace at a time, starting with something low-risk such as a development environment. For each workspace, write down:
- its Terraform version, so you can pick a compatible OpenTofu version;
- its variables, and where the sensitive values come from;
- how it authenticates to your cloud;
- its outputs and resource addresses, to compare after the move; and
- any Sentinel or OPA policies attached to it.
If you haven't switched from Terraform to OpenTofu yet, do that first while still on Terraform Cloud. Our OpenTofu vs Terraform post covers what to check.
Prepare the destination
- Create a StackShip organization, then a project and a workspace. Pick the same OpenTofu version you tested with.
- Recreate the workspace variables. Copy sensitive values from your secret manager: Terraform Cloud won't show them again.
- Set up a credential profile for AWS, Google Cloud, or Azure. It uses short-lived federation, so there are no access keys to copy across. See short-lived cloud credentials.
- Connect the GitHub repository if you want pull request plans.
Export state from Terraform Cloud
Lock the source workspace in Terraform Cloud so nothing else can write to it. Then, from a directory still configured for Terraform Cloud, pull the current state:
tofu state pull > source-state.jsonKeep this file somewhere safe until the migration is finished.
Push state into StackShip
In a fresh checkout of your configuration, change the cloud block to point at StackShip. Don't copy the old .terraform directory.
terraform {
cloud {
hostname = "stackship.run"
organization = "your-org-slug"
workspaces {
name = "network-production"
}
}
}Sign in, initialize, and push the exported state:
tofu login stackship.run
tofu init -input=false
tofu state push source-state.jsonFor a scripted migration, create a workspace token with the State migration purpose instead of using tofu login, and export it as TF_TOKEN_stackship_run.
Verify
tofu state pull > stackship-state.json
tofu plan -refresh=falseCompare outputs and resource addresses with what you recorded. A clean plan should show no unexpected changes. State moved into an empty workspace starts a new lineage with serial 1; your resources and outputs are unchanged.
What state migration doesn't copy
| Item | What to do |
|---|---|
| Variables | Recreate them; copy secrets from your secret manager. |
| Cloud credentials | Create a credential profile and apply its trust template. |
| VCS settings | Connect the repository through the StackShip GitHub App. |
| Sentinel policies | Rewrite them in Rego. See OPA policy for OpenTofu plans. |
| Members and teams | Invite people, or connect SSO and SCIM. |
| Run and audit history | Export what you need to keep before closing the old workspace. |
Cut over
- Make sure only StackShip can write to this state. Remove the VCS connection or disable runs on the old workspace.
- Start a run: push to the tracked branch, or run
tofu apply. - Check the policy result, approve the plan, and apply.
Never let two systems apply to the same infrastructure. Keep the exported state file until you have completed a clean plan and apply in StackShip.
Next steps
The migration reference in the docs covers moving local state and other backends too. To compare features before you start, read StackShip vs Terraform Cloud.