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
42.77k stars 9.56k forks source link

Mechanism to move from a deprecated resource type name to its new replacement name #27310

Open Kronk74 opened 3 years ago

Kronk74 commented 3 years ago

Hi there,

I didn't see a feature like this into changelog 0.14 file but sorry if it already exist.

After upgrading a provider, I noticed that they changed their resource name.

The problem is that my plan want to delete and recreate resources already managed/created.

To solve it, I have to remove manually old resources name and import new one with new name.

When you have 50 or 100 resources to treat it is quite annoying..

I created a bash script to do it faster, but I wonder if is it possible to add a command which can replace a resource name by another one into state file ?

Best regards,

Thomas

Kronk74 commented 3 years ago

Here is my example of bash script to replace/rename a module resource :

Be careful, this script will change your tfstate. Don't forget to create a backup before using it. (terraform state pull > backup.tfstate

#!/bin/bash
env_dir=$1
old_resource=$2
new_resource=$3
for i in $(terraform state list | grep "${old_resource}"); do
    IFS='.'
    read -r -a mod <<<"$i"
    echo "${mod[3]}"
    if [[ ${mod[3]} =~ \]$ ]]; then
        id=$(echo "${mod[3]}" | grep -Eo '^\w*')
        id="${id}_${mod[1]}"
    else
        id=${mod[1]}
    fi
    terraform state rm "${mod[0]}.${mod[1]}.${mod[2]}.${mod[3]}"
    terraform import -var-file="${env_dir}"/terraform.tfvars "${mod[0]}"."${mod[1]}"."${new_resource}"."${mod[3]}" "${id}"
done

I don't know if this kind of feature is into your development scope but it could be interesting to have it 👍

apparentlymart commented 3 years ago

Hi @Kronk74! Thanks for opening this feature request.

Terraform doesn't allow arbitrary changes of resource type name through any supported command because in general that isn't safe: the data recorded for a resource instance in the state is expected to conform to the resource type's schema, and strange things can happen if not.

Of course, what you have here is a special case where it is safe: the provider team has intentionally renamed a resource type and so the "new" resource type has inherited the same schema and all of the same schema upgrade logic as the old one, and so in this case it all happens to work out.

A while ago the SDK and provider teams discussed with us a potential mechanism for providers to report to Terraform Core that one resource type name is a replacement for another, with the goal in the long run being for Terraform to automatically handle that somehow, but in the short term it might simply override the usual prohibition on changing a resource type with terraform state mv because the provider has agreed to deal with the consequences of doing that.

The discussion I'm referring to here was an informal chat and so I don't know if it has a corresponding public issue to link this to, but it seems like hashicorp/terraform-plugin-sdk#306 might plausibly be it so I'm going to link over there for now, as a starting point.

I don't think we would consider adding an "official" way to rename resource types until we have a way for a provider to declare it to be safe, because it can potentially be difficult to recover from an incorrect resource type change, and it can have potentially harmful effects such as making Terraform incorrectly "forget" about an object that still exists in the remote system. However, if we can design a way for a provider to indicate that this is safe then I agree that it would be good to have a built-in way to deal with it, even if it's still somewhat manual with terraform state mv to start.

coryflucas commented 1 year ago

Given we now have the import block, it seems like if we had a block that performed an operation similar to terraform state rm ... you could combine the two to get a version of this. This would certainly be handy for handling the migration from unversioned to versioned resource types in the Kubernetes provider. The current docs suggest doing a terraform state rm and terraform import which works, but there is no way to express this in the configuration itself.

apparentlymart commented 1 year ago

I'm not sure when in the timeline this issue was active vs. us working on config-driven move (moved blocks), but during the design of that feature we did initially make some room for allowing a config-driven move to change resource type if the provider declares that's safe and in return promises to preserve some additional invariants about the state upgrade process to make that mechanically work.

I remember that we ended up pulling it because something wasn't ready in the provider protocol / SDK area to get that done and we didn't want to get bound to a protocol that hadn't been tested by implementing it on both client and server sides.

As far as I know it would still be possible to do that if there were resources available to do it. I think that's the best design compromise we know for this concern so far.

Importing and then removing can function as a workaround for missing refactoring features but it cannot be an actual recommended solution because it is lossy: Terraform tracks additional information about objects beyond what's available in the remote API and any "real" solution for this sort of refactoring/adaptation should be designed to preserve the relevant metadata, as moved does by having Terraform construct a new resource instance based on an existing one, rather than by constructing a resource instance based only on an existing remote object as import does

benscholler-ciq commented 2 months ago

Bump to add support for this. Case in point, Cloudflare has recently done a renaming of many resources en masse: https://github.com/cloudflare/terraform-provider-cloudflare/pull/3584

Since there is no native support for this, you're stuck with doing lookups in Terraform state for every instance where Cloudflare zero trust resources lie across numerous AWS accounts and generating many import files. It would be nice to couple the provider updates next to a resource rename when they're in a child module.

Another idea, would it be possible to add moved block support at the provider level to mark a resource as being renamed? Maybe something like a legacy names property for resources.

crw commented 2 months ago

For future viewers of this issue: if you are viewing this issue and would like to indicate your interest, please use the 👍 reaction on the issue description to upvote this issue. We also welcome additional use case descriptions. Thanks!

apparentlymart commented 2 months ago

I'm no longer a member of the Terraform team at HashiCorp, but FWIW Terraform v1.8.0 introduced a mechanism like I was describing above to allow a provider to declare that it knows how to map resource instance data from one resource type to another.

It works by having the module author write a moved block where the from and to addresses have different resource types. If (and only if) the provider of the to resource type has logic to migrate the state data from the from resource type, Terraform Core will ask the provider to perform that translation and if successful will then proceed as if the object had been originally created as the new resource type.

This mechanism does require migration logic in the provider itself, so e.g. the Cloudflare provider would need to provide logic to migrate between all of the pairs of resource types discussed in https://github.com/cloudflare/terraform-provider-cloudflare/pull/3584. If the provider team adds those migration functions then a moved block like the following should work:

moved {
  from = cloudflare_access_application.example
  to   = cloudflare_zero_trust_access_application
}

Since the Cloudflare provider uses the Terraform Plugin Framework, the relevant provider-developer-oriented documentation for this would be Plugin Framework: State Move.

As far as I know there's no further work needed in Terraform Core to make this work and so this issue could probably be closed, but since I don't work at HashiCorp anymore I'll let someone else figure out if that's true. :grinning: