Skip to content

Upgrade Procedures

Those guides describes the changes required to upgrade your Catalyst Center Terraform module version. Each upgrade procedure covers the changes necessary to move between two consecutive releases (subsequent versions).

After completing all the changes below, your data model will be compatible with Catalyst Center Terraform module version 0.4.0. This upgrade involves:

  1. Terraform State Fixes (Required for all users with templates):

    • Fix params schema change in template deployments
    • Fix comments hash change in template versions
  2. Bulk API Migration (Only if use_bulk_api = true):

    • Migrate individual site resources to bulk resources
  3. Multi-State Considerations (Only if using multiple state files):

    • Follow specific migration order (GLOBAL first, then site states)
  4. Data Model Updates (Review and update as needed):

    • Update IP pool type enum values (capitalization change)
    • Rename associated_l3_virtual_network to associated_l3_virtual_network_name in L2 VNs
    • Rename dot11be_profile to dot11be_profile_name in wireless profiles
    • Remove deprecated discovery SNMP attributes (use device credentials instead)
    • Remove description from SNMPv2 credentials
  5. Review New Features:

    • Extended site hierarchy (up to 10 levels)
    • 802.11be (WiFi 7) profiles
    • Border device SDA transit attributes
    • LLDP discovery level
Your SetupSections to Follow
Single state, use_bulk_api = false1, 2, 5
Single state, use_bulk_api = true1, 2, 3, 5
Multi-state, use_bulk_api = false1, 2, 4, 5
Multi-state, use_bulk_api = true1, 2, 3, 4, 5

Before starting the migration:

  1. Backup all state files
Terminal window
cp terraform.tfstate terraform.tfstate.backup.pre-migration
  1. Update configuration - Edit main.tf:
terraform {
required_providers {
catalystcenter = {
source = "CiscoDevNet/catalystcenter"
version = "0.5.2"
}
}
}
module "catalyst_center" {
source = "netascode/nac-catalystcenter/catalystcenter"
version = "0.4.0"
# ... rest of configuration
}
  1. Initialize
Terminal window
terraform init -upgrade

1. Template Deployment params Schema Change (Breaking Change)

Section titled “1. Template Deployment params Schema Change (Breaking Change)”

The params attribute in catalystcenter_deploy_template changed from map(string) to map(list(string)). After terraform init -upgrade, you may see:

Error: Unable to Read Previously Saved State for UpgradeResourceState
with module.catalyst_center.catalystcenter_deploy_template.regular_template_deploy["..."],
AttributeName("target_info")...AttributeName("params").ElementKeyString("interface"):
invalid JSON, expected "[", got "GigabitEthernet1/0/3"
  1. Open terraform.tfstate
  2. Search for "type": "catalystcenter_deploy_template"
  3. Convert string values to arrays in the params object:

Before:

"params": {
"interface": "GigabitEthernet1/0/3",
"vlan_id": "100"
}

After:

"params": {
"interface": ["GigabitEthernet1/0/3"],
"vlan_id": ["100"]
}

Save as fix_params.py:

#!/usr/bin/env python3
"""Fix params schema: convert string values to arrays"""
import json, shutil
from datetime import datetime
STATE_FILE = "terraform.tfstate"
BACKUP_FILE = f"terraform.tfstate.backup.params.{datetime.now().strftime('%Y%m%d_%H%M%S')}"
def fix_state():
print(f"Creating backup: {BACKUP_FILE}")
shutil.copy(STATE_FILE, BACKUP_FILE)
with open(STATE_FILE, 'r') as f:
state = json.load(f)
fixed = 0
for resource in state.get('resources', []):
if resource.get('type') == 'catalystcenter_deploy_template':
for instance in resource.get('instances', []):
for target in instance.get('attributes', {}).get('target_info', []):
params = target.get('params')
if params:
for key, value in params.items():
if isinstance(value, str):
params[key] = [value]
print(f" Fixed: {key}: \"{value}\" -> [\"{value}\"]")
fixed += 1
with open(STATE_FILE, 'w') as f:
json.dump(state, f, indent=2)
print(f"\nFixed {fixed} params. Backup: {BACKUP_FILE}")
if __name__ == '__main__':
fix_state()

Run: python3 fix_params.py

2. Template Version comments Hash Change (Breaking Change)

Section titled “2. Template Version comments Hash Change (Breaking Change)”

After fixing params (or if no params issue), terraform plan may show template versions being replaced:

# module.catalyst_center.catalystcenter_template_version.regular_commit_version["ACL_Block"] must be replaced
-/+ resource "catalystcenter_template_version" "regular_commit_version" {
~ comments = "5438fad09e3629ac8a8526fe5f14ff42" -> "b70f511ffef5100fd8dbb8255b5a06ad" # forces replacement
}

The module changed how the comments MD5 hash is calculated. Since comments forces replacement, Terraform wants to destroy and recreate template versions.

  1. Run terraform plan and note the new hash values
  2. Open terraform.tfstate
  3. Search for "type": "catalystcenter_template_version"
  4. Update comments to match plan output:

Before:

"attributes": {
"comments": "5438fad09e3629ac8a8526fe5f14ff42",
...
}

After:

"attributes": {
"comments": "b70f511ffef5100fd8dbb8255b5a06ad",
...
}

Save as fix_comments.py:

#!/usr/bin/env python3
"""Fix template version comments hash"""
import json, shutil
from datetime import datetime
STATE_FILE = "terraform.tfstate"
BACKUP_FILE = f"terraform.tfstate.backup.comments.{datetime.now().strftime('%Y%m%d_%H%M%S')}"
# UPDATE THESE VALUES from your terraform plan output!
# Run: terraform plan | grep -B2 -A2 "comments"
NEW_COMMENTS = {
"ACL_Block": "b70f511ffef5100fd8dbb8255b5a06ad",
"PNP_Template": "499c50145f21844124f99481e741ac2c",
# Add your templates here...
}
def fix_comments():
print(f"Creating backup: {BACKUP_FILE}")
shutil.copy(STATE_FILE, BACKUP_FILE)
with open(STATE_FILE, 'r') as f:
state = json.load(f)
fixed = 0
for resource in state.get('resources', []):
if resource.get('type') == 'catalystcenter_template_version':
for instance in resource.get('instances', []):
key = instance.get('index_key')
if key in NEW_COMMENTS:
old = instance['attributes'].get('comments', '')
new = NEW_COMMENTS[key]
if old != new:
instance['attributes']['comments'] = new
print(f" [{key}]: {old} -> {new}")
fixed += 1
with open(STATE_FILE, 'w') as f:
json.dump(state, f, indent=2)
print(f"\nFixed {fixed} comments. Backup: {BACKUP_FILE}")
if __name__ == '__main__':
fix_comments()

Usage:

Terminal window
# 1. Get new hash values
terraform plan | grep -B2 -A2 "comments"
# 2. Update NEW_COMMENTS dict in script
# 3. Run
python3 fix_comments.py

3. Bulk API Site Migration (Only if use_bulk_api = true)

Section titled “3. Bulk API Site Migration (Only if use_bulk_api = true)”

When upgrading with use_bulk_api = true, Terraform switches from individual resources to bulk resources:

Individual (Destroyed)Bulk (Created)
catalystcenter_area.area_0["Global/Poland"]catalystcenter_areas.bulk_areas[0]
catalystcenter_building.building["..."]catalystcenter_buildings.bulk_buildings[0]
catalystcenter_floor.floor["..."]catalystcenter_floors.bulk_floors[0]

Problem: A direct apply would destroy and recreate sites, potentially failing if fabric/devices are provisioned.

Solution: Migrate state without destroying resources.

Terminal window
# List resources to migrate
terraform state list | grep -E "catalystcenter_area\.|catalystcenter_building\.|catalystcenter_floor\."
# Get details (record the id values)
terraform state show 'module.catalyst_center.catalystcenter_area.area_0["Global/Poland"]'
Step 3.2: Remove Individual Resources from State
Section titled “Step 3.2: Remove Individual Resources from State”
Terminal window
# Remove areas
terraform state rm 'module.catalyst_center.catalystcenter_area.area_0["Global/Poland"]'
terraform state rm 'module.catalyst_center.catalystcenter_area.area_1["Global/Poland/Krakow"]'
# ... repeat for all areas
# Remove buildings
terraform state rm 'module.catalyst_center.catalystcenter_building.building["Global/Poland/Krakow/Bld A"]'
# ... repeat for all buildings
# Remove floors
terraform state rm 'module.catalyst_center.catalystcenter_floor.floor["Global/Poland/Krakow/Bld A/FLOOR_1"]'
# ... repeat for all floors

The import ID is the scope path (parent of all managed sites):

Terminal window
# Import bulk areas
terraform import 'module.catalyst_center.catalystcenter_areas.bulk_areas[0]' 'Global/Poland'
# Import bulk buildings
terraform import 'module.catalyst_center.catalystcenter_buildings.bulk_buildings[0]' 'Global/Poland'
# Import bulk floors
terraform import 'module.catalyst_center.catalystcenter_floors.bulk_floors[0]' 'Global/Poland'
Terminal window
terraform plan
# Expected: 1 to add, ~4 to change, 0 to destroy

Save as migrate_to_bulk.py:

#!/usr/bin/env python3
"""Migrate individual site resources to bulk resources"""
import json, shutil
from datetime import datetime
STATE_FILE = "terraform.tfstate"
BACKUP_FILE = f"terraform.tfstate.backup.bulk.{datetime.now().strftime('%Y%m%d_%H%M%S')}"
def migrate_state():
print(f"Creating backup: {BACKUP_FILE}")
shutil.copy(STATE_FILE, BACKUP_FILE)
with open(STATE_FILE, 'r') as f:
state = json.load(f)
collected = {"areas": {}, "buildings": {}, "floors": {}}
to_remove = []
for i, resource in enumerate(state.get('resources', [])):
if resource.get('mode') != 'managed':
continue
res_type = resource.get('type')
if res_type == 'catalystcenter_area':
to_remove.append(i)
for inst in resource.get('instances', []):
key = inst.get('index_key', '')
if key:
attrs = inst.get('attributes', {})
parts = key.rsplit('/', 1)
collected['areas'][key] = {
"id": attrs.get('id'),
"name": attrs.get('name'),
"parent_id": attrs.get('parent_id'),
"parent_name_hierarchy": parts[0] if len(parts) > 1 else "Global"
}
print(f" Collected area: {key}")
elif res_type == 'catalystcenter_building':
to_remove.append(i)
for inst in resource.get('instances', []):
key = inst.get('index_key', '')
if key:
attrs = inst.get('attributes', {})
parts = key.rsplit('/', 1)
collected['buildings'][key] = {
"id": attrs.get('id'),
"name": attrs.get('name'),
"parent_id": attrs.get('parent_id'),
"parent_name_hierarchy": parts[0] if len(parts) > 1 else "Global",
"address": attrs.get('address'),
"country": attrs.get('country'),
"latitude": attrs.get('latitude'),
"longitude": attrs.get('longitude')
}
print(f" Collected building: {key}")
elif res_type == 'catalystcenter_floor':
to_remove.append(i)
for inst in resource.get('instances', []):
key = inst.get('index_key', '')
if key:
attrs = inst.get('attributes', {})
parts = key.rsplit('/', 1)
collected['floors'][key] = {
"id": attrs.get('id'),
"name": attrs.get('name'),
"parent_id": attrs.get('parent_id'),
"parent_name_hierarchy": parts[0] if len(parts) > 1 else "Global",
"floor_number": attrs.get('floor_number'),
"height": attrs.get('height'),
"length": attrs.get('length'),
"width": attrs.get('width'),
"rf_model": attrs.get('rf_model'),
"units_of_measure": attrs.get('units_of_measure')
}
print(f" Collected floor: {key}")
# Remove individual resources
for i in sorted(set(to_remove), reverse=True):
del state['resources'][i]
# Add bulk resources
provider = 'provider["registry.terraform.io/ciscodevnet/catalystcenter"]'
module = "module.catalyst_center"
if collected['areas']:
state['resources'].append({
"module": module, "mode": "managed",
"type": "catalystcenter_areas", "name": "bulk_areas",
"provider": provider,
"instances": [{"index_key": 0, "schema_version": 0,
"attributes": {"areas": collected['areas'], "id": "areas-bulk", "scope": None},
"sensitive_attributes": [], "dependencies": []}]
})
print(f"Created bulk_areas: {len(collected['areas'])} area(s)")
if collected['buildings']:
state['resources'].append({
"module": module, "mode": "managed",
"type": "catalystcenter_buildings", "name": "bulk_buildings",
"provider": provider,
"instances": [{"index_key": 0, "schema_version": 0,
"attributes": {"buildings": collected['buildings'], "id": "buildings-bulk", "scope": None},
"sensitive_attributes": [], "dependencies": []}]
})
print(f"Created bulk_buildings: {len(collected['buildings'])} building(s)")
if collected['floors']:
state['resources'].append({
"module": module, "mode": "managed",
"type": "catalystcenter_floors", "name": "bulk_floors",
"provider": provider,
"instances": [{"index_key": 0, "schema_version": 0,
"attributes": {"floors": collected['floors'], "id": "floors-bulk", "scope": None},
"sensitive_attributes": [], "dependencies": []}]
})
print(f"Created bulk_floors: {len(collected['floors'])} floor(s)")
state['serial'] += 1
with open(STATE_FILE, 'w') as f:
json.dump(state, f, indent=2)
print(f"\nMigration complete! Backup: {BACKUP_FILE}")
if __name__ == '__main__':
migrate_state()

