Bulk API Terraform Limitations
Terraform Update-in-Place Dependency Behavior with “use_bulk_api” flag in Cisco Catalyst Center
Section titled “Terraform Update-in-Place Dependency Behavior with “use_bulk_api” flag in Cisco Catalyst Center”Applies to: Catalyst Center Provider >= 0.5.2, NAC Module >= 0.4.0 with
use_bulk_api = true
This document describes Terraform’s update-in-place behavior when managing hierarchical site structures in Cisco Catalyst Center using the bulk API.
This behavior is most commonly observed when a user attempts to partially remove hierarchical site entries managed via bulk resources in a single Terraform apply
Overview
Section titled “Overview”When use_bulk_api = true, the Cisco Catalyst Center module manages sites using bulk resources:
catalystcenter_areas.bulk_areascatalystcenter_buildings.bulk_buildingscatalystcenter_floors.bulk_floors
Each resource contains a map of all items at that hierarchy level. Removing an item from the configuration triggers an update-in-place operation on the parent resource, not an explicit destroy.
The Core Issue
Section titled “The Core Issue”Terraform treats update-in-place operations with the same dependency ordering as CREATE, not DESTROY.
| Operation Type | Execution Order |
|---|---|
| Create | Areas → Buildings → Floors |
| Update in place | Areas → Buildings → Floors |
| Destroy | Floors → Buildings → Areas (reversed) |
When removing site hierarchy elements, Terraform sees “update the Areas list” and processes it before Buildings and Floors, even though Catalyst Center requires children to be deleted first.
Module Resource Definitions
Section titled “Module Resource Definitions”The module defines explicit depends_on relationships that enforce creation order:
# Areas have no site dependencies (created first)resource "catalystcenter_areas" "bulk_areas" {...... depends_on = [catalystcenter_discovery.discovery, ...]}
# Buildings depend on Areasresource "catalystcenter_buildings" "bulk_buildings" {......
depends_on = [catalystcenter_areas.bulk_areas, ...] # <-- Buildings wait for Areas}
# Floors depend on Buildingsresource "catalystcenter_floors" "bulk_floors" {......
depends_on = [catalystcenter_building.building, catalystcenter_buildings.bulk_buildings, ...] # <-- Floors wait for Buildings}Key point: The depends_on relationships form a chain: Areas → Buildings → Floors. This ensures correct creation order, but during update-in-place operations, this same order is followed, which is against Catalyst Center’s deletion requirements.
Concrete Example: France Site Hierarchy
Section titled “Concrete Example: France Site Hierarchy”Initial Configuration
Section titled “Initial Configuration”catalyst_center: sites: areas: - name: Global - name: France parent_name: Global - name: Paris parent_name: Global/France - name: Poland parent_name: Global - name: Krakow parent_name: Global/Poland
buildings: - name: Paris_Bld_A parent_name: Global/France/Paris latitude: 48.8575 longitude: 2.3514 country: France - name: Paris_Bld_B parent_name: Global/France/Paris latitude: 48.8575 longitude: 2.3514 country: France - name: KRK_Bld_A latitude: 50.0623225 longitude: 19.937975 country: Poland parent_name: Global/Poland/Krakow
floors: - name: Floor_1 floor_number: 1 parent_name: Global/France/Paris/Paris_Bld_A - name: FLOOR_1 floor_number: 1 parent_name: Global/Poland/Krakow/KRK_Bld_AThis creates the following hierarchy in Catalyst Center:
Global├── France (Area)│ └── Paris (Area)│ ├── Paris_Bld_A (Building)│ │ └── Floor_1 (Floor)│ └── Paris_Bld_B (Building)│└── Poland (Area) └── Krakow (Area) └── KRK_Bld_A (Building) └── FLOOR_1 (Floor)Why Update-in-Place Occurs
Section titled “Why Update-in-Place Occurs”When removing France from the configuration while Poland/Krakow remains, Terraform sees:
- The
catalystcenter_areas.bulk_areasresource still exists (Poland areas remain in the map) - The
catalystcenter_buildings.bulk_buildingsresource still exists (KRK_Bld_A remains in the map) - The
catalystcenter_floors.bulk_floorsresource still exists (FLOOR_1 remains in the map)
Because items remain in each map, Terraform classifies this as update-in-place—not destroy. The bulk resources continue to exist; only their contents change (France entries removed, Poland entries unchanged).
If you were removing all sites (emptying the maps entirely), Terraform would destroy the bulk resources, which would trigger proper DESTROY ordering (Floors → Buildings → Areas). But partial removal from a map is always an update-in-place operation.
Deletion Attempt: Single Apply (Fails)
Section titled “Deletion Attempt: Single Apply (Fails)”Goal: Remove the entire France site hierarchy in one operation.
Action: Remove all France-related entries from sites.nac.yaml and run terraform apply.
Terraform Plan Output:
# module.catalyst_center.catalystcenter_areas.bulk_areas[0] will be updated in-place~ resource "catalystcenter_areas" "bulk_areas" { ~ areas = { - "Global/France" = { - id = "4d359396-59b8-4931-ac80-274cf6b3db65" -> null - name = "France" -> null - parent_name_hierarchy = "Global" -> null } - "Global/France/Paris" = { - id = "c565b323-b3dc-4a67-ae95-fd34ce20f96d" -> null - name = "Paris" -> null - parent_name_hierarchy = "Global/France" -> null } } }
# module.catalyst_center.catalystcenter_buildings.bulk_buildings[0] will be updated in-place~ resource "catalystcenter_buildings" "bulk_buildings" { ~ buildings = { - "Global/France/Paris/Paris_Bld_A" = { - id = "53855e96-25aa-436a-95a2-56a7ab8c71ab" -> null - name = "Paris_Bld_A" -> null - parent_name_hierarchy = "Global/France/Paris" -> null } - "Global/France/Paris/Paris_Bld_B" = { - id = "9b7550f4-f2ad-43bb-9834-3fad9e0e0b45" -> null - name = "Paris_Bld_B" -> null - parent_name_hierarchy = "Global/France/Paris" -> null } } }
Plan: 0 to add, 4 to change, 0 to destroy.Result: Terraform attempts to update Areas first (following CREATE order), but Catalyst Center rejects the request:
Error: Client Error
with module.catalyst_center.catalystcenter_areas.bulk_areas[0], on .terraform/modules/catalyst_center/cc_sites.tf line 12, in resource "catalystcenter_areas" "bulk_areas": 12: resource "catalystcenter_areas" "bulk_areas" {
Failed to delete object (DELETE), got error: task failed:"NCGR10012: Group cannot be deleted as there are child groups. Please delete them first."Why it fails: Terraform processed the Areas update before Buildings and Floors because update-in-place follows CREATE ordering (Areas → Buildings → Floors), not DESTROY ordering (Floors → Buildings → Areas).
Solution: Multi-Step Deletion
Section titled “Solution: Multi-Step Deletion”To align Terraform’s execution with Catalyst Center’s hierarchical constraints, deletion must be performed in multiple applies.
Step 1: Remove Floors
Section titled “Step 1: Remove Floors”Edit sites.nac.yaml to remove only the France floor entries (Poland floors remain):
catalyst_center: sites: areas: - name: Global - name: France parent_name: Global - name: Paris parent_name: Global/France - name: Poland parent_name: Global - name: Krakow parent_name: Global/Poland
buildings: - name: Paris_Bld_A parent_name: Global/France/Paris latitude: 48.8575 longitude: 2.3514 country: France - name: Paris_Bld_B parent_name: Global/France/Paris latitude: 48.8575 longitude: 2.3514 country: France - name: KRK_Bld_A latitude: 50.0623225 longitude: 19.937975 country: Poland parent_name: Global/Poland/Krakow
floors: # France Floor_1 removed - name: FLOOR_1 floor_number: 1 parent_name: Global/Poland/Krakow/KRK_Bld_ARun terraform apply. Only the Floors resource is updated (France floor removed), Areas and Buildings remain unchanged.
Step 2: Remove Buildings
Section titled “Step 2: Remove Buildings”Edit sites.nac.yaml to remove France building entries (Poland buildings remain):
catalyst_center: sites: areas: - name: Global - name: France parent_name: Global - name: Paris parent_name: Global/France - name: Poland parent_name: Global - name: Krakow parent_name: Global/Poland
buildings: # France buildings removed - name: KRK_Bld_A latitude: 50.0623225 longitude: 19.937975 country: Poland parent_name: Global/Poland/Krakow
floors: - name: FLOOR_1 floor_number: 1 parent_name: Global/Poland/Krakow/KRK_Bld_ARun terraform apply. France buildings are removed while Areas and Poland hierarchy remain.
Step 3: Remove Areas
Section titled “Step 3: Remove Areas”Edit sites.nac.yaml to remove France area entries (Poland areas remain):
catalyst_center: sites: areas: - name: Global # France and Paris areas removed - name: Poland parent_name: Global - name: Krakow parent_name: Global/Poland
buildings: - name: KRK_Bld_A latitude: 50.0623225 longitude: 19.937975 country: Poland parent_name: Global/Poland/Krakow
floors: - name: FLOOR_1 floor_number: 1 parent_name: Global/Poland/Krakow/KRK_Bld_ARun terraform apply. France areas are now safely removed. Poland hierarchy remains intact.
Why This Happens
Section titled “Why This Happens”Terraform’s Perspective
Section titled “Terraform’s Perspective”Terraform categorizes operations as:
- Create: New resource
- Update in place: Existing resource, modify attributes
- Replace: Destroy + Create (ForceNew)
- Destroy: Remove resource entirely
Removing items from a bulk resource’s map is an update-in-place, the resource continues to exist, just with fewer items. Terraform only reverses dependency order for explicit Destroy operations.
The Dependency Chain
Section titled “The Dependency Chain”depends_on chain: catalystcenter_areas.bulk_areas └── catalystcenter_buildings.bulk_buildings └── catalystcenter_floors.bulk_floorsFor CREATE: Areas → Buildings → Floors (correct) For UPDATE: Areas → Buildings → Floors (problematic for deletions) For DESTROY: Floors → Buildings → Areas (would be correct, but not triggered)
Key Takeaways
Section titled “Key Takeaways”- Update-in-place follows CREATE ordering, not DESTROY ordering
- Bulk resources (maps of items) treat partial item removal as an update-in-place
- Catalyst Center enforces hierarchy: children must be deleted before parents
- Multi-step deletion is required when partially removing entire site hierarchies
- This behavior applies to Provider >= 0.5.2, Module >= 0.4.0 with
use_bulk_api = true
One-Sentence Rule
Section titled “One-Sentence Rule”Update-in-place follows CREATE ordering. Only explicit DESTROY reverses dependencies. Delete children before parents using multiple applies.
This behavior may also occur for other hierarchical, bulk-managed resources when the bulk_site_provisioning flag is enabled,due to deletions being handled as update-in-place operations