ERS Authorization Profiles

Authorization profiles define what happens after authentication succeeds. VLAN assignment, dACL enforcement, SGT tagging - this is where policy meets enforcement.

Quick Reference

Command Purpose

netapi ise get-authz-profiles

List all profiles

netapi ise get-authz-profile <NAME>

Get single profile details

netapi ise --format json get-authz-profile <NAME>

JSON output for jq piping

netapi ise update-authz-profile <NAME> --vlan <VLAN>

Change VLAN assignment

netapi ise update-authz-profile <NAME> --dacl <DACL>

Change dACL assignment

netapi ise update-authz-profile <NAME> --no-dacl

Remove dACL from profile

Profile Anatomy

An authorization profile contains:

{
  "id": "298c2780-f164-11f0-b76e-52c54a1d1f56",
  "name": "Research_Onboard",
  "description": "MAB onboarding for research endpoints",
  "accessType": "ACCESS_ACCEPT",         // or ACCESS_REJECT
  "authzProfileType": "SWITCH",          // SWITCH, WLAN, or TRUSTSEC
  "vlan": {
    "nameID": "IOT_VLAN",                // Must match switch VLAN name!
    "tagID": 1                           // RADIUS attribute tag
  },
  "daclName": "RESEARCH_ONBOARD_DACL",   // Downloadable ACL
  "profileName": "Cisco",                // Network device profile
  "reauth": {
    "timer": 3600,                       // Reauth interval (seconds)
    "connectivity": "RADIUS_REQUEST"     // or DEFAULT, MAINTAIN_CONNECTIVITY
  }
}
The vlan.nameID must be the exact switch VLAN name (e.g., IOT_VLAN), not a numeric ID (e.g., 40). Numeric IDs cause silent failures - the switch won’t apply the VLAN even though ISE reports success.

JQ-FU: Advanced Query Patterns

The --format option is global - it goes before the subcommand:

netapi ise --format json get-authz-profile "Research_Onboard" | jq '.'

Color Code Reference

Color ANSI Code jq Syntax

Green

