con un clic
rename-attribute
// Renames a Terraform attribute safely without changing its schema or producing an unnecessary diff, while enabling users to use both the previous and subsequent attribute names for a deprecation period.
// Renames a Terraform attribute safely without changing its schema or producing an unnecessary diff, while enabling users to use both the previous and subsequent attribute names for a deprecation period.
| name | rename-attribute |
| description | Renames a Terraform attribute safely without changing its schema or producing an unnecessary diff, while enabling users to use both the previous and subsequent attribute names for a deprecation period. |
A user may ask you to rename a Terraform attribute. A Terraform attribute has a name and a schema representing the types of data the attribute can contain. Renaming a Terraform attribute keeps the original attribute and creates a new attribute with the same schema, introducing a deprecation period so the user can migrate from the old attribute to the new attribute.
Use the "Determine Attribute Schema" section.
Use the "Create New Attribute" section.
Use the "Modify New and Old Attribute Schemas" section.
Use the "Modify CRUD Methods" section.
Use the "Create Plan Modifier" section.
Use the "Write Tests" section.
Find the Schema method on the resource and look for the attribute the user
asked to be renamed. Take note of the schema for the attribute and whether the
attribute is Required, Optional, or Computed. This is the schema that will
need to be used for the new attribute.
Create a new attribute on the resource within the Schema method using the new
name that the user told you. The schema for this attribute must be identical to
the schema for the old attribute. The only difference should be the attribute
name.
There's a struct type that represents the resource that Terraform uses to
serialize and deserialize Go types to Terraform types and vice versa. The type
is usually something with "model" in the name. You should be able to find the
field by looking for a tfsdk struct field tag with the value that matches the
name of the attribute from Schema.
Once you find this struct, rename the field representing the old attribute and
append Deprecated. Create a new field representing the new attribute with a
CamelCase name based on the name the user gave you (e.g., NewAttribute).
Update the schema for the both the new and old attributes with the following changes.
Required.Optional: true.Computed: true.Add DeprecationMessage to the old attribute with a message that tells the user
to use the new attribute. Update the Description and/or MarkdownDescription
for the old attribute to note that it's deprecated.
Add a validator.ExactlyOneOf validator to the old attribute with a path
expression matching the new attribute. There should be no such validator on the
new attribute.
At the top of the Create method there will be logic that loads the plan data
into a variable. Find the variable name so we can use it for future operations.
var data ExampleModel
resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...)
Set values for the new and old attribute values in the data variable that was retrieved earlier. The values will likely be retrieved from an earlier API operation to create the resource. It must look something like this.
data.NewAttribute = // ...
data.OldAttribute = // ...
Anywhere in Create that the old attribute was used, replace that usage with
the following conditional logic. Change any to be the correct type for the
attribute and use the correct Value method.
var attribute any
if !data.Hostname.IsNull() && !data.Hostname.IsUnknown() {
attribute = data.NewAttribute.Value()
} else {
attribute = data.OldAttribute.Value()
}
At the top of the Read method there will be logic that loads the state data
into a variable. Find the variable name so we can use it for future operations.
var data ExampleModel
resp.Diagnostics.Append(req.State.Get(ctx, &data)...)
Set values for the new and old attribute values in the data variable that was retrieved earlier. The values will likely be retrieved from an earlier API operation to read the resource. It must look something like this.
data.NewAttribute = // ...
data.OldAttribute = // ...
At the top of the Update method there will be logic that loads the plan data
into a variable. Find the variable name so we can use it for future operations.
var data ExampleModel
resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...)
Set values for the new and old attribute values in the data variable that was retrieved earlier. The values will likely be retrieved from an earlier API operation to update the resource. It must look something like this.
data.NewAttribute = // ...
data.OldAttribute = // ...
Anywhere in Update that the old attribute was used, replace that usage with
the following conditional logic. Change any to be the correct type for the
attribute and use the correct Value method.
var attribute any
if !data.Hostname.IsNull() && !data.Hostname.IsUnknown() {
attribute = data.NewAttribute.Value()
} else {
attribute = data.OldAttribute.Value()
}
Create a plan modifier that both the old and new attribute will use within their
PlanModifiers field using RequiresReplaceIf. The plan modifier must use
the correct types for req and resp depending on the type of the attribute
that's being renamed. Use the types package so that you get access to IsNull
and IsUnknown.
Here's an example plan modifier for reference with TYPE meant to be replaced.
func ModifyPlanExample(ctx context.Context, req TYPE, resp TYPE) {
// Check which attribute this modifier function is being called on to support
// logic for both the old and the new attribute.
switch attribute := req.Path.String(); attribute {
case "NewAttribute":
var deprecatedAttribute types.TYPE
diags := req.Config.GetAttribute(ctx, path.Root("deprecated_attribute"), &deprecatedAttribute)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
// The deprecated attribute has a value. We do not need to replace the resource
// just because the new attribute doesn't have a value.
if !deprecatedAttribute.IsNull() && !deprecatedAttribute.IsUnknown() {
return
}
case "DeprecatedAttribute":
var newAttribute types.TYPE
diags := req.Config.GetAttribute(ctx, path.Root("new_attribute"), &newAttribute)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
// The new attribute has a value. We do not need to replace the resource
// just because the deprecated attribute doesn't have a value.
if !newAttribute.IsNull() && !newAttribute.IsUnknown() {
return
}
default:
resp.Diagnostics.AddAttributeError(
req.Path,
fmt.Sprintf("Invalid plan modifier for attribute %s", attribute),
"ModifyPlanExample can only be used for deprecated_attribute and new_attribute.",
)
}
// If we've reached this point, it's because the actual value of either the
// deprecated attribute or new attribute was modified, which must result in the
// resource being replaced.
resp.RequiresReplace = true
}
Write tests that test the following functionality. Utilize Go sub-tests where appropriate.