-
Notifications
You must be signed in to change notification settings - Fork 99
Description
tl;dr: When implementing the moved block support you get an extra Note: Objects have changed outside of Terraform when the terraform configuration uses output variables referencing the moved.to resource or an attribute not mapped by MoveState, for example:
output "items" {
value = hashicups_order.edu.items
}Theory: Some part of the internal Terraform graph is updated when using an output variable, therefore, the mapped state after MoveState is called creates a diff with the state returned from Read.
What consequence can this have?
If you don't map all fields in MoveState implementation (sometimes impossible since Source.Schema != Target.Schema), users sees the Note: Objects have changed outside of Terraform and become afraid to complete the moved.
Module version
github.com/hashicorp/terraform-plugin-framework v1.14.1
Relevant provider source code
- Full source code in this branch
- Summary: only map
idfrom the source type, as the rest of the attributes are found in theRead
func (r *orderResource) MoveState(context.Context) []resource.StateMover {
return []resource.StateMover{{StateMover: stateMover}}
}
func stateMover(ctx context.Context, req resource.MoveStateRequest, resp *resource.MoveStateResponse) {
if req.SourceTypeName != "hashicups_order_legacy" || !strings.HasSuffix(req.SourceProviderAddress, "/hashicups"){
return
}
// Use always new sharding config when moving from cluster to adv_cluster
rawStateValue, err := req.SourceRawState.UnmarshalWithOpts(tftypes.Object{
AttributeTypes: map[string]tftypes.Type{
"id": tftypes.String,
},
}, tfprotov6.UnmarshalOpts{ValueFromJSONOpts: tftypes.ValueFromJSONOpts{IgnoreUndefinedAttributes: true}})
diags := &resp.Diagnostics
if err != nil {
diags.AddError("Unable to Unmarshal state", err.Error())
return
}
var stateObj map[string]tftypes.Value
if err := rawStateValue.As(&stateObj); err != nil {
diags.AddError("Unable to Parse state", err.Error())
return
}
var id *string
if err := stateObj["id"].As(&id); err != nil {
diags.AddError("Unable to Parse id from state", err.Error())
return
}
model := orderResourceModel{
ID: types.StringPointerValue(id),
}
diags.Append(resp.TargetState.Set(ctx, model)...)
}Terraform Configuration Files
terraform {
required_providers {
hashicups = {}
}
required_version = ">1.10"
}
provider "hashicups" {
host = "http://localhost:19090"
username = "education"
password = "test123"
}
# LEGACY RESOURCE
# resource "hashicups_order_legacy" "edu" {
# items = [{
# coffee = {
# id = 3
# }
# quantity = 2
# },
# {
# coffee = {
# id = 2
# }
# quantity = 3
# }]
# }
# NEW RESOURCE
moved {
from = hashicups_order_legacy.edu
to = hashicups_order.edu
}
resource "hashicups_order" "edu" {
items = [{
coffee = {
id = 3
}
quantity = 2
},
{
coffee = {
id = 2
}
quantity = 3
}]
}
# LEGACY OUTPUT VAR
# output "edu_order_legacy" {
# value = hashicups_order_legacy.edu
# }
# NEW OUTPUT VAR
output "edu_order" {
value = hashicups_order.edu
}
Debug Output (relevant parts see comment for full logs)
2025-03-11T11:22:28.157Z [INFO] backend/local: plan operation completed
# START UNEXPECTED PLAN OUTPUT #
Note: Objects have changed outside of Terraform
Terraform detected the following changes made outside of Terraform since the last "terraform apply" which may have affected this plan:
# hashicups_order.edu has changed
# (moved from hashicups_order_legacy.edu)
~ resource "hashicups_order" "edu" {
id = "3"
+ items = [
+ {
+ coffee = {
+ id = 3
+ image = "/vault.png"
+ name = "Vaulatte"
+ price = 200
+ teaser = "Nothing gives you a safe and secure feeling like a Vaulatte"
# (1 unchanged attribute hidden)
}
+ quantity = 2
},
+ {
+ coffee = {
+ id = 2
+ image = "/packer.png"
+ name = "Packer Spiced Latte"
+ price = 350
+ teaser = "Packed with goodness to spice up your images"
# (1 unchanged attribute hidden)
}
+ quantity = 3
},
]
}
Unless you have made equivalent changes to your configuration, or ignored the relevant attributes using ignore_changes, the following plan may include actions to undo or respond to these changes.
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
# END UNEXPECTED PLAN OUTPUT #
Terraform will perform the following actions:
# hashicups_order_legacy.edu has moved to hashicups_order.edu
resource "hashicups_order" "edu" {
id = "3"
# (1 unchanged attribute hidden)
}
Plan: 0 to add, 0 to change, 0 to destroy.
Expected Behavior
The Note: Objects have changed outside of Terraform section should not be there
Actual Behavior
Full Note: Objects have changed outside of Terraform
Steps to Reproduce
git clone https://github.com/EspenAlbert/terraform-provider-hashicups-1.gitgit checkout moved-with-output-refs-changes-outside-tfcd 12-finaldocker-compose up -dmake build- configure
$TF_CLI_CONFIG_FILEto the$(pwd)/bin cd examples/moved- uncomment
# LEGACY RESOURCEsection and comment# NEW RESOURCEsections terraform apply- comment
# LEGACY RESOURCEsection and uncomment# NEW RESOURCEsections terraform plan### References