Quality Form Definition HCL Drift on Nested Section Updates

Why does this setting in the genesyscloud_quality_form_definition resource force a full recreation when modifying a nested section order? The environment is Genesys Cloud v2024-06 with Terraform Provider v1.12.0. Running terraform plan shows a diff on the sections block despite the UUIDs remaining static. This breaks our CI/CD pipeline for incremental QA form updates.

The HCL structure uses a list of maps for sections. Changing the index or adding a new evaluation criterion triggers a destroy/create cycle instead of an in-place update. The API endpoint /api/v2/quality/formdefinitions supports PATCH, but the provider seems to ignore it for this specific block.

Is there a workaround to preserve the resource ID during structural changes to the form definition? Need to avoid downtime for active quality monitors.

What’s happening here is that Terraform treats the sections block as an ordered list by default. In Zendesk, we were used to updating ticket forms where the backend often handled reordering via separate API calls or ignored index shifts if the IDs matched. Genesys Cloud’s API for Quality Form Definitions is stricter. When you change the order, Terraform sees the entire list as different because the indices have shifted, triggering a destroy and recreate cycle. This is a common pain point during migrations from Zendesk to Genesys Cloud, where the state management feels less forgiving.

To fix this without breaking your CI/CD pipeline, you need to explicitly tell Terraform how to track these sections. Use the for_each meta-argument or define the sections as a map instead of a list if your provider version supports it for this specific resource. If you are stuck with a list structure, ensure you are using a stable key for ordering.

  1. Check if your Terraform provider version supports dynamic blocks for sections. This allows you to iterate over a local variable map, preserving identity based on keys rather than indices.
  2. If not, try moving the section definitions into a locals block as a map keyed by the section name or ID. Reference this map in the resource.
  3. Ensure that any nested items within sections also use stable identifiers. If you add a new item, append it at the end of the map/list to avoid shifting existing indices.
  4. Run terraform plan again. You should see only the modified section being updated, not the whole form.

This approach mirrors how we handled Zendesk custom field migrations-by decoupling the logical definition from the positional index. It keeps the state clean and prevents unexpected recreations during incremental updates.

The main issue here is that Terraform treats the sections block as an ordered list. Changing the index shifts everything, triggering a full destroy and recreate cycle.

Switch to a map structure keyed by section ID. This preserves identity during reordering. It aligns better with how I handle API throughput in JMeter configs anyway.

TL;DR: Use dynamic blocks with for_each to treat sections as a set, not a list.

The problem here is that Terraform’s default handling of list blocks is index-sensitive. When you reorder items, the indices change, so Terraform thinks every item changed. This forces a destroy-and-recreate cycle, which is brutal for Quality Form Definitions because you lose customizations and break agent workflows.

Instead of a static sections block, use a dynamic block driven by a for_each map. This ties each section to its unique ID, not its position. Reordering in the map doesn’t trigger a recreation because the identity remains stable. I use this pattern weekly for our WFM schedule publishing scripts to avoid cascading updates when shift swaps happen. It keeps the CI/CD pipeline clean and prevents accidental form resets. Check the provider docs for dynamic block examples with maps. It is a small config change that saves hours of debugging later.

Check your dynamic block implementation for the genesyscloud_quality_form_definition. While the suggestion above regarding for_each is correct for preserving identity, the syntax often trips people up when dealing with nested attributes in Genesys Cloud resources. The provider is strict about map vs list structures in the API payload.

Here is the robust pattern that aligns with how we handle bulk data structures in recording exports:

  • Define the source variable as a map of objects, keyed by the section ID.
  • Use a dynamic block inside the resource.
  • Iterate using for_each to ensure Terraform tracks changes by key, not index.
resource "genesyscloud_quality_form_definition" "qa_form" {
 name = "Customer Satisfaction Survey"
 
 dynamic "section" {
 for_each = var.qa_sections
 content {
 id = section.value.id
 name = section.value.name
 weight = section.value.weight
 
 dynamic "item" {
 for_each = section.value.items
 content {
 id = item.value.id
 text = item.value.text
 type = item.value.type
 required = item.value.required
 }
 }
 }
 }
}

In legal discovery workflows, we see similar drift when metadata fields are reordered in bulk export configurations. The chain of custody requires stable identifiers. If the index shifts, the audit trail breaks. By keying on the ID, you maintain the integrity of the resource state.

Also, verify that the section.value.id matches the exact UUID returned from the Genesys Cloud API. A mismatch here causes the provider to attempt a creation, resulting in a 409 conflict if the ID already exists in the system. This approach prevents the full recreation cycle and keeps your CI/CD pipeline stable.