Run: python3 migrate_to_bulk.py && terraform plan

4. Multi-State Migration Order (Only if using multiple state files)

Section titled “4. Multi-State Migration Order (Only if using multiple state files)”

Multi-state setups require specific migration order: GLOBAL state first, then site states.

  1. Templates are in GLOBAL; site states reference them via deploy_template
  2. Parent sites must exist before child sites
  3. L3 VNs are global; site states create l3_vn_to_fabric_site mappings
Terminal window
cd terraform_multistate_global/
# Backup
cp terraform.tfstate terraform.tfstate.backup
# Upgrade
terraform init -upgrade
terraform plan

GLOBAL typically needs:

  • comments fix (template versions)
  • Bulk area migration (if use_bulk_api = true)
Terminal window
# Apply fixes as needed (see Sections 1, 2, and 3)
python3 fix_comments.py
# For bulk API: migrate parent areas only
# ... (see Section 3)
# Verify and apply
terraform plan # Should show: 1 add, 0 destroy
terraform apply
Phase 2: Site States (Can be parallel after GLOBAL)
Section titled “Phase 2: Site States (Can be parallel after GLOBAL)”
Terminal window
cd terraform_multistate_site_a/
cp terraform.tfstate terraform.tfstate.backup
terraform init -upgrade
terraform plan
# Site states typically need:
# - params fix (deploy_template)
# - Bulk migration (if use_bulk_api = true)
python3 fix_params.py
python3 migrate_to_bulk.py # if bulk API
terraform plan # Should show: 1 add, 0 destroy
terraform apply

When using bulk API with multi-state, after terraform import, the bulk resource may contain ALL areas under Global, including areas from other states.

Solution: Filter the bulk resource to only include managed areas:

#!/usr/bin/env python3
"""Fix bulk_areas to only include sites managed by this state"""
import json, shutil
from datetime import datetime
STATE_FILE = "terraform.tfstate"
BACKUP_FILE = f"terraform.tfstate.backup.filter.{datetime.now().strftime('%Y%m%d_%H%M%S')}"
# UPDATE: Sites managed by THIS state only
MANAGED_AREAS = ["Global/Poland"] # For GLOBAL state
# MANAGED_AREAS = ["Global/Poland/Krakow"] # For SITE_A state
def fix_bulk():
shutil.copy(STATE_FILE, BACKUP_FILE)
with open(STATE_FILE, 'r') as f:
state = json.load(f)
for resource in state.get('resources', []):
if resource.get('type') == 'catalystcenter_areas':
for inst in resource.get('instances', []):
areas = inst.get('attributes', {}).get('areas', {})
filtered = {k: v for k, v in areas.items() if k in MANAGED_AREAS}
inst['attributes']['areas'] = filtered
print(f"Filtered: {list(areas.keys())} -> {list(filtered.keys())}")
state['serial'] += 1
with open(STATE_FILE, 'w') as f:
json.dump(state, f, indent=2)
if __name__ == '__main__':
fix_bulk()
5.1 IP Pool Type Enum Values (Capitalization Change)
Section titled “5.1 IP Pool Type Enum Values (Capitalization Change)”

The type enum values for IP pools and IP pool reservations have been updated to use capitalized values.

catalyst_center:
network_settings:
ip_pools:
- name: POOL_1
ip_address_space: IPv4
type: generic # lowercase
ip_pools_reservations:
- name: RESERVATION_1
type: generic # lowercase
catalyst_center:
network_settings:
ip_pools:
- name: POOL_1
ip_address_space: IPv4
type: Generic # Capitalized
ip_pools_reservations:
- name: RESERVATION_1
type: Generic # Capitalized
0.3.00.4.0
genericGeneric
tunnelTunnel

The associated_l3_virtual_network attribute has been renamed to associated_l3_virtual_network_name for clarity.

catalyst_center:
fabric:
fabric_sites:
- name: Global/Site1
l2_virtual_networks:
- name: L2_VN_1
vlan_name: VLAN_100
vlan_id: 100
associated_l3_virtual_network: Campus # Old attribute name
catalyst_center:
fabric:
fabric_sites:
- name: Global/Site1
l2_virtual_networks:
- name: L2_VN_1
vlan_name: VLAN_100
vlan_id: 100
associated_l3_virtual_network_name: Campus # New attribute name
5.3 Wireless Network Profile SSID Attribute Rename
Section titled “5.3 Wireless Network Profile SSID Attribute Rename”

The dot11be_profile attribute has been renamed to dot11be_profile_name for consistency.

catalyst_center:
network_profiles:
wireless:
- name: WirelessProfile_1
ssid_details:
- name: SSID_1
enable_fabric: true
dot11be_profile: "WiFi7_Profile" # Old attribute name
catalyst_center:
network_profiles:
wireless:
- name: WirelessProfile_1
ssid_details:
- name: SSID_1
enable_fabric: true
dot11be_profile_name: "WiFi7_Profile" # New attribute name

The following SNMP-related attributes have been removed from the discovery configuration. SNMP credentials should now be configured using the device credentials under network_settings.device_credentials.

