Lesson 1 of 6

Catalyst Center API Overview

Objective

In this lesson you will learn the fundamentals of the Catalyst Center Intent API as used by an Infrastructure-as-Code workflow. We focus on authentication, API versioning, and the asynchronous task management model that the Intent API uses. This matters in production because automation systems (Terraform, CI/CD pipelines) must authenticate reliably, call the correct API version, and track long-running tasks to ensure configuration changes complete successfully — for example when deploying an SDA fabric across multiple sites.

Topology & Device Table

ASCII topology (management-plane view — IPs used for management/automation traffic):

       [Terraform Host]
       Interface: eth0
       IP: 192.0.2.20/24
             |
             | 192.0.2.20
             |
    -------------------------
    |                       |

192.0.2.10/24 192.0.2.11/24 [Catalyst Center] [ISE Server] Interface: mgmt0 Interface: mgmt0 IP: 192.0.2.10 IP: 192.0.2.11 (Intent API) (ISE API)

Device Table

DeviceInterfaceIP AddressSubnet MaskRole
Terraform Hosteth0192.0.2.20255.255.255.0Automation host (Terraform)
Catalyst Centermgmt0192.0.2.10255.255.255.0Intent API / Controller
ISE Servermgmt0192.0.2.11255.255.255.0Policy / SGT / ISE API

Tip: In production, management addresses are often on a dedicated management network (out-of-band). Here we use the 192.0.2.0/24 TEST-NET-1 addresses for lab clarity.


Key Concepts (theory + behavior you must understand)

  • Authentication & Sessions: The Intent API requires authenticated sessions or API tokens. Automation platforms must store credentials securely and refresh tokens before expiry. In production, this is usually done with a secrets manager; in a lab we use environment variables or a local credentials file.

  • API Versioning: Catalyst Center Intent API evolves. Automation must specify the correct API version (or use an explicit version variable) so templates and data produce compatible payloads. If the API version changes, automation should detect and adapt rather than hard-fail.

  • Asynchronous Task Model: Many Intent API operations are asynchronous — the API returns a task identifier immediately while the controller performs the long-running work. Automation must poll the task endpoint and interpret task states (PENDING -> RUNNING -> SUCCESS/FAILURE) to decide whether to continue or rollback.

  • Idempotence & State Calculation: IaC tools compute a desired state and send minimal diffs. The Intent API may accept full objects or patch-like updates; understanding whether an operation is create/update/delete is essential to prevent accidental resource removal.

  • Real-world flow: When you trigger a fabric deployment (for example creating transit networks with BGP ASNs 65010/65020 or adding an anycast gateway VLAN 2800), Catalyst Center will enqueue tasks. Your automation should record task IDs, poll until completion, and log results for audit and troubleshooting.


Step-by-step configuration

Each step below includes commands, why they matter, and verification commands with expected output.

Step 1: Prepare defaults.yaml (centralized defaults)

What we are doing: Create a defaults file that separates data from templates. This file sets sane default flags (security and networking) and default transit types used by the Catalyst Center automation templates. Centralizing defaults reduces human error and enables consistent suffixes or boolean flags across the deployment.

# create defaults.yaml
cat > data/defaults.yaml << 'EOF'
defaults:
  catalyst_center:
    fabric:
      fabric_sites:
        anycast_gateways:
          critical_pool: false
          intra_subnet_routing_enabled: false
        transits:
          routing_protocol_name: BGP
          type: IP_BASED
  transits:
    type: IP_BASED
EOF

What just happened: The file data/defaults.yaml was created and contains default behaviors: BGP as the routing protocol for transits and IP-based transit networks. These defaults will be merged with site-specific data during templating so that individual site definitions do not need to repeat common flags.

Real-world note: Using a single defaults file avoids inconsistent flags across many sites (for example accidentally enabling layer2_flooding in one site).

Verify:

# show defaults.yaml
cat data/defaults.yaml

Expected output:

defaults:
  catalyst_center:
    fabric:
      fabric_sites:
        anycast_gateways:
          critical_pool: false
          intra_subnet_routing_enabled: false
        transits:
          routing_protocol_name: BGP
          type: IP_BASED
  transits:
    type: IP_BASED

Step 2: Define transit networks (data model)

What we are doing: Create a variable file that defines the transit networks and their ASNs (these values drive Catalyst Center objects). This is the "data" that the IaC templates will apply. Using transits as data keeps the code generic and allows easy changes.

