
HashiCorp ships official tooling for moving state into Terraform Cloud. There's tf-migrate, there are docs, there's a guided flow. In the other direction you get an error message. Everything about exporting state out of the platform, whether you're moving to an S3 bucket or another management platform, is assembled from a CLI command, an API, and a workspace page in the UI. This post covers all three, plus the two things you can't export at all.
Everything here was verified against HashiCorp's live documentation and the current CLI behavior as of July 2026.
Run terraform state pull > terraform.tfstate from a working directory configured for the workspace. That's it for a single workspace: the command downloads the current state and writes it to stdout as raw JSON. For bulk export across many workspaces, script the State Versions API instead. The web UI covers the one-off case with a per-version download link. As of July 2026 there is no official HashiCorp tool that exports state out of the platform, so pick the route that matches your workspace count.
The CLI route in full:
terraform login # once, stores a user API token
terraform init # connects to the workspace via your cloud block
terraform state pull > terraform.tfstateOne caveat worth knowing before you archive these files: state pull upgrades the state to the format of your locally installed Terraform version before writing it out. If you need the byte-identical original, download it through the API or UI instead, which serve the stored file as-is.
terraform init -migrate-state work for leaving Terraform Cloud?No. Swap the cloud block for a backend block, run terraform init -migrate-state, and current Terraform versions answer: "Migrating state from Terraform Cloud to another backend is not yet implemented." Inbound migration is a first-class feature; outbound is not. The gap has been tracked in hashicorp/terraform#31042 since 2022, with a companion docs issue (#33214) still open as of July 2026.
The supported sequence is manual, and it works reliably:
terraform state pull > terraform.tfstate while the cloud block is still in place.cloud block and add your new backend block (S3, GCS, azurerm, or a remote block pointing at another platform).terraform init against the new backend.terraform state push terraform.tfstate to seed it.If the push complains about serials or lineage, you're pushing into a backend that already has state in it. How to update state in a remote backend covers those checks and how to resolve them safely.
Two endpoints do the work. GET /state-versions?filter[workspace][name]=<ws>&filter[organization][name]=<org> lists a workspace's versions, newest first; GET /workspaces/:workspace_id/current-state-version returns just the latest. Each result carries a hosted-state-download-url you fetch with the same token. One authentication rule matters more than everything else: these endpoints reject organization tokens. Use a user token, or a team token with the read-state-versions permission.
A minimal single-workspace export:
# TFC_TOKEN must be a user or team token, not an org token
URL=$(curl -s \
--header "Authorization: Bearer $TFC_TOKEN" \
"https://app.terraform.io/api/v2/state-versions?filter%5Bworkspace%5D%5Bname%5D=my-workspace&filter%5Borganization%5D%5Bname%5D=my-org" \
| jq -r '.data[0].attributes."hosted-state-download-url"')
curl -s --header "Authorization: Bearer $TFC_TOKEN" "$URL" > my-workspace.tfstateTwo details for bulk scripts. First, the list endpoint paginates with page[number] and page[size] (20 per page by default), so archiving full history means walking pages, not grabbing .data[0]. Second, HashiCorp's documented rate limit is 30 requests per second per user, and it's enforced per user rather than per token, so parallel tokens won't raise it. At roughly three requests per workspace, that's still hundreds of workspaces a minute. Each state version also exposes a hosted-json-state-download-url serving a stable external-integration format, which is the better choice if other tooling will parse the files.
Yes, one version at a time. Every workspace has a States page listing each state version alongside the run and commit that produced it. Click a version and you get a diff against the previous state plus a link to the raw state file. It's fine for grabbing one file during debugging, and useless for fleet export: there's no bulk download, and nobody wants to click through 300 workspaces.
Two things. Sensitive variable values are write-only: the variables API returns them as null and the UI never shows them again, so budget migration time for re-entering secrets from whatever manager originally held them. And deleted state is gone: HashiCorp's own docs state that once a workspace and its state are deleted, the infrastructure can no longer be tracked or managed, with no documented recovery path. The safe-delete default only refuses to delete workspaces still managing resources; it does nothing to preserve state files. Export first. Delete second.
Loop the API, or use migration tooling that does it for you. A bash loop over the state-versions endpoint handles the raw export fine at fleet scale. If the export is step one of a platform move, here's where the tooling stands as of July 2026. HashiCorp's tf-migrate only moves state into HCP Terraform or Terraform Enterprise. tfm, from HashiCorp's implementation services team, handles org-to-org and Enterprise-to-Cloud moves but carries a "not officially supported" label in its README. Scalr's TFC migration tool pulls workspaces through the same API and preserves the full state-version history rather than just the current file, recreates workspace attributes and variables, and recovers sensitive Terraform variable values from plan files when they're available.
Where the export lands depends on why you're leaving. Teams going self-managed put state in object storage and accept the locking and backup work that comes with it. Teams moving platform-to-platform should size the whole job first, not just the state step: our migration effort and timeline breakdown covers what the other steps cost in practice. State import is free on Scalr's side either way, and the free tier covers 50 runs a month, which is enough to validate a batch of migrated workspaces before any money changes hands.
For the full decision framework beyond the mechanics, see the platform engineer's guide to replacing Terraform Cloud.
