RADIUS decides who gets on the network. TACACS+ decides what an engineer is allowed to type once they're on a router. With Cisco ISE, that means a Device Admin policy set, a shell profile for the privilege level, and command sets that permit or deny each command.
This post builds that for one IOS XE router and two kinds of users. A network admin gets everything. A help desk user gets show commands but is blocked from show running-config and from config mode. Then I break the TACACS+ shared secret on purpose, to show what ISE logs and how the local fallback account keeps you from being locked out.
I built this in my home lab with Cisco ISE 3.5.0.527 and a Cisco IOS XE 17.18.2 router. All the output is real. Addresses are documentation values, and secrets and passwords are placeholders.
For the theory behind AAA, see AAA, TACACS+ and RADIUS for Device Administration. If ISE isn't installed yet, start with Cisco ISE 3.5 Initial Setup.
The lab
| Device | Role | Addressing |
|---|---|---|
| ise35 | Cisco ISE 3.5, TACACS+ server (TCP 49) | 192.0.2.10 |
| core-rtr5 | IOS XE 17.18.2 router, TACACS+ client | Ethernet0/0 10.86.251.11/24 in VRF MGMT |
| ops-01 | Admin workstation, SSH client | 10.86.251.21/24 |

| User | ISE identity group | Intended access |
|---|---|---|
| na-01 | NetOps-Admins | Privilege 15, every command |
| hd-11 | NetOps-Helpdesk | Privilege 15, read-only commands enforced by a command set |
| netadmin-local | (local on the router) | Break-glass account, only used when ISE is unreachable |
Step 1: Enable Device Admin on the ISE node
TACACS+ is off by default. In Administration > System > Deployment, edit the node and tick Enable Device Admin Service. Until you do, ISE doesn't listen on TCP 49 and the router will mark the server dead.
Step 2: Add the router as a network device
In Administration > Network Resources > Network Devices, add core-rtr5 with IP 10.86.251.11, expand TACACS Authentication Settings, and set the shared secret. Use the same secret on the router. Step 7 shows what happens when they don't match.
Step 3: Identity groups and users
Create two user identity groups, NetOps-Admins and NetOps-Helpdesk, then two internal users: na-01 in NetOps-Admins and hd-11 in NetOps-Helpdesk. In production these would usually be AD groups, but the policy logic is identical.
Step 4: Shell profile and command sets
In Work Centers > Device Administration > Policy Elements > Results:
- TACACS Profiles: create
IOS_Priv15with Default Privilege and Maximum Privilege set to 15. - TACACS Command Sets: create two sets.
| Command set | Rules |
|---|---|
| CS-Permit-All | Permit any command that is not listed below ticked, no rules |
| CS-Helpdesk-Show | DENY_ALWAYS show running-config.*; PERMIT terminal length.*; PERMIT show; PERMIT ping; PERMIT traceroute; PERMIT exit. Unlisted commands are denied |
Order matters less than you'd think for DENY_ALWAYS: it wins over any permit in any set, which is why show running-config stays blocked even though plain show is permitted.
Why the help desk gets privilege 15. My first attempt gave hd-11 privilege 1. That doesn't work for this design: at privilege 1 IOS rejectsshow running-configandconfigure terminalitself with% Invalid input, before command authorization ever asks ISE. You get no Live Logs entry and nothing to audit. Giving priv 15 and restricting with a command set means every command goes to ISE, gets a decision and gets logged.
Step 5: The Device Admin policy set
In Work Centers > Device Administration > Device Admin Policy Sets, add a policy set IOS-Device-Admin with the condition DEVICE:Device Type EQUALS All Device Types and the Default Device Admin protocol.
- Authentication policy: Default rule, identity source All_User_ID_Stores, reject if the user isn't found or authentication fails.
- Authorization policy:
| Rule | Condition | Command set | Shell profile |
|---|---|---|---|
| NetOps-Admins-Priv15 | IdentityGroup:Name EQUALS User Identity Groups:NetOps-Admins | CS-Permit-All | IOS_Priv15 |
| NetOps-Helpdesk-ReadOnly | IdentityGroup:Name EQUALS User Identity Groups:NetOps-Helpdesk | CS-Helpdesk-Show | IOS_Priv15 |
| Default | (none) | DenyAllCommands | Deny All Shell Profile |
Step 6: Router configuration
username netadmin-local privilege 15 secret <LOCAL_PASSWORD>
enable secret <ENABLE_SECRET>
!
aaa new-model
tacacs server ISE-1
address ipv4 192.0.2.10
key <TACACS_SHARED_SECRET>
timeout 5
aaa group server tacacs+ ISE-TAC
server name ISE-1
ip vrf forwarding MGMT
ip tacacs source-interface Ethernet0/0
!
aaa authentication login CON none
aaa authentication login VTY group ISE-TAC local
aaa authorization exec VTY group ISE-TAC local if-authenticated
aaa authorization commands 1 VTY group ISE-TAC local if-authenticated
aaa authorization commands 15 VTY group ISE-TAC local if-authenticated
aaa accounting exec VTY start-stop group ISE-TAC
aaa accounting commands 1 VTY start-stop group ISE-TAC
aaa accounting commands 15 VTY start-stop group ISE-TAC
!
line con 0
login authentication CON
line vty 0 4
login authentication VTY
authorization exec VTY
authorization commands 1 VTY
authorization commands 15 VTY
accounting exec VTY
accounting commands 1 VTY
accounting commands 15 VTY
transport input ssh
Notes on this config:
- Named method lists on the VTYs only. The console uses
CON nonehere because it's a lab. In production, give the console its own list withlocalso you're never locked out by a bad AAA change. group ISE-TAC localfalls back to the local database only when ISE is unreachable (marked dead), not when ISE rejects the password. That's the break-glass path tested in Step 7.ip vrf forwarding MGMTplus the source interface keeps TACACS+ in the management VRF.- IOS XE 17.18 printed
%TAC-4-NON_TLS_SERVER_CONFIGURED, a warning that this server uses classic TACACS+ rather than TACACS+ over TLS. It works as shown; TLS is a separate project.
Step 7: Test the admin
From ops-01, SSH as na-01:
core-rtr5#show privilege
Current privilege level is 15
core-rtr5#show running-config | include ^hostname
hostname core-rtr5
core-rtr5#configure terminal
Enter configuration commands, one per line. End with CNTL/Z.
core-rtr5(config)#interface Loopback0
core-rtr5(config-if)#description set-by-na-01
core-rtr5(config-if)#end
Everything is allowed. Open Operations > TACACS > Live Logs:

The authorization detail for na-01 shows the policy set and rule that matched and the shell profile returned:

Step 8: Test the help desk user
SSH as hd-11:
core-rtr5#show privilege
Current privilege level is 15
core-rtr5#show ip interface brief | include Ethernet0/0
Ethernet0/0 10.86.251.11 YES TFTP up up
core-rtr5#show running-config
core-rtr5#show running-config | include hostname
core-rtr5#configure terminal
core-rtr5#show clock
*07:28:56.165 UTC Sat Oct 31 2026
show ip interface brief and show clock run. show running-config and configure terminal just return to the prompt with no output. On this IOS XE build there's no "Command authorization failed" message at the CLI, so the help desk user only sees that nothing happened. The reason is on ISE.
show running-config hit the DENY_ALWAYS rule:

configure terminal matched nothing in CS-Helpdesk-Show, so it was denied by default:

That difference is useful when you're auditing: 13023 means someone tried something you explicitly banned, 13025 means they tried something you never allowed.
Step 9: Fault case, a shared secret mismatch
I changed the TACACS+ shared secret on the ISE network device entry and left the router alone, then tried to SSH as na-01. The login was refused. ISE logged a failed authentication with no username:

TACACS+ encrypts the whole packet body with the shared secret, so with the wrong key ISE can't even read the username. That's why the Username field is blank and the steps stop at 13011. Authorization requests from the same router in that window logged 13078 Invalid TACACS+ authorization request packet - possibly malformed packet, which is the same problem.
On the router, the server got marked dead, so the next login used the local fallback:
core-rtr5#show tacacs
Tacacs+ Server - public :
Server name: ISE-1
Server address: 192.0.2.10
Server port: 49
...
Server Status: Dead
core-rtr5#show logging | include SEC_LOGIN
*Oct 31 07:31:46.382: %SEC_LOGIN-5-LOGIN_SUCCESS: Login Success [user: netadmin-local] [Source: 10.86.251.21] [localport: 22] at 07:31:46 UTC Sat Oct 31 2026
Fix: set the ISE network device secret back to match the router's key. One thing that caught me out: the first na-01 login straight after the fix still failed, and ISE logged nothing for it, because the router still had the server marked Dead. About 40 seconds later the login worked and show tacacs showed Server Status: Alive. If you fix a key and it "still doesn't work", wait out the dead timer (or set aaa dead-criteria and deadtime deliberately) before changing anything else.
Troubleshooting table
| Live Logs code | Message | Likely cause | Fix |
|---|---|---|---|
| 13011 | Invalid TACACS+ request packet - possibly mismatched Shared Secrets | Router key and ISE network device TACACS secret differ |
Make them match, then wait for the router to mark the server alive |
| 13078 | Invalid TACACS+ authorization request packet - possibly malformed packet | Usually the same key mismatch, seen on authorization requests | Same as 13011 |
| 13017 | Received TACACS+ packet from unknown Network Device or AAA Client | The source IP isn't a network device in ISE, or TACACS isn't enabled on that entry | Add the device or fix the source interface |
| 13023 | Command matched a Deny-Always rule | A DENY_ALWAYS rule in the command set | Working as designed, or move the user to another group |
| 13025 | Command failed to match a Permit rule | Command not in the command set and unlisted commands are denied | Add a PERMIT rule if the user should have it |
| (nothing logged) | Router says login failed, ISE shows no entry | Device Admin service off, TCP 49 blocked, or server marked dead | Enable Device Admin, check reachability and show tacacs |
% Invalid input at the CLI |
IOS rejects the command, ISE never sees it | User's privilege level is too low for the command to exist | Use priv 15 plus command sets, or custom privilege levels |
Takeaways
- Device Admin is a separate ISE service. Enable it on the node before anything else.
- Give restricted users privilege 15 and limit them with command sets, so every command reaches ISE and is logged. Low privilege levels make IOS reject commands before ISE sees them.
- DENY_ALWAYS beats every permit. Use it for the few commands that must never run, like
show running-configfor a help desk. - 13023 and 13025 tell you whether a denial was an explicit ban or just never permitted.
- A key mismatch shows as 13011 with no username. The
localfallback only kicks in once the server is dead, and recovery waits for the dead timer.
Related: ISE Live Logs Casebook: Five 802.1X Failures, Reproduced covers the RADIUS side of the same troubleshooting habit, and ISE 11007: Could Not Locate Network Device is the RADIUS cousin of 13017.