# create data/transits.yaml
cat > data/transits.yaml << 'EOF'
transit:
  Transit1:
    name: CORP
    type: IP_BASED_TRANSIT
    asn: 65010
  Transit2:
    name: Guest
    type: IP_BASED_TRANSIT
    asn: 65020
EOF

What just happened: We declared two transit networks: CORP (ASN 65010) and Guest (ASN 65020). These map directly to Catalyst Center transit network objects. The IaC framework will iterate over these entries to create per-transit resources.

Real-world note: In production, separate ASNs isolate control-plane domains for corporate vs guest routing; the Catalyst Center will use these ASNs when provisioning BGP to edge devices.

Verify:

# view transits data
cat data/transits.yaml

Expected output:

transit:
  Transit1:
    name: CORP
    type: IP_BASED_TRANSIT
    asn: 65010
  Transit2:
    name: Guest
    type: IP_BASED_TRANSIT
    asn: 65020

Step 3: Configure Terraform module entry (glue between data & templates)

What we are doing: Declare the module block that will consume data directories and templates to drive Catalyst Center. This instructs Terraform where to find data and where to write default values. We do not hardcode a remote source URL here in the lesson; instead we show the module configuration attributes used by the framework.

# example module declaration (file: main.tf)
cat > main.tf << 'EOF'
module "catalyst_center" {
  source = "<module_source>"

  yaml_directories = ["data/"]
  templates_directories = ["data/templates/"]
  write_default_values_file = "defaults.yaml"
}
EOF

What just happened: The Terraform module block declares that the automation module will read YAML data from data/ and templates from data/templates/. write_default_values_file indicates that the workflow can emit a consolidated defaults file. The module source is placeholdered here because in a real environment you point to the tested module repository that implements Catalyst Center interactions.

Real-world note: Pointing to a maintained module ensures you don't need to understand low-level Catalyst Center object models — the module handles translation from YAML to API payloads.

Verify:

# show module file
cat main.tf

Expected output:

module "catalyst_center" {
  source = "<module_source>"

  yaml_directories = ["data/"]
  templates_directories = ["data/templates/"]
  write_default_values_file = "defaults.yaml"
}