Removed AttributeAlternative
snmp_versionUse snmpv2_read_credentials, snmpv2_write_credentials, or snmpv3_credentials
snmp_user_nameUse snmpv3_credentials.username
snmp_rw_community_descRemoved (not needed)
snmp_ro_community_descRemoved (not needed)
snmp_ro_communityUse snmpv2_read_credentials.read_community
snmp_rw_communityUse snmpv2_write_credentials.write_community
snmp_priv_protocolUse snmpv3_credentials.privacy_type
snmp_priv_passphraseUse snmpv3_credentials.privacy_password
snmp_modeUse snmpv3_credentials.snmp_mode
snmp_auth_protocolUse snmpv3_credentials.auth_type
snmp_auth_passphraseUse snmpv3_credentials.auth_password
https_read_credentialUse global_credential_list with credential name
http_write_credentialUse global_credential_list with credential name
catalyst_center:
inventory:
discovery:
- name: DISCOVERY_1
type: Single
ip_address_list: "10.1.1.1"
snmp_version: v3
snmp_user_name: snmp_user
snmp_mode: AuthPriv
snmp_auth_protocol: SHA
snmp_auth_passphrase: auth_pass
snmp_priv_protocol: AES128
snmp_priv_passphrase: priv_pass
catalyst_center:
network_settings:
device_credentials:
snmpv3_credentials:
- name: SNMPV3_CRED
username: snmp_user
snmp_mode: AUTHPRIV
auth_type: SHA
auth_password: auth_pass
privacy_type: AES128
privacy_password: priv_pass
inventory:
discovery:
- name: DISCOVERY_1
type: Single
ip_address_list: "10.1.1.1"
global_credential_list:
- SNMPV3_CRED
5.5 SNMPv2 Credential Description Attribute Removed
Section titled “5.5 SNMPv2 Credential Description Attribute Removed”

The description attribute has been removed from SNMPv2 read and write credentials.

catalyst_center:
network_settings:
device_credentials:
snmpv2_read_credentials:
- name: SNMPV2_READ
description: "Read-only community" # Removed in 0.4.0
read_community: public
snmpv2_write_credentials:
- name: SNMPV2_WRITE
description: "Read-write community" # Removed in 0.4.0
write_community: private
catalyst_center:
network_settings:
device_credentials:
snmpv2_read_credentials:
- name: SNMPV2_READ
read_community: public
snmpv2_write_credentials:
- name: SNMPV2_WRITE
write_community: private
5.6 Template Variable Value Type Enhancement
Section titled “5.6 Template Variable Value Type Enhancement”

Template variables now support both single string values and list values. This is a non-breaking change - existing single string values continue to work.

catalyst_center:
inventory:
devices:
- name: DEVICE_1
dayn_templates:
regular:
- name: TEMPLATE_1
variables:
- name: interface
value: "GigabitEthernet1/0/1"
catalyst_center:
inventory:
devices:
- name: DEVICE_1
dayn_templates:
regular:
- name: TEMPLATE_1
variables:
- name: interface
value: "GigabitEthernet1/0/1" # Single value still works
- name: vlans
value: # List values now supported
- "100"
- "200"
- "300"

After completing all applicable sections:

Terminal window
terraform plan

Expected results:

SetupExpected Plan
Non-bulk API1 to add, 0 to destroy (managed_sites_validation)
Bulk API1 to add, ~4 to change, 0 to destroy

If you still see destroy/create operations:

  • Template versions being replaced? → Verify comments fix (Section 2)
  • Areas/buildings/floors being replaced? → See bulk migration (Section 3)
  • State read errors? → Verify params fix (Section 1)