\033[32m

\u001b[32m

Yellow

\033[33m

\u001b[33m

Blue

\033[34m

\u001b[34m

Cyan

\033[36m

\u001b[36m

Bold White

\033[1;37m

\u001b[1;37m

Red

\033[31m

\u001b[31m

Reset

\033[0m

\u001b[0m

Pattern 1: One-Liner Status Card

Quick glance at a profile’s key settings:

# One-liner status card (colored)
curl -ks -H "Authorization: Basic $ISE_API_TOKEN" -H "Accept: application/json" \
  "https://${ISE_HOST}:9060/ers/config/authorizationprofile/name/${PROFILE_NAME}" | \
  jq -r '.AuthorizationProfile | "\u001b[32m[\(.accessType)]\u001b[0m \u001b[1;37m\(.name)\u001b[0m → VLAN:\u001b[33m\(.vlan.nameID // "none")\u001b[0m dACL:\u001b[36m\(.daclName // "none")\u001b[0m"'
Output
[ACCESS_ACCEPT] Research_Onboard → VLAN:IOT_VLAN dACL:RESEARCH_ONBOARD_DACL

Pattern 2: Beautiful Box Summary

Full profile details with box-drawing characters:

# Beautiful profile summary with all fields
PROFILE_NAME="Research_Onboard"
curl -ks -H "Authorization: Basic $ISE_API_TOKEN" -H "Accept: application/json" \
  "https://${ISE_HOST}:9060/ers/config/authorizationprofile/name/${PROFILE_NAME}" | \
  jq -r '.AuthorizationProfile | "
\u001b[1;37m╔══════════════════════════════════════════════════════════════╗
║  AUTHORIZATION PROFILE: \(.name | .[0:38])\u001b[0m\u001b[1;37m
╠══════════════════════════════════════════════════════════════╣\u001b[0m
  \u001b[34mID:\u001b[0m          \(.id)
  \u001b[34mAccess:\u001b[0m      \u001b[32m\(.accessType)\u001b[0m
  \u001b[34mType:\u001b[0m        \(.authzProfileType)
  \u001b[34mVLAN:\u001b[0m        \u001b[33m\(.vlan.nameID // "none")\u001b[0m (tag: \(.vlan.tagID // "-"))
  \u001b[34mdACL:\u001b[0m        \u001b[36m\(.daclName // "none")\u001b[0m
  \u001b[34mProfile:\u001b[0m     \(.profileName // "-")
  \u001b[34mDesc:\u001b[0m        \(.description // "-")
\u001b[1;37m╚══════════════════════════════════════════════════════════════╝\u001b[0m"'
Output
╔══════════════════════════════════════════════════════════════╗
║  AUTHORIZATION PROFILE: Research_Onboard
╠══════════════════════════════════════════════════════════════╣
  ID:          298c2780-f164-11f0-b76e-52c54a1d1f56
  Access:      ACCESS_ACCEPT
  Type:        SWITCH
  VLAN:        IOT_VLAN (tag: 1)
  dACL:        RESEARCH_ONBOARD_DACL
  Profile:     Cisco
  Desc:        MAB onboarding for research endpoints
╚══════════════════════════════════════════════════════════════╝

Pattern 3: ALL Profiles Table

Audit every profile’s VLAN and dACL assignment:

# ALL profiles table with VLAN + dACL (colored, production-ready)
printf "\u001b[1;37m%-35s %-15s %-30s\u001b[0m\n" "PROFILE" "VLAN" "DACL"
printf "%s\n" "$(printf '=%.0s' {1..80})"
curl -ks -H "Authorization: Basic $ISE_API_TOKEN" -H "Accept: application/json" \
  "https://${ISE_HOST}:9060/ers/config/authorizationprofile" | \
  jq -r '.SearchResult.resources[].name' | sort | while read name; do
    curl -ks -H "Authorization: Basic $ISE_API_TOKEN" -H "Accept: application/json" \
      "https://${ISE_HOST}:9060/ers/config/authorizationprofile/name/$name" 2>/dev/null | \
      jq -r --arg n "$name" '.AuthorizationProfile |
        "\u001b[1;37m\($n)\u001b[0m\t\u001b[33m\(.vlan.nameID // "-")\u001b[0m\t\u001b[36m\(.daclName // "-")\u001b[0m"'
done | column -t -s $'\t'
Output
PROFILE                              VLAN            DACL
================================================================================
Blackhole_Wireless_Access            -               BLACKHOLE_DENY
Cisco_IP_Phones                      VOICE_VLAN      PERMIT_ALL
Deny_Access                          -               -
Linux_EAPTLS_Permit                  DATA_VLAN       Linux-Permit-AD-Only
Research_EAPTLS_Permit               IOT_VLAN        RESEARCH_PERMIT_DACL
Research_Onboard                     IOT_VLAN        RESEARCH_ONBOARD_DACL

Pattern 4: Stale VLAN Finder

Find profiles using numeric VLAN IDs (misconfiguration):

# Find profiles with stale/numeric VLAN IDs (potential misconfig)
echo -e "\u001b[1;37mProfiles with numeric VLAN IDs (potential misconfiguration):\u001b[0m"
curl -ks -H "Authorization: Basic $ISE_API_TOKEN" -H "Accept: application/json" \
  "https://${ISE_HOST}:9060/ers/config/authorizationprofile" | \
  jq -r '.SearchResult.resources[].name' | while read name; do
    RESULT=$(curl -ks -H "Authorization: Basic $ISE_API_TOKEN" -H "Accept: application/json" \
      "https://${ISE_HOST}:9060/ers/config/authorizationprofile/name/$name" 2>/dev/null | \
      jq -r '.AuthorizationProfile | select(.vlan.nameID != null) |
        select(.vlan.nameID | test("^[0-9]+$")) |
        "\(.name)\t\(.vlan.nameID)"')
    if [ -n "$RESULT" ]; then
      echo -e "  \u001b[31m⚠\u001b[0m $RESULT" | column -t -s $'\t'
    fi
done
Output
Profiles with numeric VLAN IDs (potential misconfiguration):
  ⚠ Legacy_Profile    40
  ⚠ Old_Guest         999

Pattern 5: dACL Audit

Group all profiles by their dACL assignment:

# dACL audit: Group profiles by dACL assignment
echo -e "\u001b[1;37mProfiles grouped by dACL:\u001b[0m"
curl -ks -H "Authorization: Basic $ISE_API_TOKEN" -H "Accept: application/json" \
  "https://${ISE_HOST}:9060/ers/config/authorizationprofile" | \
  jq -r '.SearchResult.resources[].name' | while read name; do
    curl -ks -H "Authorization: Basic $ISE_API_TOKEN" -H "Accept: application/json" \
      "https://${ISE_HOST}:9060/ers/config/authorizationprofile/name/$name" 2>/dev/null | \
      jq -r --arg n "$name" '[(.AuthorizationProfile.daclName // "NO_DACL"), $n] | @tsv'
done | sort | awk -F'\t' '
  BEGIN { prev="" }
  {
    if ($1 != prev) {
      printf "\n\033[36m%s\033[0m\n", $1
      prev = $1
    }
    printf "  \033[33m%s\033[0m\n", $2
  }'
Output
BLACKHOLE_DENY
  Blackhole_Wireless_Access

Linux-Permit-AD-Only
  Linux_EAPTLS_Permit

NO_DACL
  Deny_Access

PERMIT_ALL
  Cisco_IP_Phones

RESEARCH_ONBOARD_DACL
  Research_Onboard

Pattern 6: Profile Diff

Compare two profiles side-by-side:

# Compare two profiles side-by-side (diff)
PROFILE_A="Research_Onboard"
PROFILE_B="Research_EAPTLS_Permit"

curl -ks -H "Authorization: Basic $ISE_API_TOKEN" -H "Accept: application/json" \
  "https://${ISE_HOST}:9060/ers/config/authorizationprofile/name/${PROFILE_A}" | \
  jq '.AuthorizationProfile | del(.id, .link)' > /tmp/prof_a.json

curl -ks -H "Authorization: Basic $ISE_API_TOKEN" -H "Accept: application/json" \
  "https://${ISE_HOST}:9060/ers/config/authorizationprofile/name/${PROFILE_B}" | \
  jq '.AuthorizationProfile | del(.id, .link)' > /tmp/prof_b.json

diff --color=always <(jq -S . /tmp/prof_a.json) <(jq -S . /tmp/prof_b.json)
Output (diff)
2c2
<   "name": "Research_Onboard",
---
>   "name": "Research_EAPTLS_Permit",
4c4
<   "description": "MAB onboarding for research endpoints - limited access",
---
>   "description": "EAP-TLS authenticated research endpoints - full access",
8c8
<   "daclName": "RESEARCH_ONBOARD_DACL",
---
>   "daclName": "RESEARCH_PERMIT_DACL",

Pattern 7: VLAN Audit

Find all profiles using a specific VLAN:

# VLAN audit: Find profiles using specific VLAN (or missing VLAN)
TARGET_VLAN="IOT_VLAN"  # or "none" to find profiles without VLAN

echo -e "\u001b[1;37mProfiles using VLAN: ${TARGET_VLAN}\u001b[0m"
curl -ks -H "Authorization: Basic $ISE_API_TOKEN" -H "Accept: application/json" \
  "https://${ISE_HOST}:9060/ers/config/authorizationprofile" | \
  jq -r '.SearchResult.resources[].name' | while read name; do
    VLAN=$(curl -ks -H "Authorization: Basic $ISE_API_TOKEN" -H "Accept: application/json" \
      "https://${ISE_HOST}:9060/ers/config/authorizationprofile/name/$name" 2>/dev/null | \
      jq -r '.AuthorizationProfile.vlan.nameID // "none"')
    if [ "$VLAN" = "$TARGET_VLAN" ]; then
      echo -e "  \u001b[32m✓\u001b[0m $name"
    fi
done

Pattern 8: Filter Non-Empty Values

Strip out all the false/null/empty noise:

# Only non-null, non-false, non-empty values (the interesting stuff)
curl -ks -H "Authorization: Basic $ISE_API_TOKEN" -H "Accept: application/json" \
  "https://${ISE_HOST}:9060/ers/config/authorizationprofile/name/${PROFILE_NAME}" | \
  jq '.AuthorizationProfile | with_entries(select(.value != false and .value != null and .value != ""))'

Pattern 9: Flatten to Key-Value

Grep-friendly vertical output:

# Vertical key=value (grep-friendly, colored)
curl -ks -H "Authorization: Basic $ISE_API_TOKEN" -H "Accept: application/json" \
  "https://${ISE_HOST}:9060/ers/config/authorizationprofile/name/${PROFILE_NAME}" | \
  jq -r '.AuthorizationProfile | to_entries[] | "\u001b[34m\(.key)\u001b[0m=\u001b[33m\(.value)\u001b[0m"'

Pattern 10: netapi + jq + awk Pipeline

Full pipeline with colored awk output:

# netapi with global --format option (jq-friendly)
netapi ise --format json get-authz-profile "Research_Onboard" | jq '.'

# Extract specific fields
netapi ise --format json get-authz-profile "Research_Onboard" | jq '{name, vlan: .vlan.nameID, dacl: .daclName}'

# Pipe to awk for custom formatting
netapi ise --format json get-authz-profile "Research_Onboard" | \
  jq -r '[.name, .vlan.nameID, .daclName] | @tsv' | \
  awk -F'\t' '{printf "\033[1;37m%s\033[0m VLAN:\033[33m%s\033[0m dACL:\033[36m%s\033[0m\n", $1, $2, $3}'

Troubleshooting

VLAN_NOT_FOUND Error

AUTHMGR-5-FAIL: Authorization failed or unapplied for client
VLAN_NOT_FOUND: RESEARCH_VLAN

Root Cause: Profile has stale VLAN name that doesn’t exist on switch.

Fix:

# Check current VLAN assignment
netapi ise --format json get-authz-profile "Research_Onboard" | jq '.vlan'

# Update to correct VLAN name
netapi ise update-authz-profile "Research_Onboard" --vlan IOT_VLAN

# Verify
netapi ise --format json get-authz-profile "Research_Onboard" | jq '.vlan.nameID'

Numeric VLAN ID Issue

ISE shows "nameID": "40" instead of "nameID": "IOT_VLAN".

Problem: Using numeric ID causes silent authorization failures.

Fix:

netapi ise update-authz-profile "Research_Onboard" --vlan IOT_VLAN

Find All Misconfigured Profiles

# One-liner to find numeric VLANs
curl -ks -H "Authorization: Basic $ISE_API_TOKEN" -H "Accept: application/json" \
  "https://${ISE_HOST}:9060/ers/config/authorizationprofile" | \
  jq -r '.SearchResult.resources[].name' | while read name; do
    VLAN=$(curl -ks -H "Authorization: Basic $ISE_API_TOKEN" -H "Accept: application/json" \
      "https://${ISE_HOST}:9060/ers/config/authorizationprofile/name/$name" 2>/dev/null | \
      jq -r '.AuthorizationProfile.vlan.nameID // empty')
    if [[ "$VLAN" =~ ^[0-9]+$ ]]; then
      echo -e "\033[31m⚠ NUMERIC:\033[0m $name → $VLAN"
    fi
done

CRUD Operations

Create Profile

# Basic with VLAN
netapi ise create-authz-profile "New_Profile" --vlan DATA_VLAN

# With VLAN + dACL
netapi ise create-authz-profile "Secure_Profile" --vlan DATA_VLAN --dacl PERMIT_AD_ONLY

# With description and reauth
netapi ise create-authz-profile "IoT_Profile" \
  --vlan IOT_VLAN \
  --dacl IOT_RESTRICTED \
  --descr "IoT devices - restricted access" \
  --reauth-timer 3600

Update Profile

# Change VLAN
netapi ise update-authz-profile "Research_Onboard" --vlan IOT_VLAN

# Change dACL
netapi ise update-authz-profile "Research_Onboard" --dacl NEW_DACL

# Remove dACL
netapi ise update-authz-profile "Research_Onboard" --no-dacl

# Set reauth timer (8 hours)
netapi ise update-authz-profile "Research_Onboard" --reauth-timer 28800

Delete Profile

netapi ise delete-authz-profile "Old_Profile"

Bulk Operations from YAML

netapi ise create-authz-profiles-from-file profiles.yaml
netapi ise update-authz-profiles-from-file profiles.yaml
profiles.yaml
profiles:
  - name: Domus_Secure_Profile
    description: "Trusted users - full network access"
    access_type: ACCESS_ACCEPT
    vlan:
      name: DATA_VLAN
      tag: 1
    dacl_name: DACL_SECURE_FULL
    reauth_timer: 28800

  - name: Domus_IoT_Profile
    description: "IoT devices - restricted access"
    access_type: ACCESS_ACCEPT
    vlan:
      name: IOT_VLAN
      tag: 1
    dacl_name: DACL_IOT_RESTRICTED
    reauth_timer: 3600

Raw curl Examples

For automation or when netapi isn’t available:

Get Profile

# Get profile by name
PROFILE_NAME="Linux-EAP-TLS-Access"
curl -sk -u "${ISE_AUTH}" \
  "${BASE_URL}/authorizationprofile/name/${PROFILE_NAME}" \
  -H "Accept: application/json" | jq '.AuthorizationProfile'

Create Profile with dACL

# Create profile with dACL
# First get dACL ID
DACL_NAME="Linux-Permit-AD-Only"
DACL_ID=$(curl -sk -u "${ISE_AUTH}" \
  "${BASE_URL}/downloadableacl/name/${DACL_NAME}" \
  -H "Accept: application/json" | jq -r '.DownloadableAcl.id')

curl -sk -u "${ISE_AUTH}" \
  "${BASE_URL}/authorizationprofile" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -X POST \
  -d '{
    "AuthorizationProfile": {
      "name": "Linux-EAP-TLS-Access",
      "description": "EAP-TLS authenticated Linux with AD-only dACL",
      "accessType": "ACCESS_ACCEPT",
      "authzProfileType": "SWITCH",
      "daclName": "'"${DACL_NAME}"'",
      "vlan": {
        "nameID": "DATA",
        "tagID": 10
      }
    }
  }'

Clone Profile

# Clone existing profile with new name
SOURCE_PROFILE="Linux-EAP-TLS-Access"
NEW_PROFILE="Linux-EAP-TLS-Access-v2"

# Get source profile
PROFILE=$(curl -sk -u "${ISE_AUTH}" \
  "${BASE_URL}/authorizationprofile/name/${SOURCE_PROFILE}" \
  -H "Accept: application/json")

# Remove ID and update name
NEW_PROFILE_JSON=$(echo "$PROFILE" | jq '.AuthorizationProfile | del(.id) | del(.link) | .name = "'"${NEW_PROFILE}"'" | .description = "Cloned from '"${SOURCE_PROFILE}"'"')

curl -sk -u "${ISE_AUTH}" \
  "${BASE_URL}/authorizationprofile" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -X POST \
  -d "{\"AuthorizationProfile\": ${NEW_PROFILE_JSON}}"

Export All Profiles

# Export all profiles to JSON
curl -sk -u "${ISE_AUTH}" \
  "${BASE_URL}/authorizationprofile" \
  -H "Accept: application/json" | \
  jq -r '.SearchResult.resources[].id' | while read ID; do
    curl -sk -u "${ISE_AUTH}" \
      "${BASE_URL}/authorizationprofile/${ID}" \
      -H "Accept: application/json"
  done | jq -s '.' > authz-profiles-backup.json