<div class="topology-diagram">
<img src="data:image/svg+xml;base64,PD9wbGFudHVtbCAxLjIwMjYuMT8+PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiBjb250ZW50U3R5bGVUeXBlPSJ0ZXh0L2NzcyIgZGF0YS1kaWFncmFtLXR5cGU9Ik5XRElBRyIgaGVpZ2h0PSIxNTRweCIgcHJlc2VydmVBc3BlY3RSYXRpbz0ibm9uZSIgc3R5bGU9IndpZHRoOjU1MnB4O2hlaWdodDoxNTRweDtiYWNrZ3JvdW5kOiNGRkZGRkY7IiB2ZXJzaW9uPSIxLjEiIHZpZXdCb3g9IjAgMCA1NTIgMTU0IiB3aWR0aD0iNTUycHgiIHpvb21BbmRQYW49Im1hZ25pZnkiPjxkZWZzLz48Zz48dGV4dCBmaWxsPSIjMDAwMDAwIiBmb250LWZhbWlseT0ic2Fucy1zZXJpZiIgZm9udC1zaXplPSIxMiIgbGVuZ3RoQWRqdXN0PSJzcGFjaW5nIiB0ZXh0TGVuZ3RoPSIxMzUuMTUyMyIgeD0iNSIgeT0iMTYuMTM4NyI+TWFuYWdlbWVudF9OZXR3b3JrPC90ZXh0Pjx0ZXh0IGZpbGw9IiMwMDAwMDAiIGZvbnQtZmFtaWx5PSJzYW5zLXNlcmlmIiBmb250LXNpemU9IjEyIiBsZW5ndGhBZGp1c3Q9InNwYWNpbmciIHRleHRMZW5ndGg9Ijg0LjE5OTIiIHg9IjU1Ljk1MzEiIHk9IjMwLjEwNzQiPjEwLjEwLjEwLjAvMjQ8L3RleHQ+PHJlY3QgZmlsbD0iI0UyRTJGMCIgaGVpZ2h0PSI1IiBzdHlsZT0ic3Ryb2tlOiMxODE4MTg7c3Ryb2tlLXdpZHRoOjE7IiB3aWR0aD0iMzk4Ljg5MjEiIHg9IjE0NS4xNTIzIiB5PSIxNi40Njg4Ii8+PHBhdGggZD0iTTIzMC41NzMyLDIxLjQ2ODggTDIzMC41NzMyLDY0LjI3MzQiIGZpbGw9Im5vbmUiIHN0eWxlPSJzdHJva2U6IzE4MTgxODtzdHJva2Utd2lkdGg6MTsiLz48dGV4dCBmaWxsPSIjMDAwMDAwIiBmb250LWZhbWlseT0ic2Fucy1zZXJpZiIgZm9udC1zaXplPSIxMSIgbGVuZ3RoQWRqdXN0PSJzcGFjaW5nIiB0ZXh0TGVuZ3RoPSI2Ni40NzgiIHg9IjE5Ny4zMzQyIiB5PSIzNy43NzY5Ij4xMC4xMC4xMC4xMDwvdGV4dD48dGV4dCBmaWxsPSIjMDAwMDAwIiBmb250LWZhbWlseT0ic2Fucy1zZXJpZiIgZm9udC1zaXplPSIxMSIgbGVuZ3RoQWRqdXN0PSJzcGFjaW5nIiB0ZXh0TGVuZ3RoPSIyNS4wNTA4IiB4PSIxOTcuMzM0MiIgeT0iNTAuNTgxNSI+ZXRoMDwvdGV4dD48cGF0aCBkPSJNMzg2Ljc4MDMsMjEuNDY4OCBMMzg2Ljc4MDMsNjQuMjczNCIgZmlsbD0ibm9uZSIgc3R5bGU9InN0cm9rZTojMTgxODE4O3N0cm9rZS13aWR0aDoxOyIvPjx0ZXh0IGZpbGw9IiMwMDAwMDAiIGZvbnQtZmFtaWx5PSJzYW5zLXNlcmlmIiBmb250LXNpemU9IjExIiBsZW5ndGhBZGp1c3Q9InNwYWNpbmciIHRleHRMZW5ndGg9IjY2LjQ3OCIgeD0iMzUzLjU0MTMiIHk9IjM3Ljc3NjkiPjEwLjEwLjEwLjIwPC90ZXh0Pjx0ZXh0IGZpbGw9IiMwMDAwMDAiIGZvbnQtZmFtaWx5PSJzYW5zLXNlcmlmIiBmb250LXNpemU9IjExIiBsZW5ndGhBZGp1c3Q9InNwYWNpbmciIHRleHRMZW5ndGg9IjM5LjcyNDYiIHg9IjM1My41NDEzIiB5PSI1MC41ODE1Ij5tZ210MDwvdGV4dD48cGF0aCBkPSJNNTAyLjgwNTQsMjEuNDY4OCBMNTAyLjgwNTQsNjQuMjczNCIgZmlsbD0ibm9uZSIgc3R5bGU9InN0cm9rZTojMTgxODE4O3N0cm9rZS13aWR0aDoxOyIvPjx0ZXh0IGZpbGw9IiMwMDAwMDAiIGZvbnQtZmFtaWx5PSJzYW5zLXNlcmlmIiBmb250LXNpemU9IjExIiBsZW5ndGhBZGp1c3Q9InNwYWNpbmciIHRleHRMZW5ndGg9IjY2LjQ3OCIgeD0iNDY5LjU2NjQiIHk9IjM3Ljc3NjkiPjEwLjEwLjEwLjMwPC90ZXh0Pjx0ZXh0IGZpbGw9IiMwMDAwMDAiIGZvbnQtZmFtaWx5PSJzYW5zLXNlcmlmIiBmb250LXNpemU9IjExIiBsZW5ndGhBZGp1c3Q9InNwYWNpbmciIHRleHRMZW5ndGg9IjM5LjcyNDYiIHg9IjQ2OS41NjY0IiB5PSI1MC41ODE1Ij5tZ210MDwvdGV4dD48cmVjdCBmaWxsPSIjRjFGMUYxIiBoZWlnaHQ9IjMzLjk2ODgiIHN0eWxlPSJzdHJva2U6IzE4MTgxODtzdHJva2Utd2lkdGg6MC41OyIgd2lkdGg9IjEzNi44NDE4IiB4PSIxNjAuMTUyMyIgeT0iNjQuMjczNCIvPjx0ZXh0IGZpbGw9IiMwMDAwMDAiIGZvbnQtZmFtaWx5PSJzYW5zLXNlcmlmIiBmb250LXNpemU9IjEyIiBsZW5ndGhBZGp1c3Q9InNwYWNpbmciIHRleHRMZW5ndGg9IjExNi44NDE4IiB4PSIxNzAuMTUyMyIgeT0iODUuNDEyMSI+QWRtaW5fV29ya3N0YXRpb248L3RleHQ+PHJlY3QgZmlsbD0iI0YxRjFGMSIgaGVpZ2h0PSIzMy45Njg4IiBzdHlsZT0ic3Ryb2tlOiMxODE4MTg7c3Ryb2tlLXdpZHRoOjAuNTsiIHdpZHRoPSIxMTUuNTcyMyIgeD0iMzI2Ljk5NDEiIHk9IjY0LjI3MzQiLz48dGV4dCBmaWxsPSIjMDAwMDAwIiBmb250LWZhbWlseT0ic2Fucy1zZXJpZiIgZm9udC1zaXplPSIxMiIgbGVuZ3RoQWRqdXN0PSJzcGFjaW5nIiB0ZXh0TGVuZ3RoPSI5NS41NzIzIiB4PSIzMzYuOTk0MSIgeT0iODUuNDEyMSI+Q2F0YWx5c3RfQ2VudGVyPC90ZXh0PjxyZWN0IGZpbGw9IiNGMUYxRjEiIGhlaWdodD0iMzMuOTY4OCIgc3R5bGU9InN0cm9rZTojMTgxODE4O3N0cm9rZS13aWR0aDowLjU7IiB3aWR0aD0iMzguNzM4MyIgeD0iNDgxLjQzNjMiIHk9IjY0LjI3MzQiLz48dGV4dCBmaWxsPSIjMDAwMDAwIiBmb250LWZhbWlseT0ic2Fucy1zZXJpZiIgZm9udC1zaXplPSIxMiIgbGVuZ3RoQWRqdXN0PSJzcGFjaW5nIiB0ZXh0TGVuZ3RoPSIxOC43MzgzIiB4PSI0OTEuNDM2MyIgeT0iODUuNDEyMSI+SVNFPC90ZXh0Pjw/cGxhbnR1bWwtc3JjIG9vakZvS25DTHdaY0tiMzhJb3FmcG9fQUxsMURwNGpDSnlyRHBJazl6dUNBV1FHTDUxOUpLZWZJWXVpTFIxTUszMHRxOENYMHR5WDQySXBoYzlBUmNIU1YzYkhVTjk5T2FmY1ZmbjJESk9FWEdHcEdlV20zZkxYaGkzUmRuOUI0ZENoWWFkWGRlOE1mSFRXcjZHNHJ2UVJkYmkxclVHUXhPYlRmWkFnb2JnaU0wMDAwPz48L2c+PC9zdmc+" alt="Network Topology Diagram" style="max-width:100%;height:auto;background:#fff;padding:16px;border:1px solid #e5e7eb;border-radius:8px;" />
</div>

cisco
# create credentials file (terraform.tfvars)
cat > terraform.tfvars << 'EOF'
cc_server = "https://192.0.2.10"
cc_username = "admin"
cc_password = "Lab@123"
cc_org = "NHPREP"
cc_api_version = "v1"
EOF

What just happened: We set the management URL, credentials (username/password per lesson rules), organization, and an explicit API version. The module will use these values to authenticate to the Intent API and set the API version in request URIs or payload translation logic.

Real-world note: Never commit plaintext credentials to Git. In production store secrets in a vault and reference them via secure providers.

Verify:

# show terraform variables
cat terraform.tfvars

Expected output:

cc_server = "https://192.0.2.10"
cc_username = "admin"
cc_password = "Lab@123"
cc_org = "NHPREP"
cc_api_version = "v1"


<div class="topology-diagram">
<img src="data:image/svg+xml;base64,PD9wbGFudHVtbCAxLjIwMjYuMT8+PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiBjb250ZW50U3R5bGVUeXBlPSJ0ZXh0L2NzcyIgZGF0YS1kaWFncmFtLXR5cGU9Ik5XRElBRyIgaGVpZ2h0PSIxNTRweCIgcHJlc2VydmVBc3BlY3RSYXRpbz0ibm9uZSIgc3R5bGU9IndpZHRoOjU1MnB4O2hlaWdodDoxNTRweDtiYWNrZ3JvdW5kOiNGRkZGRkY7IiB2ZXJzaW9uPSIxLjEiIHZpZXdCb3g9IjAgMCA1NTIgMTU0IiB3aWR0aD0iNTUycHgiIHpvb21BbmRQYW49Im1hZ25pZnkiPjxkZWZzLz48Zz48dGV4dCBmaWxsPSIjMDAwMDAwIiBmb250LWZhbWlseT0ic2Fucy1zZXJpZiIgZm9udC1zaXplPSIxMiIgbGVuZ3RoQWRqdXN0PSJzcGFjaW5nIiB0ZXh0TGVuZ3RoPSIxMzUuMTUyMyIgeD0iNSIgeT0iMTYuMTM4NyI+TWFuYWdlbWVudF9OZXR3b3JrPC90ZXh0Pjx0ZXh0IGZpbGw9IiMwMDAwMDAiIGZvbnQtZmFtaWx5PSJzYW5zLXNlcmlmIiBmb250LXNpemU9IjEyIiBsZW5ndGhBZGp1c3Q9InNwYWNpbmciIHRleHRMZW5ndGg9Ijg0LjE5OTIiIHg9IjU1Ljk1MzEiIHk9IjMwLjEwNzQiPjEwLjEwLjEwLjAvMjQ8L3RleHQ+PHJlY3QgZmlsbD0iI0UyRTJGMCIgaGVpZ2h0PSI1IiBzdHlsZT0ic3Ryb2tlOiMxODE4MTg7c3Ryb2tlLXdpZHRoOjE7IiB3aWR0aD0iMzk4Ljg5MjEiIHg9IjE0NS4xNTIzIiB5PSIxNi40Njg4Ii8+PHBhdGggZD0iTTIzMC41NzMyLDIxLjQ2ODggTDIzMC41NzMyLDY0LjI3MzQiIGZpbGw9Im5vbmUiIHN0eWxlPSJzdHJva2U6IzE4MTgxODtzdHJva2Utd2lkdGg6MTsiLz48dGV4dCBmaWxsPSIjMDAwMDAwIiBmb250LWZhbWlseT0ic2Fucy1zZXJpZiIgZm9udC1zaXplPSIxMSIgbGVuZ3RoQWRqdXN0PSJzcGFjaW5nIiB0ZXh0TGVuZ3RoPSI2Ni40NzgiIHg9IjE5Ny4zMzQyIiB5PSIzNy43NzY5Ij4xMC4xMC4xMC4xMDwvdGV4dD48dGV4dCBmaWxsPSIjMDAwMDAwIiBmb250LWZhbWlseT0ic2Fucy1zZXJpZiIgZm9udC1zaXplPSIxMSIgbGVuZ3RoQWRqdXN0PSJzcGFjaW5nIiB0ZXh0TGVuZ3RoPSIyNS4wNTA4IiB4PSIxOTcuMzM0MiIgeT0iNTAuNTgxNSI+ZXRoMDwvdGV4dD48cGF0aCBkPSJNMzg2Ljc4MDMsMjEuNDY4OCBMMzg2Ljc4MDMsNjQuMjczNCIgZmlsbD0ibm9uZSIgc3R5bGU9InN0cm9rZTojMTgxODE4O3N0cm9rZS13aWR0aDoxOyIvPjx0ZXh0IGZpbGw9IiMwMDAwMDAiIGZvbnQtZmFtaWx5PSJzYW5zLXNlcmlmIiBmb250LXNpemU9IjExIiBsZW5ndGhBZGp1c3Q9InNwYWNpbmciIHRleHRMZW5ndGg9IjY2LjQ3OCIgeD0iMzUzLjU0MTMiIHk9IjM3Ljc3NjkiPjEwLjEwLjEwLjIwPC90ZXh0Pjx0ZXh0IGZpbGw9IiMwMDAwMDAiIGZvbnQtZmFtaWx5PSJzYW5zLXNlcmlmIiBmb250LXNpemU9IjExIiBsZW5ndGhBZGp1c3Q9InNwYWNpbmciIHRleHRMZW5ndGg9IjM5LjcyNDYiIHg9IjM1My41NDEzIiB5PSI1MC41ODE1Ij5tZ210MDwvdGV4dD48cGF0aCBkPSJNNTAyLjgwNTQsMjEuNDY4OCBMNTAyLjgwNTQsNjQuMjczNCIgZmlsbD0ibm9uZSIgc3R5bGU9InN0cm9rZTojMTgxODE4O3N0cm9rZS13aWR0aDoxOyIvPjx0ZXh0IGZpbGw9IiMwMDAwMDAiIGZvbnQtZmFtaWx5PSJzYW5zLXNlcmlmIiBmb250LXNpemU9IjExIiBsZW5ndGhBZGp1c3Q9InNwYWNpbmciIHRleHRMZW5ndGg9IjY2LjQ3OCIgeD0iNDY5LjU2NjQiIHk9IjM3Ljc3NjkiPjEwLjEwLjEwLjMwPC90ZXh0Pjx0ZXh0IGZpbGw9IiMwMDAwMDAiIGZvbnQtZmFtaWx5PSJzYW5zLXNlcmlmIiBmb250LXNpemU9IjExIiBsZW5ndGhBZGp1c3Q9InNwYWNpbmciIHRleHRMZW5ndGg9IjM5LjcyNDYiIHg9IjQ2OS41NjY0IiB5PSI1MC41ODE1Ij5tZ210MDwvdGV4dD48cmVjdCBmaWxsPSIjRjFGMUYxIiBoZWlnaHQ9IjMzLjk2ODgiIHN0eWxlPSJzdHJva2U6IzE4MTgxODtzdHJva2Utd2lkdGg6MC41OyIgd2lkdGg9IjEzNi44NDE4IiB4PSIxNjAuMTUyMyIgeT0iNjQuMjczNCIvPjx0ZXh0IGZpbGw9IiMwMDAwMDAiIGZvbnQtZmFtaWx5PSJzYW5zLXNlcmlmIiBmb250LXNpemU9IjEyIiBsZW5ndGhBZGp1c3Q9InNwYWNpbmciIHRleHRMZW5ndGg9IjExNi44NDE4IiB4PSIxNzAuMTUyMyIgeT0iODUuNDEyMSI+QWRtaW5fV29ya3N0YXRpb248L3RleHQ+PHJlY3QgZmlsbD0iI0YxRjFGMSIgaGVpZ2h0PSIzMy45Njg4IiBzdHlsZT0ic3Ryb2tlOiMxODE4MTg7c3Ryb2tlLXdpZHRoOjAuNTsiIHdpZHRoPSIxMTUuNTcyMyIgeD0iMzI2Ljk5NDEiIHk9IjY0LjI3MzQiLz48dGV4dCBmaWxsPSIjMDAwMDAwIiBmb250LWZhbWlseT0ic2Fucy1zZXJpZiIgZm9udC1zaXplPSIxMiIgbGVuZ3RoQWRqdXN0PSJzcGFjaW5nIiB0ZXh0TGVuZ3RoPSI5NS41NzIzIiB4PSIzMzYuOTk0MSIgeT0iODUuNDEyMSI+Q2F0YWx5c3RfQ2VudGVyPC90ZXh0PjxyZWN0IGZpbGw9IiNGMUYxRjEiIGhlaWdodD0iMzMuOTY4OCIgc3R5bGU9InN0cm9rZTojMTgxODE4O3N0cm9rZS13aWR0aDowLjU7IiB3aWR0aD0iMzguNzM4MyIgeD0iNDgxLjQzNjMiIHk9IjY0LjI3MzQiLz48dGV4dCBmaWxsPSIjMDAwMDAwIiBmb250LWZhbWlseT0ic2Fucy1zZXJpZiIgZm9udC1zaXplPSIxMiIgbGVuZ3RoQWRqdXN0PSJzcGFjaW5nIiB0ZXh0TGVuZ3RoPSIxOC43MzgzIiB4PSI0OTEuNDM2MyIgeT0iODUuNDEyMSI+SVNFPC90ZXh0Pjw/cGxhbnR1bWwtc3JjIG9vakZvS25DTHdaY0tiMzhJb3FmcG9fQUxsMURwNGpDSnlyRHBJazl6dUNBV1FHTDUxOUpLZWZJWXVpTFIxTUszMHRxOENYMHR5WDQySXBoYzlBUmNIU1YzYkhVTjk5T2FmY1ZmbjJESk9FWEdHcEdlV20zZkxYaGkzUmRuOUI0ZENoWWFkWGRlOE1mSFRXcjZHNHJ2UVJkYmkxclVHUXhPYlRmWkFnb2JnaU0wMDAwPz48L2c+PC9zdmc+" alt="Network Topology Diagram" style="max-width:100%;height:auto;background:#fff;padding:16px;border:1px solid #e5e7eb;border-radius:8px;" />
</div>

