Skip to content

fix(scope_definition): remove ignore_changes masking a redundant depends_on drift bug#438

Merged
sebastiancorrea81 merged 4 commits into
mainfrom
fix/scope-definition-ignore-changes-drift
Jul 15, 2026
Merged

fix(scope_definition): remove ignore_changes masking a redundant depends_on drift bug#438
sebastiancorrea81 merged 4 commits into
mainfrom
fix/scope-definition-ignore-changes-drift

Conversation

@sebastiancorrea81

Copy link
Copy Markdown
Collaborator

Summary

  • Fixed a real, reproducible drift in nullplatform_service_specification.attributes: the API silently persists a values = {} key that the rendered template never sets. Now mirrored via merge({ values = {} }, ...) instead of masking it with ignore_changes.
  • Found and fixed the actual root cause of the provider_type (scope_type) and type (action_specification) "phantom replace" issue: data.external.scope_type and data.external.action_specs each carried a redundant, explicit depends_on = [nullplatform_service_specification.from_template], on top of the implicit dependency already created via SERVICE_SPECIFICATION_ID. That explicit whole-resource depends_on deferred the entire data read to apply-time whenever service_specification had any pending change, marking provider_type/type as (known after apply) — and since both are ForceNew, this forced a phantom destroy+recreate of scope_type and of every action_specification instance at once.
  • Removed the now-unneeded ignore_changes on scope_type, action_specification, and nullplatform_notification_channel (in scope_definition_agent_association) — all verified field-by-field to never actually drift.

How this was verified

  • Field-by-field: removed each entry from ignore_changes one at a time, then planapplyplan to confirm no drift.
  • Reproduced the phantom-replace bug on purpose (reintroduced the attributes drift while the depends_on was still in place) — confirmed scope_type and all 15 action_specification instances showed must be replaced.
  • Removed the redundant depends_on and re-ran the same forced-drift scenario — confirmed zero diff on scope_type/action_specification even with a real pending change on service_specification.
  • Full end-to-end: destroyinitvalidateplan (21 to add) → apply (21 added) → plan again — No changes, confirming no drift even from a fresh create, with zero ignore_changes left in either module.

Test plan

  • tofu validate clean, no warnings (previously had a "Redundant ignore_changes element" warning on status)
  • tofu plan shows no changes against existing state
  • Forced-drift test confirms no cascading replace on scope_type/action_specification
  • Full destroy/recreate cycle confirms zero drift from scratch

sebas_correa added 3 commits July 14, 2026 16:11
…lly drift

service_specification.attributes: the API silently persists a `values = {}`
key that the rendered template never sets, causing perpetual drift on every
plan. Mirror it in config via merge() instead of hiding the diff.

service_specification.name/type, scope_type.status, action_specification.
name/annotations/parameters/results/retryable/icon: verified via repeated
plan+apply+plan cycles to never actually drift; removed from ignore_changes
so real changes are no longer masked.

scope_type.provider_type and action_specification.type: confirmed root
cause via forced-drift testing — both are populated from a data.external
keyed on service_specification.id, so ANY pending change on
service_specification (even unrelated ones, like the attributes fix above)
replans them as (known after apply). Since both are ForceNew, this forces
a phantom destroy+recreate — of scope_type, and of every
action_specification instance at once. These two must stay ignored.
…place

data.external.scope_type and data.external.action_specs each carried an
explicit `depends_on = [nullplatform_service_specification.from_template]`
that was already redundant — both implicitly depend on that resource via
SERVICE_SPECIFICATION_ID (derived from its .id). The explicit whole-resource
depends_on made OpenTofu defer the entire data read to apply-time whenever
service_specification had ANY pending change, marking their results
`(known after apply)`. Since provider_type/type are ForceNew, that phantom
unknown forced a destroy+recreate of scope_type and of every
action_specification instance.

Removing the redundant depends_on eliminates the cascade at the source:
confirmed via forced-drift testing that scope_type and action_specification
show zero diff even while service_specification has a real pending change.
This makes the previous ignore_changes=[provider_type]/[type] workaround
unnecessary — removed along with the comments explaining it.
source/type on nullplatform_notification_channel had no explicit
resource-level depends_on and no cascade risk (their inputs come from
already-known module output variables from scope_definition, which no
longer produces phantom unknowns after the earlier fix). Verified via
repeated plan+apply+plan cycles, including forcing a real drift upstream
in service_specification, that neither field ever drifts or cascades.

replace_triggered_by on terraform_data.api_key_trigger is untouched — it's
a deliberate mechanism to rotate the notification channel when api_key
changes, unrelated to this drift investigation.
@sebastiancorrea81
sebastiancorrea81 merged commit 2a715b6 into main Jul 15, 2026
44 checks passed
@sebastiancorrea81
sebastiancorrea81 deleted the fix/scope-definition-ignore-changes-drift branch July 15, 2026 13:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants