hashicorp / terraform

Terraform enables you to safely and predictably create, change, and improve infrastructure. It is a source-available tool that codifies APIs into declarative configuration files that can be shared amongst team members, treated as code, edited, reviewed, and versioned.
https://www.terraform.io/
Other
41.69k stars 9.41k forks source link

[Improvement] Command: terraform workspace rename #16072

Open samber opened 6 years ago

samber commented 6 years ago

Everything is in the title ;)

$ terraform workspace list
* default
  dev

$ terraform workspace rename dev staging
Renamed "dev" workspace into "staging"

$ terraform workspace list
* default
  staging
apparentlymart commented 6 years ago

Hi @samber! Thanks for this suggestion.

We are planning to do this as part of some improvements to the capabilities of backends. We're not working on this right now, but it's on the list. The primary motivation on our end is to make it easier to migrate between backends that might have different workspace naming conventions, but it seems like it would be generally useful outside of that specific purpose.

An important thing to get right here is to properly manage state lineage and locking during a rename, so that concurrent runs can't cause a state fork. Should be doable, but needs some thought about what the correct behavior is.

lhagemann commented 6 years ago

Another usecase for something like this ... converting the "default" workspace to a named workspace.

$ terraform workspace list
* default

$ terraform workspace new us-phoenix-1
Created and switched to environment "us-phoenix-1"!

You're now on a new, empty environment. Environments isolate their state,
so if you run "terraform plan" Terraform will not see any existing state
for this configuration.

But now I want my default state to be reflected in the new workspace.

anlutro commented 6 years ago

Is there a workaround to do this until the feature is implemented properly?

mazzy89 commented 6 years ago

I'm wondering too how to rename existing workspaces. I've tried to move the state .tfstate from the folder named as the old workspace to the new one in my S3 backend. Very dangerous. The plan detected to remove all the resources

eschwartz commented 6 years ago

I think I found a workaround:

Workaround for renaming your workspace

Let's say you want to rename workspace_src to workspace_dst

# Download the statefile for `workspace_src`
$ aws s3 cp s3://my-tf-state/env:/workspace_src/terraform.tfstate ./workspace_src.tfstate

# Create a new workspace, using that state file
$ terraform workspace new -state=./workspace_src.tfstate workspace_dst

# Delete the old workspace
$ terraform workspace delete -force workspace_src

Don't blame me if it messes something up for you, but it seemed to work for me :)

EDIT:

Bonus Points -- Renaming your default workspace

Terraform won't let you delete your default workspace. But you don't want two workspaces pointing at the same resources, right? So what you can do is simply remove every item from your default workspace's state:

# After following the steps above...
# Select your default workspace
$ terraform select default

# Remove everything from the state
$ terraform state list | xargs terraform state rm

# Switch back to your new workspace
$ terraform workspace select workspace_dst

Your default workspace will still exist, but it will be a clean slate.

Again, I take no responsibility if this destroys everything you've every cared about. But it worked for me.

apparentlymart commented 6 years ago

Thanks for sharing that info, @eschwartz, and sorry to everyone else that I didn't notice the other discussion here until now.

Working directly with the underlying data store would be the workaround I'd suggest for now. The instructions for S3 here seem reasonable. A downside of this sort of direct manipulation is that the locking done by Terraform won't apply, and so you'll need to coordinate with any collaborators to make sure no-one takes action on any of the affected workspaces during the operation.

Generalizing this advice to other backends requires knowing what the backend is expecting. In the case of S3, the "list all workspaces" operation works by enumerating all of the bucket objects and matching key prefixes, with a historical special case for the workspace called "default" due to the fact that this backend predates the workspace concept. Therefore you can, if you like, directly copy the object in S3 using the AWS CLI or UI and then delete the old object. Each backend has some different details due to the different capabilities of the underlying store, but the same general principle should hold.


As noted above, the "default" workspace is currently considered special for historical reasons. I expect that this special case will gradually be weakened over time. Once all of the backends are able to support multiple workspaces, we can remove the requirement that a default backend must exist and instead just retain the special naming behaviors if it does happen to exist. Unfortunately other work has prevented us from reaching that milestone yet, but it remains a goal.

It's possible that this would be done at the same time as adding first-class support for renaming, since (as some others alluded to above) otherwise we'd need to figure out how to handle a request to rename either to or from the name "default". Easier then to stop treating that workspace as special and handle it as any other.

eschwartz commented 6 years ago

Therefore you can, if you like, directly copy the object in S3 using the AWS CLI or UI and then delete the old object.

Ah, so here's is:

An Even Easier Workaround!

aws s3 mv s3://my-tf-state/env:/{workspace_src,workspace_dst}/path/to/terraform.tfstate
gaurdro commented 4 years ago

Any update on This? I don't want to still have to copy state files around by hand and really want the locking terraform provides.

whalesalad commented 4 years ago

At the end of the day the non-default workspaces are stored inside of the terraform.tfstate.d directory. Directories inside of this directory correspond 1:1 with the workspaces that you have.

Observe:

Looking at the contents of the dir:

$ tree terraform.tfstate.d
terraform.tfstate.d
└── us-east
    ├── terraform.tfstate
    └── terraform.tfstate.backup

Listing my workspaces:

$ terraform workspace list
* default
  us-east

I can clone my environment with the following:

cp -r terraform.tfstate.d/us-east terraform.tfstate.d/us-east-renamed

Now if I re-run the comman to list workspaces:

terraform workspace list
* default
  us-east
  us-east-renamed

If you want to rename your workspace, just rename the directory.

Am I missing something here? If you scroll to the end of the workspace docs the fine print explains it.

Workspaces are technically equivalent to renaming your state file. They aren't any more complex than that. Terraform wraps this simple notion with a set of protections and support for remote state.

So if you are doing local state ... sounds pretty cut and dry. Remote ... the commands from comments above do the trick.

Yep it would be great to have this feature but this is not a blocker at the moment.

myroslav commented 4 years ago

My backend-independent recipe is:

$ terraform workspace select oldenv
$ terraform workspace new -state=<(terraform state pull) newenv
$ [[ $(terraform workspace show) == "default" ]] && \
    (terraform state list | xargs terraform state rm; true ) || \
    terraform workspace delete -force oldenv

WARNING: If it is your first time doing moves, backup your state file with

terraform state pull > backup-`date -Iseconds`.tfstate
realsby commented 3 years ago

I need this

cyppan commented 3 years ago

Just a note for those copying the state files across already used environments: if locking is enabled on the backend, don't forget to update the digest in the relevant locking state (i.e: in the case of DynamoDB copying also the "Digest" column from DynamoDB old state row to the new state row).

aaabramov commented 2 years ago

To "fork" default workspace to dev for GCS backend it would be:

$ terraform workspace new dev
$ gsutil cp gs://bucket_name/prefix/default.tfstate gs://bucket_name/prefix/dev.tfstate

where:

UPDATE: I tried to collect possible solution in my blog post: https://aaabramov.medium.com/forking-workspaces-in-terraform-55ed1ed308f7

gregorschulz commented 1 year ago

Therefore you can, if you like, directly copy the object in S3 using the AWS CLI or UI and then delete the old object.

Ah, so here's is:

An Even Easier Workaround!

aws s3 mv s3://my-tf-state/env:/{workspace_src,workspace_dst}/path/to/terraform.tfstate

How about

aws s3 mv --recursive --sse aws:kms --sse-kms-key-id <KEYID> s3://my-tf-state/env:/{workspace_src,workspace_dst}/ 

to rename the complete workspace which was encrypted.