cisco
# initialize terraform
terraform init

# create an execution plan
terraform plan -out plan.tfplan

# apply the plan (non-interactive in CI/CD)
terraform apply -auto-approve plan.tfplan

What just happened: terraform init downloads module dependencies and initialises the working directory. terraform plan computes the desired changes and writes a plan. terraform apply executes the plan — the module makes HTTP calls to the Catalyst Center Intent API. For operations that are asynchronous, the module captures and exposes task IDs in the Terraform state.

Real-world note: In production pipelines, keep apply output logs and task IDs for audit. If a task fails, task IDs allow you to query controller diagnostics and rollback changes.

Verify:

# expected terraform apply completion message
# (simulated expected output)

Expected output:

Initializing the backend...

Initializing provider plugins...
- Finding latest version of provider "registry.terraform.io/hashicorp/null"...
- Installing registry.terraform.io/hashicorp/null v3.1.0...
Terraform has been successfully initialized!

An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # catalystcenter_transit_network.tr["Transit1"] will be created
  + resource "catalystcenter_transit_network" "tr" {
      + name                          = "CORP"
      + autonomous_system_number      = 65010
      + type                          = "IP_BASED_TRANSIT"
    }

  # catalystcenter_transit_network.tr["Transit2"] will be created
  + resource "catalystcenter_transit_network" "tr" {
      + name                          = "Guest"
      + autonomous_system_number      = 65020
      + type                          = "IP_BASED_TRANSIT"
    }

Plan: 2 to add, 0 to change, 0 to destroy.

Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

Applying the plan...
catalystcenter_transit_network.tr["Transit1"]: Creating...
catalystcenter_transit_network.tr["Transit1"]: Creation complete after 5s [id=task-12345]
catalystcenter_transit_network.tr["Transit2"]: Creating...
catalystcenter_transit_network.tr["Transit2"]: Creation complete after 4s [id=task-12346]

Apply complete! Resources: 2 added, 0 changed, 0 destroyed.

Note: The module returned task-like IDs (here shown as task-12345). The important production behavior is that the API returns an immediate task identifier and the module recorded it.


Verification Checklist

  • Check 1: Defaults file exists and contains BGP and IP_BASED defaults. Verify with cat data/defaults.yaml.
  • Check 2: Transit data contains CORP (ASN 65010) and Guest (ASN 65020). Verify with cat data/transits.yaml.
  • Check 3: Terraform apply completed with added resources and produced task IDs. Verify with terraform show and check the state for the created resources and their recorded task IDs.

Common Mistakes

SymptomCauseFix
Terraform plan shows changes every run (non-idempotent)Templates generate dynamic values (timestamps or random names)Remove transient values from templates or add stable naming rules in defaults.yaml
Authentication failures (401/403)Wrong credentials or using hard-coded expired tokensVerify cc_username/cc_password, rotate tokens, use a secrets manager
API version mismatch causes payload errorsModule or templates assume different API version than controllerSet cc_api_version to the correct value; update module/templates to match controller version
Long-running operations appear stuckAutomation didn't poll task endpoint or misinterprets task statesImplement task polling and handle PENDING -> RUNNING -> SUCCESS/FAILURE states; check controller logs for error details

Key Takeaways

  • Catalyst Center Intent API operations are often asynchronous: automation must capture and poll task IDs until completion.
  • Separate data (YAML) from code (templates/module) and use a central defaults file to reduce human error and to apply consistent flags (for example, BGP for transits).
  • Always specify and control API versioning from automation — mismatched versions lead to subtle payload/field differences and failures.
  • In production, treat credentials and API tokens as sensitive: store them in a vault and never commit plaintext credentials into source control.

Important final note: This lesson described the Intent API workflow from the automation side (Terraform + YAML data). In practice, adapt polling/timeout values and error handling to your organization’s change windows and rollback policies so an incomplete task does not leave the network in an inconsistent state.