SymptomCauseFix
”Error decoding… missing expected [“params schema changeRun fix_params.py (Section 1)
template_version must be replacedcomments hash changeRun fix_comments.py (Section 2)
Large destroy/create count after params/comments fixBulk migration neededSee Section 3
Site state fails after GLOBAL migrationWrong orderApply GLOBAL first (Section 4)

If anything goes wrong:

Terminal window
cp terraform.tfstate.backup.pre-migration terraform.tfstate

6. New Features in 0.4.0 (No Migration Required)

Section titled “6. New Features in 0.4.0 (No Migration Required)”

The following new features are available in version 0.4.0. No migration is required for existing configurations, but you may want to leverage these new capabilities:

  • Extended Site Hierarchy: Support for up to 10 area levels
catalyst_center:
sites:
areas:
- name: Level1
parent_name: Global
- name: Level2
parent_name: Global/Level1
# ... up to Global/area/area/area/area/area/area/area/area/area/area
  • Floor Units of Measure: New units_of_measure attribute for floor dimensions
catalyst_center:
sites:
floors:
- name: Floor1
parent_name: Global/Area/Building
height: 3.0
length: 30.0
width: 20.0
units_of_measure: meters # or 'feet'
  • LLDP Discovery Level: New lldp_level attribute for LLDP-based network discovery
catalyst_center:
inventory:
discovery:
- name: LLDP_Discovery
type: LLDP
lldp_level: 3
ip_address_list: "10.10.30.1"
global_credential_list:
- CLI_CRED
- SNMPV3_CRED
  • 802.11be (WiFi 7) Profiles: New dot11be_profiles for WiFi 7 support with OFDMA and MU-MIMO settings
catalyst_center:
wireless:
dot11be_profiles:
- name: "WiFi7_HighPerf"
ofdma_down_link: true
ofdma_up_link: true
mu_mimo_down_link: true
mu_mimo_up_link: true
ofdma_multi_ru: true
  • Associate WiFi 7 Profiles with SSIDs: Use dot11be_profile_name in wireless network profiles
catalyst_center:
network_profiles:
wireless:
- name: WirelessProfile_1
ssid_details:
- name: SSID_1
enable_fabric: true
dot11be_profile_name: "WiFi7_HighPerf"
  • New SSID Attributes: Additional wireless SSID configuration options
catalyst_center:
wireless:
ssids:
- name: SSID_1
auth_type: WPA3_PERSONAL
wlan_type: Enterprise
web_passthrough: true # Allows guests to bypass certain login requirements (Guest SSID)
wlan_band_select: true # Allowed only when band options contain at least 2.4 GHz and 5 GHz
  • SDA Transit Affinity IDs: New attributes for SDA transit load balancing
catalyst_center:
fabric:
border_devices:
- name: BORDER_1
border_types:
- LAYER_3
sda_transit: SDA_Transit_1
affinity_id_prime: 1
affinity_id_decider: 100
connected_to_internet: true
multicast_over_transit: true
New AttributeTypeDescription
affinity_id_primeint (0-2147483647)Primary affinity ID for transit load balancing
affinity_id_deciderint (0-2147483647)Decider affinity ID for transit selection
multicast_over_transitboolEnable multicast over SDA transit
  • device_discovery_validation: Validates device presence in Catalyst Center inventory during plan phase, improving error handling for devices not found
  • managed_sites validation: Ensures all sites specified in managed_sites variable exist in YAML configuration with precondition check
  • bulk_site_provisioning validation: Verifies site hierarchy format and existence in YAML configuration
  • use_bulk_api: Enable bulk API operations for areas, buildings, and floors using new map-based provider resources for faster execution

After completing all the changes below, your data model will be compatible with Catalyst Center Terraform module version 0.3.0. Make sure to:

  1. Handle the Terraform state changes for Provision Device resources (resource renamed)
  2. Update RF Profile schema for radio type C (6GHz) properties
  3. Update Discovery snmp_mode enum values
  4. Review new optional features available in this version

1. Provision Device Resource Rename (Breaking Change)

Section titled “1. Provision Device Resource Rename (Breaking Change)”

The Terraform resource catalystcenter_fabric_provision_device has been renamed to catalystcenter_provision_device. This is a breaking change that requires manual Terraform state management. When upgrading, Terraform will show errors like:

Error: no schema available for module.catalyst_center.catalystcenter_fabric_provision_device.provision_device["DEVICE_NAME"] while reading state

Execute the following command to find all provision device resources in your state:

Terminal window
terraform state list | grep catalystcenter_fabric_provision_device

Example output:

module.catalyst_center.catalystcenter_fabric_provision_device.provision_device["BR10"]
module.catalyst_center.catalystcenter_fabric_provision_device.provision_device["EDGE01"]
module.catalyst_center.catalystcenter_fabric_provision_device.provision_device["FIAB"]
module.catalyst_center.catalystcenter_fabric_provision_device.provision_device["Transit-CP-1"]
Step 2: Run Terraform Plan to Get Device and Site IDs
Section titled “Step 2: Run Terraform Plan to Get Device and Site IDs”

Run terraform plan to see the planned creation of new resources. Note the network_device_id and site_id values for each device:

Terminal window
terraform plan

Example output:

# module.catalyst_center.catalystcenter_provision_device.provision_device["BR10"] will be created
+ resource "catalystcenter_provision_device" "provision_device" {
+ id = (known after apply)
+ network_device_id = "e7869917-cf34-44ed-998a-e72ef9866eeb"
+ reprovision = false
+ site_id = "d3fa2852-cbca-4387-93c9-f1e47b6f7340"
}

Save the site_id and network_device_id values for each device for the import step.

Remove all identified resources from the Terraform state:

Terminal window
terraform state rm 'module.catalyst_center.catalystcenter_fabric_provision_device.provision_device["BR10"]'
terraform state rm 'module.catalyst_center.catalystcenter_fabric_provision_device.provision_device["EDGE01"]'
terraform state rm 'module.catalyst_center.catalystcenter_fabric_provision_device.provision_device["FIAB"]'
terraform state rm 'module.catalyst_center.catalystcenter_fabric_provision_device.provision_device["Transit-CP-1"]'

Import each device using the new resource name with the site_id and network_device_id values from Step 2:

Terminal window
terraform import 'module.catalyst_center.catalystcenter_provision_device.provision_device["BR10"]' "<site_id>,<network_device_id>"

Example:

Terminal window
terraform import 'module.catalyst_center.catalystcenter_provision_device.provision_device["BR10"]' "d3fa2852-cbca-4387-93c9-f1e47b6f7340,e7869917-cf34-44ed-998a-e72ef9866eeb"
terraform import 'module.catalyst_center.catalystcenter_provision_device.provision_device["EDGE01"]' "d3fa2852-cbca-4387-93c9-f1e47b6f7340,7ef492ca-b008-479a-9de4-7e40438c7d10"
terraform import 'module.catalyst_center.catalystcenter_provision_device.provision_device["FIAB"]' "944b4c7e-e9ed-4ec3-8d1f-e8d065a8c4b3,28a9f0f0-2834-4f12-8409-26d34a7f5bbb"
terraform import 'module.catalyst_center.catalystcenter_provision_device.provision_device["Transit-CP-1"]' "944b4c7e-e9ed-4ec3-8d1f-e8d065a8c4b3,e9272b6f-6ef0-487c-92dd-bce5c21747d7"

The import format is: "<site_id>,<network_device_id>"

Run terraform plan to verify the state is correct. You should see only in-place updates for other resources (like adding parent_id to areas/buildings/floors), not recreations of provision devices.

Terminal window
terraform plan
terraform apply

2. RF Profile Radio Type C (6GHz) Schema Changes

Section titled “2. RF Profile Radio Type C (6GHz) Schema Changes”

The RF Profile schema for radio type C (6GHz) has been significantly enhanced with new properties. The enable_radio_type_c attribute is now required (previously optional).

catalyst_center:
wireless:
rf_profiles:
- name: CUSTOM_RF_PROFILE
enable_radio_type_a: true
enable_radio_type_b: true
enable_radio_type_c: false # Was optional
enable_custom: true
catalyst_center:
wireless:
rf_profiles:
- name: CUSTOM_RF_PROFILE
enable_radio_type_a: true
enable_radio_type_b: true
enable_radio_type_c: true # Now required
# enable_custom removed in 0.3.0
radio_type_c_properties:
parent_profile: CUSTOM
enable_standard_power_service: true
psc_enforcing_enabled: true
discovery_frames_6ghz: Broadcast Probe Response
broadcast_probe_response_interval: 20
min_dbs_width: 20
max_dbs_width: 320
preamble_puncture: true
multi_bssid_properties:
dot11ax_parameters:
ofdma_down_link: true
ofdma_up_link: true
mu_mimo_up_link: true
mu_mimo_down_link: true
dot11be_parameters:
ofdma_down_link: true
ofdma_up_link: true
mu_mimo_up_link: true
mu_mimo_down_link: true
ofdma_multi_ru: true
target_wake_time: true
twt_broadcast_support: true
fra_properties_c:
client_reset_count: 5
client_utilization_threshold: 50
PropertyTypeDescription
enable_standard_power_serviceboolEnable standard power service
psc_enforcing_enabledboolEnable PSC enforcing
discovery_frames_6ghzenumNone, Broadcast Probe Response, FILS Discovery
broadcast_probe_response_intervalint (5-25)Broadcast probe response interval
min_dbs_widthenum20, 40, 80, 160, 320
max_dbs_widthenum20, 40, 80, 160, 320
preamble_punctureboolEnable preamble puncture
multi_bssid_propertiesobjectMulti-BSSID configuration
fra_properties_cobjectFRA properties for 6GHz

The snmp_mode enum values in discovery configurations have been updated to use different casing.

catalyst_center:
inventory:
discovery:
- name: Discovery_1
snmp_mode: AUTHPRIV
catalyst_center:
inventory:
discovery:
- name: Discovery_1
snmp_mode: AuthPriv
0.2.00.3.0
AUTHPRIVAuthPriv
AUTHNOPRIVAuthNoPriv
NOAUTHNOPRIVNoAuthNoPriv

4. New Features in 0.3.0 (No Migration Required)

Section titled “4. New Features in 0.3.0 (No Migration Required)”

The following new features are available in version 0.3.0. No migration is required for existing configurations, but you may want to leverage these new capabilities:

catalyst_center:
fabric:
fabric_sites:
- name: Global/Site1
multicast:
virtual_networks:
- name: Campus
ip_pool_name: CampusVN-IPPool
multicast_rps:
- name: RP1
rp_location: FABRIC
is_default_v4_rp: true
fabric_rps:
- Border-Device-1.example.com
- Border-Device-2.example.com
catalyst_center:
fabric:
extranet_policies:
- name: Shared_Services
provider_virtual_network: Shared
subscriber_virtual_networks:
- Campus
- Guest
fabric_sites:
- Global/Site1
  • Access Point Provisioning: Provision access points with RF profiles
catalyst_center:
inventory:
devices:
- name: AP_FLOOR1
type: AccessPoint
rf_profile: BASIC_PROFILE_1
state: PROVISION
site: Global/Site1/Building1/Floor1
  • Embedded Wireless Controller Node: New fabric role
catalyst_center:
inventory:
devices:
- name: SWITCH_EWLC
fabric_roles:
- EDGE_NODE
- EMBEDDED_WIRELESS_CONTROLLER_NODE
enable_wireless: true
enable_rolling_ap_upgrade: true
ap_reboot_percentage: 15
catalyst_center:
inventory:
devices:
- name: EDGE01
port_assignments:
- interfaces_range: GigabitEthernet1/0/1-1/0/10
interface_description: User Access Ports
connected_device_type: USER_DEVICE
data_vlan_name: Campus_Data
  • Group-Based Policy Enforcement: For EXTENDED_NODE pool types
catalyst_center:
fabric:
fabric_sites:
- name: Global/Site1
anycast_gateways:
- ip_pool_name: ExtendedNode-IPPool
pool_type: EXTENDED_NODE
group_based_policy_enforcement_enabled: true
  • TCP MSS Adjustment: Configure TCP MSS for anycast gateways
catalyst_center:
fabric:
fabric_sites:
- name: Global/Site1
anycast_gateways:
- ip_pool_name: Campus-IPPool
tcp_mss_adjustment: 1360
  • Reconfigure Flag: Apply pending fabric configuration events
catalyst_center:
fabric:
fabric_sites:
- name: Global/Site1
reconfigure: true
catalyst_center:
system_settings:
authentication_and_policy_servers:
ise:
ip_address: 10.1.1.100
shared_secret: SecretKey123
username: admin
password: AdminPass
fqdn: ise.example.com
retries: 3
timeout: 5
protocols:
radius:
authentication_port: 1812
accounting_port: 1813
  • use_bulk_api: Enable bulk API operations for faster execution
  • bulk_site_provisioning: Site path for bulk device provisioning. When set with use_bulk_api=true, provisions all devices from this site and all child sites in a single bulk operation. Example: ‘Global/Poland’ will provision all devices under Poland hierarchy.

After completing all the above changes, your data model will be compatible with Catalyst Center Terraform module version 0.2.0. Make sure to:

  1. Update all deploy_state references to redeploy_template with appropriate values
  2. Replace name with ip_pool_name in Anycast Gateway configurations
  3. Handle the Terraform state changes for network profile assignments
  4. Handle the Terraform state changes for Fabric L3 Handoff IP Transit resources
  5. Update LAN Automation attribute names and IP pool structure
  6. Change hostname to fqdn_name in device inventory
  7. Update discovery configuration attribute names

In version 0.1.1, template deployment was controlled by the deploy_state attribute with two options:

  • DEPLOY - never redeploy template
  • REDEPLOY - always redeploy template regardless of content changes

In version 0.2.0, this has been replaced with redeploy_template offering more granular control:

  • NEVER - never redeploy template
  • ALWAYS - always redeploy template regardless of content changes
  • ON_CHANGE - redeploy template only when there is a content template change
inventory:
devices:
- name: DEVICE_1
dayn_templates:
regular:
- name: TEMP1
deploy_state: DEPLOY
inventory:
devices:
- name: DEVICE_1
dayn_templates:
regular:
- name: TEMP1
redeploy_template: NEVER

The redeploy_template parameter can be configured at multiple levels with the following precedence (highest to lowest):

  1. Device-specific template level:
catalyst_center:
inventory:
devices:
- name: DEVICE_1
dayn_templates:
regular:
- name: TEMP1
redeploy_template: NEVER
  1. Device-wide template level:
catalyst_center:
inventory:
devices:
- name: DEVICE_1
dayn_templates:
redeploy_template: NEVER
regular:
- name: TEMP1
  1. Global template level:
catalyst_center:
templates:
projects:
- name: Project_DayN
description: Project_DayN
dayn_templates:
- name: TEMPLATE_1
redeploy_template: ALWAYS
  1. Default level:
defaults:
catalyst_center:
templates:
redeploy_template: NEVER
redeploy_templateTemplate Content ChangeTemplate Redeployed
ALWAYSYesYes
ALWAYSNoYes
NEVERYesNo
NEVERNoNo
ON_CHANGEYesYes
ON_CHANGENoNo

2. Anycast Gateway Attribute Name Replacement

Section titled “2. Anycast Gateway Attribute Name Replacement”

The name attribute in Anycast Gateway configuration has been replaced with ip_pool_name to better reflect its purpose as a reference to IP pool reservations.

catalyst_center:
fabric:
fabric_sites:
- name: Global/AREA
anycast_gateways:
- name: IPPool_1
catalyst_center:
fabric:
fabric_sites:
- name: Global/AREA
anycast_gateways:
- ip_pool_name: IPPool_1

3. Network Profile Assignment Resource Change

Section titled “3. Network Profile Assignment Resource Change”

The Terraform resource catalystcenter_associate_site_to_network_profile has been replaced with catalystcenter_network_profile_for_site_assignments. This requires manual Terraform state management.

Execute the following command to find all network profile association resources:

Terminal window
terraform state list | grep catalystcenter_associate_site_to_network_profile

Example output:

module.catalyst_center.catalystcenter_associate_site_to_network_profile.site_to_network_profile["Global/Poland/Krakow#_#Krk_switchingProfile"]
module.catalyst_center.catalystcenter_associate_site_to_network_profile.site_to_wireless_network_profile["Global/Poland/Krakow#_#C9800_KRK_Profile"]

Remove the identified resources from the Terraform state:

Terminal window
terraform state rm 'module.catalyst_center.catalystcenter_associate_site_to_network_profile.site_to_network_profile["Global/Poland/Krakow#_#Krk_switchingProfile"]'
terraform state rm 'module.catalyst_center.catalystcenter_associate_site_to_network_profile.site_to_wireless_network_profile["Global/Poland/Krakow#_#C9800_KRK_Profile"]'

Execute terraform apply to create the new network profile assignment resources:

Terminal window
terraform apply

Expected output:

# module.catalyst_center.catalystcenter_network_profile_for_sites_assignments.site_to_network_profile["Krk_switchingProfile"] will be created
+ resource "catalystcenter_network_profile_for_sites_assignments" "site_to_network_profile" {
+ id = (known after apply)
+ items = [
+ {
+ id = "a8fbb587-887c-42be-aaad-395abd685ebf"
},
]
+ network_profile_id = "076013fa-322f-48cb-b278-8bde227ebed7"
}
# module.catalyst_center.catalystcenter_network_profile_for_sites_assignments.site_to_wireless_network_profile["C9800_KRK_Profile"] will be created
+ resource "catalystcenter_network_profile_for_sites_assignments" "site_to_wireless_network_profile" {
+ id = (known after apply)
+ items = [
+ {
+ id = "a8fbb587-887c-42be-aaad-395abd685ebf"
},
]
+ network_profile_id = "5304325b-0aac-4be9-90ea-90c5654033f7"
}

4. Fabric L3 Handoff IP Transit Resource Change

Section titled “4. Fabric L3 Handoff IP Transit Resource Change”

The Terraform resource catalystcenter_fabric_l3_handoff_ip_transit has been replaced with catalystcenter_fabric_l3_handoff_ip_transits (note the plural form). This change consolidates multiple IP transit handoffs under a single resource per network device, requiring manual Terraform state management.

Execute the following command to find all L3 handoff IP transit resources:

Terminal window
terraform state list | grep catalystcenter_fabric_l3_handoff_ip_transit

Example output:

module.catalyst_center.catalystcenter_fabric_l3_handoff_ip_transit.l3_handoff_ip_transit["Campus/GigabitEthernet1/0/3/BGP65002/BR10.cisco.eu"]

Run command to get the network_device_id and fabric_id values needed for the import step:

Terminal window
terraform state show 'module.catalyst_center.catalystcenter_fabric_l3_handoff_ip_transit.l3_handoff_ip_transit["Campus/GigabitEthernet1/0/3/BGP65002/BR10.cisco.eu"]'

Example output:

# module.catalyst_center.catalystcenter_fabric_l3_handoff_ip_transit.l3_handoff_ip_transit["Campus/GigabitEthernet1/0/3/BGP65002/BR10.cisco.eu"]:
resource "catalystcenter_fabric_l3_handoff_ip_transit" "l3_handoff_ip_transit" {
fabric_id = "aca2f40c-40a2-4bc2-9690-f59d758fd600"
id = "fbe1ad74-7df9-46a2-8746-8875f9cbbed4"
interface_name = "GigabitEthernet1/0/3"
local_ip_address = "172.16.100.1/24"
network_device_id = "8fbd4920-25d2-4b73-be25-2421c61e9605"
remote_ip_address = "172.16.100.2/24"
transit_network_id = "fc09acc4-dd6d-48f0-a446-6afe7d0867f4"
virtual_network_name = "Campus"
vlan_id = 100
}

Save the network_device_id and fabric_id values for the import step:

  • network_device_id: 8fbd4920-25d2-4b73-be25-2421c61e9605
  • fabric_id: aca2f40c-40a2-4bc2-9690-f59d758fd600

Remove the identified resources from the Terraform state:

Terminal window
terraform state rm 'module.catalyst_center.catalystcenter_fabric_l3_handoff_ip_transit.l3_handoff_ip_transit["Campus/GigabitEthernet1/0/3/BGP65002/BR10.cisco.eu"]'

Import the new consolidated resource using the network device ID and fabric ID copied from Step 2:

Terminal window
terraform import 'module.catalyst_center.catalystcenter_fabric_l3_handoff_ip_transits.l3_handoff_ip_transits["BR10.cisco.eu"]' "8fbd4920-25d2-4b73-be25-2421c61e9605,aca2f40c-40a2-4bc2-9690-f59d758fd600"

The import format is: "<network_device_id>,<fabric_id>"

Several attributes in the LAN Automation data model have been updated for consistency and clarity.

0.1.10.2.0
peer_device_management_ip_addresssecondary_device_management_ip_address
redistribute_isis_to_bgpadvertise_lan_automation_routes_into_bgp

(0.2.0) Data Model:

catalyst_center:
lan_automation:
- name: Automate_Edge_Switches
secondary_device_management_ip_address: 198.18.130.11
advertise_lan_automation_routes_into_bgp: true

The ip_pools structure has been modified to use name and role instead of ip_pool_name and ip_pool_role.

catalyst_center:
lan_automation:
- name: Automate_Edge_Switches
ip_pools:
- ip_pool_name: MainPool
ip_pool_role: MAIN_POOL
catalyst_center:
lan_automation:
- name: Automate_Edge_Switches
ip_pools:
- name: MainPool
role: PRINCIPAL_IP_ADDRESS_POOL

Additionally the ip_pool_role values have been updated as follows:

0.1.10.2.0
MAIN_POOLPRINCIPAL_IP_ADDRESS_POOL
PHYSICAL_LINK_POOLLINK_OVERLAPPING_IP_POOL

The hostname attribute in device inventory has been replaced with fqdn_name to support fully qualified domain names.

catalyst_center:
inventory:
devices:
- name: DEVICE_1
hostname: DEVICE_1
catalyst_center:
inventory:
devices:
- name: DEVICE_1
fqdn_name: DEVICE_1.example.com

The attribute preferred_ip_method has been renamed to preferred_mgmt_ip_method in discovery configurations:

catalyst_center:
inventory:
discovery:
- name: Discovery_1
preferred_ip_method: UseLoopBack
catalyst_center:
inventory:
discovery:
- name: Discovery_1
preferred_mgmt_ip_method: UseLoopBack

After completing all the above changes, your data model will be compatible with Catalyst Center Terraform module version 0.1.1. Make sure to:

  1. Update authentication template number_of_hosts enum value from ‘Limited’ to ‘Single’
  2. Add L3 Virtual Networks structure at fabric level if using global L3 VNs
  3. Update device inventory managed AP locations structure
  4. Add new discovery configuration attributes if using advanced SNMP settings
  5. Remove deprecated RF profile attributes
  6. Update anycast gateway configurations with new optional attributes
  7. Remove deprecated border device attributes
  8. Handle the Terraform state changes for fabric provision device resource consolidation

1. Authentication Template Enum Value Update

Section titled “1. Authentication Template Enum Value Update”

The number_of_hosts attribute enum value has been updated for consistency.

catalyst_center:
authentication_templates:
- name: AUTH_TEMPLATE_1
number_of_hosts: Limited
catalyst_center:
authentication_templates:
- name: AUTH_TEMPLATE_1
number_of_hosts: Single

2. Fabric L3 Virtual Networks Structure Enhancement

Section titled “2. Fabric L3 Virtual Networks Structure Enhancement”

L3 Virtual Networks can now be defined at the global fabric level in addition to fabric site level, enabling better organization and reuse.

catalyst_center:
fabric:
l3_virtual_networks:
- name: GLOBAL_L3_VN_1
- name: GLOBAL_L3_VN_2
fabric_sites:
- name: Global/AREA
l3_virtual_networks:
- GLOBAL_L3_VN_1
- GLOBAL_L3_VN_2

3. Device Inventory Managed AP Locations Enhancement

Section titled “3. Device Inventory Managed AP Locations Enhancement”

The managed_ap_locations attribute has been split into primary_managed_ap_locations and secondary_managed_ap_locations for better AP management control.

catalyst_center:
inventory:
devices:
- name: WLC_DEVICE_1
managed_ap_locations:
- Global/Area1/Building1/Floor1
- Global/Area1/Building1/Floor2
catalyst_center:
inventory:
devices:
- name: WLC_DEVICE_1
primary_managed_ap_locations:
- Global/Area1/Building1/Floor1
secondary_managed_ap_locations:
- Global/Area1/Building1/Floor2

New SNMP-related attributes have been added to discovery configurations for enhanced device discovery capabilities.

catalyst_center:
inventory:
discovery:
- name: DISCOVERY_1
cdp_level: 16
enable_password_list:
- enable_password_1
ip_filter_list:
- 192.168.1.0/24
netconf_port: "830"
retry: 3
user_name_list:
- admin
snmp_version: v3
snmp_user_name: snmp_user
snmp_rw_community_desc: "RW Community"
snmp_ro_community_desc: "RO Community"
snmp_ro_community: public
snmp_rw_community: private
snmp_priv_protocol: AES128
snmp_priv_passphrase: priv_pass
snmp_mode: AUTHPRIV
snmp_auth_protocol: SHA
snmp_auth_passphrase: auth_pass

Deprecated RF profile attributes channel_width and enable_brown_field have been removed from the schema.

Remove these attributes from your RF profile configurations:

  • channel_width
  • enable_brown_field

New optional attributes have been added to anycast gateway configurations for enhanced functionality.

catalyst_center:
fabric:
fabric_sites:
- name: Global/AREA
anycast_gateways:
- name: ANYCAST_GW_1
intra_subnet_routing_enabled: true
multiple_ip_to_mac_addresses: false
supplicant_based_extended_node_onboarding: true

The deprecated external_domain_routing_protocol_name attribute has been removed from border device configurations.

Remove the external_domain_routing_protocol_name attribute from your border device configurations.

8. Fabric Provision Device Resource Consolidation

Section titled “8. Fabric Provision Device Resource Consolidation”

Multiple device-specific provision resources have been consolidated into a single catalystcenter_fabric_provision_device resource. The following resources are now unified:

  • catalystcenter_fabric_provision_device.border_device
  • catalystcenter_fabric_provision_device.edge_device
  • catalystcenter_fabric_provision_device.non_fabric_device

All devices now use the single catalystcenter_fabric_provision_device.provision_device resource.

Execute the following command to find all fabric provision device resources:

Terminal window
terraform state list | grep catalystcenter_fabric_provision_device

Example output:

module.catalyst_center.catalystcenter_fabric_provision_device.border_device["BR10.cisco.eu"]
module.catalyst_center.catalystcenter_fabric_provision_device.edge_device["EDGE01.cisco.eu"]
module.catalyst_center.catalystcenter_fabric_provision_device.non_fabric_device["PNP_DEVICE.cisco.eu"]

Remove the identified resources from the Terraform state:

Terminal window
terraform state rm 'module.catalyst_center.catalystcenter_fabric_provision_device.border_device["BR10.cisco.eu"]'
terraform state rm 'module.catalyst_center.catalystcenter_fabric_provision_device.edge_device["EDGE01.cisco.eu"]'
terraform state rm 'module.catalyst_center.catalystcenter_fabric_provision_device.non_fabric_device["PNP_DEVICE.cisco.eu"]'

Import the new consolidated resources using the site ID and network device ID:

Terminal window
terraform import 'module.catalyst_center.catalystcenter_fabric_provision_device.provision_device["BR10.cisco.eu"]' "662ed0df-861f-4753-9008-df85fe9a584e,e7869917-cf34-44ed-998a-e72ef9866eeb"
terraform import 'module.catalyst_center.catalystcenter_fabric_provision_device.provision_device["EDGE01.cisco.eu"]' "662ed0df-861f-4753-9008-df85fe9a584e,7ef492ca-b008-479a-9de4-7e40438c7d10"
terraform import 'module.catalyst_center.catalystcenter_fabric_provision_device.provision_device["PNP_DEVICE.cisco.eu"]' "c2c12299-f7cc-4058-9d99-ffe57de50062,ce8d33e4-6d7b-4f2a-8ed6-0d95603305be"

The import format is: "<site_id>,<network_device_id>"