Hey there @marcinbelczewski ,
Side note: If this doesn’t help, it’d be useful to see your implementation of ModifyPlan
for further debugging.
At a high-level looking at what you’re describing, it’s important to note that Terraform core does filter out any RequireReplaces
if there is no update to the resource (it doesn’t have to be a different attribute in particular, but something needs to be planned to be updated).
In your use-case, I’d say what is likely missing is setting a new plan value to go along with the required replace, so the new plan value should be AWS_OWNED_KMS_KEY
(the planned value being changed is the user set KMS key).
I wrote a naive attribute plan modifier to showcase that idea (although ModifyPlan
would also work if you’d prefer that):
func (m examplePlanModifier) PlanModifyString(ctx context.Context, req planmodifier.StringRequest, resp *planmodifier.StringResponse) {
// No config value and no planned value, default to AWS_OWNED_KMS_KEY
if req.ConfigValue.IsNull() && req.PlanValue.IsUnknown() {
resp.PlanValue = types.StringValue("AWS_OWNED_KMS_KEY")
return
}
// No config value and the state value is not our computed default value ("AWS_OWNED_KMS_KEY"),
// so we know that we need to replace the resource and set the new value to "AWS_OWNED_KMS_KEY".
if req.ConfigValue.IsNull() && !req.StateValue.IsNull() && req.StateValue.ValueString() != "AWS_OWNED_KMS_KEY" {
// If this plan value isn't updated, then Terraform core will ignore the RequiresReplace
resp.PlanValue = types.StringValue("AWS_OWNED_KMS_KEY")
resp.RequiresReplace = true
}
}
With that plan modifier and this schema:
func (r *thingResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) {
resp.Schema = schema.Schema{
Attributes: map[string]schema.Attribute{
"kms_key_id": schema.StringAttribute{
Optional: true,
Computed: true,
PlanModifiers: []planmodifier.String{
ExamplePlanModifier(),
},
},
},
}
}
You’d get the following config/apply output:
resource "examplecloud_thing" "test" {
kms_key_id = "1234abcd-12ab-34cd-56ef-1234567890ab"
}
$ terraform apply -auto-approve
Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
+ create
Terraform will perform the following actions:
# examplecloud_thing.test will be created
+ resource "examplecloud_thing" "test" {
+ kms_key_id = "1234abcd-12ab-34cd-56ef-1234567890ab"
}
Plan: 1 to add, 0 to change, 0 to destroy.
examplecloud_thing.test: Creating...
examplecloud_thing.test: Creation complete after 0s
Apply complete! Resources: 1 added, 0 changed, 0 destroyed.
resource "examplecloud_thing" "test" {
# removed the kms_key_id
}
$ terraform apply -auto-approve
examplecloud_thing.test: Refreshing state...
Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
-/+ destroy and then create replacement
Terraform will perform the following actions:
# examplecloud_thing.test must be replaced
-/+ resource "examplecloud_thing" "test" {
~ kms_key_id = "1234abcd-12ab-34cd-56ef-1234567890ab" -> "AWS_OWNED_KMS_KEY" # forces replacement
}
Plan: 1 to add, 0 to change, 1 to destroy.
examplecloud_thing.test: Destroying...
examplecloud_thing.test: Destruction complete after 0s
examplecloud_thing.test: Creating...
examplecloud_thing.test: Creation complete after 0s
Apply complete! Resources: 1 added, 0 changed, 1 destroyed.
You can tweak the plan modification for your specific use-case/business rules, but the general idea is when requiring a replacement, change the plan value for the attribute as well if it’s not already.