About a year ago /u/packetsar shared FreeZTP on reddit, just in time for a project that required upgrade/configuration of 400+ IOS-XE switches (3650/3850, several stacks). Over the following months, he was extremely receptive and accommodating with feature updates and tweaks; so kudos, pal.

This is a sanitized example of a switch template that I built out with a strong focus on keeping the process as simple and unbreakable as possible for the intended end-users, at the cost of a self-imposed excessively large learning curve. My last three blog posts were a direct result of achieving this goal; something that a lot of docs, labbing, and scouring of community forums contributed to.

A Few Disclosures

  • I do not have a background in development, this was my first venture into any kind of programming/templating. This was largely an exercise in figuring out what I could accomplish in the template, without having to manually intervene, whether it be via python, console, or otherwise. There are probably some development best-practices that I unwittingly spat in the face of, or more optimal ways to accomplish some of these functions.
  • Smart-install is deprecated as of IOS-XE 16.3.7+ (still works in 16.3.6). This does not impact AutoInstall.
  • /u/packetsar has done an excellent job detailing all facets of FreeZTP, so questions/concerns regarding its inner-workings are better answered by the project's GitHub readme.md.

FreeZTP config

I configured FreeZTP to use external keystore and template files. This kept the code and other sensitive template information somewhat concealed, for lack of a better term. This also kept ztp show config outputs concise and easier to read when validating FreeZTP settings.

ztp set external-keystore SBS type csv && \
~ztp set external-keystore SBS file '/srv/ztp/keystore/ks_asw.csv' && \
~ztp set external-template SBS file '/srv/ztp/template/tmpl_asw.j2'

CSV

The CSV file has minimal headings; keystore_id (hostname), association (template), and idarray_1 - idarray_4 for the switch serial numbers (our defined stack limitation for this project).

keystore_id,association,idarray_1,idarray_2,idarray_3,idarray_4
ASW-TR04-02,ASW,FOC11111111,FOC22222222,FOC33333333,FOC44444444

Template

  • First section provides guidelines and outputs the variables that ZTP passed into the template.

  • Second section defines static variables that I didn't want to store in the CSV; e.g. protocol secrets/keys, syslog server addresses, and other things that the template will use.

  • Third section assigns dynamic values to variables.

    Expand for Dynamic Variable Example

    gi_ints is a list of GigabitEthernet interfaces, which scales with the number of switches in the stack.

    Switch Count gi_ints
    1 gi1/0/1-36
    2 gi1/0/1-36,gi2/0/1-36
    3 gi1/0/1-36,gi2/0/1-36,gi3/0/1-36
    4 gi1/0/1-36,gi2/0/1-36,gi3/0/1-36,gi4/0/1-36
{%-set tmpl={"file":"tmpl_test.j2",
             "rev":"8"}-%}
{%-set indent="\n!"~" "*23-%}
!-- --------------------------------------------------------------------------- --!
!--                          ***IMPORTANT (Example)***                          --!
!--          Hostname (keystore_id in CSV) has strict formatting rules.         --!
!--                                                                             --!
!--  * TR and switch/stack numbers must each be two digits (use leading '0').   --!
!--                           * TRs are numbered 1-9.                           --!
!--     * Any TR can only accommodate up to 9 switches/stacks; i.e. 01-09.      --!
!--      * Any stack is only intended to have 4 switches; i.e. idarray_1-4.     --!
!--                                                                             --!
!--                     Example for TR-4, switch/stack 1...                     --!
!--                                                                             --!
!--      Access Switch      |       TR Number        |   Switch/Stack Number    --!
!--   ----------------------|------------------------|----------------------    --!
!--           ASW           |          TR04          |            02            --!
!--                                                                             --!
!--                        Example hostname: ASW-TR04-02                        --!
!-- --------------------------------------------------------------------------- --!
!
!-- Variables (keys) parsed from CSV keystore.
!
!---- KEYSTORE_ID     > {{keystore_id|default("N/A")}}{# Used to associate the switch to a template (serial number).#}
!---- ASSOCIATION     > {{association|default("N/A")}}{# Switch template associated with KEYSTORE_ID.#}
!---- IDARRAY_1       > {{idarray_1|default("N/A")}}{#   Switch 1 serial number.#}
!---- IDARRAY_2       > {{idarray_2|default("N/A")}}{#   Switch 2 serial number.#}
!---- IDARRAY_3       > {{idarray_3|default("N/A")}}{#   Switch 3 serial number.#}
!---- IDARRAY_4       > {{idarray_4|default("N/A")}}{#   Switch 4 serial number.#}
!---- IDARRAY         > {{idarray|default("N/A")}}{#     All switch serial numbers.#}
!
!---------------------------------------------------------------- VARIABLES: STATIC
!
!-- Variables (keys) statically defined within the template.
!-- Variables with multiple values are JSON formatted.
!{#   VARIABLE        > {%set VARIABLE="VALUE"%}                    <<< set | print >>>          {{VARIABLE}}   #}
!---- TFTP_ADDR       > {%set tftp_addr="172.17.251.251"%}{#                                   #}{{tftp_addr}}
!---- DOMAIN          > {%set domain="exmaple.local"%}{#                                       #}{{domain}}
!---- STP_PRIORITY    > {%set stp_priority="28672"%}{#                                         #}{{stp_priority}}
!---- ENABLE_SEC      > {%set enable_sec="enable-secret"%}{#                                   #}{{enable_sec}}
!---- TACACS_KEY      > {%set tacacs_key="tacacs-secret"%}{#                                   #}{{tacacs_key}}
!---- RADIUS_KEY      > {%set radius_key="radius-secret"%}{#                                   #}{{radius_key}}
!---- NTP_KEY         > {%set ntp_key="ntp-secret"%}{#                                         #}{{ntp_key}}
!---- SYSLOG_ADDR     > {%set syslog_addr="10.255.1.1"%}{#                                     #}{{syslog_addr}}
!---- [IMAGE]         > {%set image={"bin":"cat3k_caa-universalk9.16.03.07.SPA.bin",
                                     "ver":"16.3.7"}%}{#                                       #}{{image|dictsort(false)|join(indent)}}
!---- [CREDS]         > {%set creds={"admin":"admin-secret",
                                     "eem_svc":"eem-secret"}%}{#                               #}{{creds|dictsort(false)|join(indent)}}
!---- [NTP_ADDR]      > {%set ntp_addr={"pri":{"addr":"10.255.1.11","pref":"prefer"},
                                        "sec":{"addr":"10.255.1.12","pref":""}}%}{#            #}{{ntp_addr|dictsort(false)|join(indent)}}
!---- [ISE_ADDR]      > {%set ise_addr={"01":"10.255.1.21",
                                        "02":"10.255.1.22",
                                        "03":"10.255.1.23"}%}{#                                #}{{ise_addr|dictsort(false)|join(indent)}}
!---- [SNMPV3]        > {%set snmpv3={"grp":"SNMP_ASW",
                                      "user":"snmp_asw",
                                      "auth":"snmp-auth",
                                      "priv":"smp-priv"}%}{#                                   #}{{snmpv3|dictsort(false)|join(indent)}}
!---- [VLAN]          > {%set vlan={"native":{"id":"999","name":"NATIVE"},
                                    "global":{"id":"998","name":"GLOBAL"}}%}{#                 #}{{vlan|dictsort(false,"value")|join(indent)}}
!
!---------------------------------------------------------------- VARIABLES: DYNAMIC
!
!-- Variables (keys) with dynamic values based on variables paresed from the CSV keystore.
!
!-- Copy HOSTNAME from KEYSTORE_ID, get SW_COUNT and SERIALS from IDARRAY.
!---- HOSTNAME        > {%set hostname=keystore_id%}{#                                         #}{{hostname}}
!---- SW_COUNT        > {%set sw_count=idarray|count%}{#                                       #}{{sw_count}}
!---- SERIALS         > {%set serials=idarray%}{#                                              #}{{serials}}
!
!-- Create range lists for all access interfaces and all provisioning interfaces based on IDARRAY.
{%set gi_ints,te_ints,prov_ints=[],[],[]-%}
{%for sw in idarray-%}
{%  set i=loop.index-%}
{%  set gi_ints,te_ints,prov_ints=
        gi_ints.append("Gi%s/0/1-36"|format(i)),
        te_ints.append("Te%s/0/37-47"|format(i)),
        prov_ints.append("Te%s/0/48"|format(i))-%}
{%endfor-%}
!---- GI_INTS         > {%set gi_ints=gi_ints|join(",")%}{#                                    #}{{gi_ints}}
!---- TE_INTS         > {%set te_ints=te_ints|join(",")%}{#                                    #}{{te_ints}}
!---- PROV_INTS       > {%set prov_ints=prov_ints|join(",")%}{#                                #}{{prov_ints}}
!
!-- Split hostname to get TR (numeric only) and SW.
!---- TR              > {%set tr=(hostname.split("-")[1])[2:]%}{#                              #}{{tr}}
!---- TRN             > {%set trn=tr[1:]%}{#                                                   #}{{trn}}
!---- SW              > {%set sw=(hostname.split("-")[2])%}{#                                  #}{{sw}}
!
!-- Determine PO_NUM from TR (second digit) and (int of) SW; also used as last octet of MGMT IP.
!---- PO_NUM          > {%set po_num="1%s%s"|format(tr[1:],sw|int)%}{#                         #}{{po_num}}
!
!-- Determine MGMT subnet; IP from subnet and PO_NUM; append a name for the default route.
{%set mgmt={"net":"10.254.%s"|format(trn)}-%}
{%set x=mgmt.update({"mask":"255.255.255.0"})-%}
{%set x=mgmt.update({"ip":"%s.%s"|format(mgmt.net,po_num)})-%}
{%set x=mgmt.update({"rt_name":"Default>MGMT:DIST-01"})-%}
!---- [MGMT]          > {#                                                                     #}{{mgmt|dictsort(false,"value")|join(indent)}}
!
!-- Determine TR_VLS from TR, append them to the (static) [VLAN] dictionary.
{%set tr_vls={"grt_thin":{"id":"1%s01"|format(trn),"name":"GRT:TR%s/THIN-CLIENTS"|format(tr)},
              "grt_thick":{"id":"1%s02"|format(trn),"name":"GRT:TR%s/THICK-CLIENTS"|format(tr)},
              "grt_voice":{"id":"1%s03"|format(trn),"name":"GRT:TR%s/VOICE"|format(tr)},
              "grt_crit":{"id":"1%s99"|format(trn),"name":"GRT:TR%s/CRIT-AUTH"|format(tr)},
              "mgmt_sw":{"id":"2%s01"|format(trn),"name":"MGMT:TR%s/SW-WLC"|format(tr)},
              "mgmt_ap":{"id":"2%s02"|format(trn),"name":"MGMT:TR%s/CAPWAP"|format(tr)}}-%}
!---- [TR_VLS]        > {%set x=vlan.update(tr_vls)-%}{#                                       #}{{tr_vls|dictsort(false)|join(indent)}}
!
!-- Create VLAN_LIST from [VLAN].
{%set vlan_list=[]-%}
{%for k,v in vlan|dictsort(false,"value")-%}
{%  set vlan_list=vlan_list.append(v.id) -%}
{%endfor-%}
!---- VLAN_LIST       > {%set vlan_list=vlan_list|join(",")%}{#                                #}{{vlan_list}}
!
...
Expand for Full Template Example
{%-set tmpl={"file":"tmpl_test.j2",
             "rev":"8"}-%}
{%-set indent="\n!"~" "*23-%}
!-- --------------------------------------------------------------------------- --!
!--                          ***IMPORTANT (Example)***                          --!
!--          Hostname (keystore_id in CSV) has strict formatting rules.         --!
!--                                                                             --!
!--  * TR and switch/stack numbers must each be two digits (use leading '0').   --!
!--                           * TRs are numbered 1-9.                           --!
!--     * Any TR can only accommodate up to 9 switches/stacks; i.e. 01-09.      --!
!--      * Any stack is only intended to have 4 switches; i.e. idarray_1-4.     --!
!--                                                                             --!
!--                     Example for TR-4, switch/stack 2...                     --!
!--                                                                             --!
!--      Access Switch      |       TR Number        |   Switch/Stack Number    --!
!--   ----------------------|------------------------|----------------------    --!
!--           ASW           |          TR04          |            02            --!
!--                                                                             --!
!--                        Example hostname: ASW-TR04-02                        --!
!-- --------------------------------------------------------------------------- --!
!
!-- Variables (keys) parsed from CSV keystore.
!
!---- KEYSTORE_ID     > {{keystore_id|default("N/A")}}{# Used to associate the switch to a template (serial number).#}
!---- ASSOCIATION     > {{association|default("N/A")}}{# Switch template associated with KEYSTORE_ID.#}
!---- IDARRAY_1       > {{idarray_1|default("N/A")}}{#   Switch 1 serial number.#}
!---- IDARRAY_2       > {{idarray_2|default("N/A")}}{#   Switch 2 serial number.#}
!---- IDARRAY_3       > {{idarray_3|default("N/A")}}{#   Switch 3 serial number.#}
!---- IDARRAY_4       > {{idarray_4|default("N/A")}}{#   Switch 4 serial number.#}
!---- IDARRAY         > {{idarray|default("N/A")}}{#     All switch serial numbers.#}
!
!---------------------------------------------------------------- VARIABLES: STATIC
!
!-- Variables (keys) statically defined within the template.
!-- Variables with multiple values are JSON formatted.
!{#   VARIABLE        > {%set VARIABLE="VALUE"%}                    <<< set | print >>>          {{VARIABLE}}   #}
!---- TFTP_ADDR       > {%set tftp_addr="172.17.251.251"%}{#                                   #}{{tftp_addr}}
!---- DOMAIN          > {%set domain="exmaple.local"%}{#                                       #}{{domain}}
!---- STP_PRIORITY    > {%set stp_priority="28672"%}{#                                         #}{{stp_priority}}
!---- ENABLE_SEC      > {%set enable_sec="enable-secret"%}{#                                   #}{{enable_sec}}
!---- TACACS_KEY      > {%set tacacs_key="tacacs-secret"%}{#                                   #}{{tacacs_key}}
!---- RADIUS_KEY      > {%set radius_key="radius-secret"%}{#                                   #}{{radius_key}}
!---- NTP_KEY         > {%set ntp_key="ntp-secret"%}{#                                         #}{{ntp_key}}
!---- SYSLOG_ADDR     > {%set syslog_addr="10.255.1.1"%}{#                                     #}{{syslog_addr}}
!---- [IMAGE]         > {%set image={"bin":"cat3k_caa-universalk9.16.03.07.SPA.bin",
                                     "ver":"16.3.7"}%}{#                                       #}{{image|dictsort(false)|join(indent)}}
!---- [CREDS]         > {%set creds={"admin":"admin-secret",
                                     "eem_svc":"eem-secret"}%}{#                               #}{{creds|dictsort(false)|join(indent)}}
!---- [NTP_ADDR]      > {%set ntp_addr={"pri":{"addr":"10.255.1.11","pref":"prefer"},
                                        "sec":{"addr":"10.255.1.12","pref":""}}%}{#            #}{{ntp_addr|dictsort(false)|join(indent)}}
!---- [ISE_ADDR]      > {%set ise_addr={"01":"10.255.1.21",
                                        "02":"10.255.1.22",
                                        "03":"10.255.1.23"}%}{#                                #}{{ise_addr|dictsort(false)|join(indent)}}
!---- [SNMPV3]        > {%set snmpv3={"grp":"SNMP_ASW",
                                      "user":"snmp_asw",
                                      "auth":"snmp-auth",
                                      "priv":"smp-priv"}%}{#                                   #}{{snmpv3|dictsort(false)|join(indent)}}
!---- [VLAN]          > {%set vlan={"native":{"id":"999","name":"NATIVE"},
                                    "global":{"id":"998","name":"GLOBAL"}}%}{#                 #}{{vlan|dictsort(false,"value")|join(indent)}}
!
!---------------------------------------------------------------- VARIABLES: DYNAMIC
!
!-- Variables (keys) with dynamic values based on variables paresed from the CSV keystore.
!
!-- Copy HOSTNAME from KEYSTORE_ID, get SW_COUNT and SERIALS from IDARRAY.
!---- HOSTNAME        > {%set hostname=keystore_id%}{#                                         #}{{hostname}}
!---- SW_COUNT        > {%set sw_count=idarray|count%}{#                                       #}{{sw_count}}
!---- SERIALS         > {%set serials=idarray%}{#                                              #}{{serials}}
!
!-- Create range lists for all access interfaces and all provisioning interfaces based on IDARRAY.
{%set gi_ints,te_ints,prov_ints=[],[],[]-%}
{%for sw in idarray-%}
{%  set i=loop.index-%}
{%  set gi_ints,te_ints,prov_ints=
        gi_ints.append("Gi%s/0/1-36"|format(i)),
        te_ints.append("Te%s/0/37-47"|format(i)),
        prov_ints.append("Te%s/0/48"|format(i))-%}
{%endfor-%}
!---- GI_INTS         > {%set gi_ints=gi_ints|join(",")%}{#                                    #}{{gi_ints}}
!---- TE_INTS         > {%set te_ints=te_ints|join(",")%}{#                                    #}{{te_ints}}
!---- PROV_INTS       > {%set prov_ints=prov_ints|join(",")%}{#                                #}{{prov_ints}}
!
!-- Split hostname to get TR (numeric only) and SW.
!---- TR              > {%set tr=(hostname.split("-")[1])[2:]%}{#                              #}{{tr}}
!---- TRN             > {%set trn=tr[1:]%}{#                                                   #}{{trn}}
!---- SW              > {%set sw=(hostname.split("-")[2])%}{#                                  #}{{sw}}
!
!-- Determine PO_NUM from TR (second digit) and (int of) SW; also used as last octet of MGMT IP.
!---- PO_NUM          > {%set po_num="1%s%s"|format(tr[1:],sw|int)%}{#                         #}{{po_num}}
!
!-- Determine MGMT subnet; IP from subnet and PO_NUM; append a name for the default route.
{%set mgmt={"net":"10.254.%s"|format(trn)}-%}
{%set x=mgmt.update({"mask":"255.255.255.0"})-%}
{%set x=mgmt.update({"ip":"%s.%s"|format(mgmt.net,po_num)})-%}
{%set x=mgmt.update({"rt_name":"Default>MGMT:DIST-01"})-%}
!---- [MGMT]          > {#                                                                     #}{{mgmt|dictsort(false,"value")|join(indent)}}
!
!-- Determine TR_VLS from TR, append them to the (static) [VLAN] dictionary.
{%set tr_vls={"grt_thin":{"id":"1%s01"|format(trn),"name":"GRT:TR%s/THIN-CLIENTS"|format(tr)},
              "grt_thick":{"id":"1%s02"|format(trn),"name":"GRT:TR%s/THICK-CLIENTS"|format(tr)},
              "grt_voice":{"id":"1%s03"|format(trn),"name":"GRT:TR%s/VOICE"|format(tr)},
              "grt_crit":{"id":"1%s99"|format(trn),"name":"GRT:TR%s/CRIT-AUTH"|format(tr)},
              "mgmt_sw":{"id":"2%s01"|format(trn),"name":"MGMT:TR%s/SW-WLC"|format(tr)},
              "mgmt_ap":{"id":"2%s02"|format(trn),"name":"MGMT:TR%s/CAPWAP"|format(tr)}}-%}
!---- [TR_VLS]        > {%set x=vlan.update(tr_vls)-%}{#                                         #}{{tr_vls|dictsort(false)|join(indent)}}
!
!-- Create VLAN_LIST from [VLAN].
{%set vlan_list=[]-%}
{%for k,v in vlan|dictsort(false,"value")-%}
{%  set vlan_list=vlan_list.append(v.id) -%}
{%endfor-%}
!---- VLAN_LIST       > {%set vlan_list=vlan_list|join(",")%}{#                                #}{{vlan_list}}
!
!---------------------------------------------------------------- CONFIG: BASE
!
service timestamps debug datetime msec localtime show-timezone year
service timestamps log datetime msec localtime show-timezone year
service password-encryption
!
hostname {{hostname}}
!
clock timezone PST -8 0
clock summer-time PDT recurring
!
{%for sw in idarray-%}
{%  set i=loop.index-%}
switch {{i}} provision ws-c3850-12x48u
{%endfor-%}
!
no ip domain lookup
ip domain-name {{domain}}
!
logging buffered 409600 debugging
logging console debug
!
!ip ssh logging events
ip ssh version 2
!
spanning-tree mode rapid-pvst
spanning-tree vlan 1-4094 priority {{stp_priority}}
!
vtp mode transparent
!
file prompt quiet
!
ip dhcp snooping vlan 1-4094
no ip dhcp snooping information option
ip dhcp snooping
!
udld aggressive
!
macro name int-auth
 switchport
 switchport mode access
 switchport nonegotiate
 switchport voice vlan {{vlan.grt_voice.id}}
 device-tracking attach-policy IPDT
 authentication event server dead action reinitialize vlan {{vlan.grt_crit.id}}
 authentication event server dead action authorize voice
 authentication event server alive action reinitialize
 authentication host-mode multi-auth
 authentication order mab dot1x
 authentication priority dot1x mab
 authentication port-control auto
 authentication violation restrict
 mab
 dot1x pae authenticator
 dot1x timeout tx-period 7
 dot1x max-reauth-req 3
 spanning-tree portfast
 ip dhcp snooping limit rate 25
 no shutdown
 no downshift
 no macro description
 @
!
macro name int-noauth
 # Unauthenticated interface macro, variables not mandatory.
 # > Assign appropriate access vlan using $VL
 #
 # Example config for a thin client in VL-1201;
 # (config)# interface gi#/#/#
 # (config-if)# macro apply int-noauth $VL 1201
 #
 switchport
 switchport access vlan $VL
 switchport mode access
 switchport nonegotiate
 device-tracking attach-policy IPDT
 spanning-tree portfast
 ip dhcp snooping limit rate 25
 no shutdown
 no downshift
 no macro description
 @
!
banner login @
UNAUTHORIZED ACCESS TO THIS DEVICE IS PROHIBITED
You must have explicit, authorized permission to access or configure this device.
Unauthorized attempts and actions to access or use this system may result in civil and/or
criminal penalties.
All activities performed on this device are logged and monitored.
-----------------------------------------------------------------------------------------
@
!
alias exec intstat show int status
alias exec ipint show ip int br | e una
alias exec ver show ver | i Soft|file
alias exec vlbr show vlan br | i active
alias exec rundiff show arch config diff
alias exec auth show auth sess int gi
alias exec radstat sh aaa server | i RADIUS|Platform State
alias exec int-auth event man run int-auth
alias exec int-noauth event man run int-noauth
!
!---------------------------------------------------------------- CONFIG: AUTH
!
enable secret 0 {{enable_sec}}
!
{%for k,v in creds|dictsort(false)-%}
username {{k}} privilege 15 secret 0 {{v}}
{%endfor-%}
!
aaa new-model
!
!-- TACACS servers and group config.
!
{%-for k,v in ise_addr|dictsort(false)%}
tacacs server ISE{{k}}-T
 address ipv4 {{v}}
 key 0 {{tacacs_key}}
 single-connection
{%endfor-%}
!
aaa group server tacacs+ ISE-T
{%-for k,v in ise_addr|dictsort(false)%}
 server name ISE{{k}}-T
{%-endfor%}
 ip tacacs source-interface Vlan{{vlan.mgmt_sw.id}}
!
!-- RADIUS servers and group config.
!
{%-for k,v in ise_addr|dictsort(false)%}
radius server ISE{{k}}-R
 address ipv4 {{v}} auth-port 1812 acct-port 1813
 automate-tester username test-user ignore-acct-port probe-on
 key 0 {{radius_key}}
{%endfor-%}
!
aaa group server radius ISE-R
{%-for k,v in ise_addr|dictsort(false)%}
 server name ISE{{k}}-R
{%-endfor%}
 ip radius source-interface Vlan{{vlan.mgmt_sw.id}}
 load-balance method least-outstanding batch-size 5
!
radius-server attribute 6 on-for-login-auth
radius-server attribute 8 include-in-access-req
radius-server attribute 25 access-request include
radius-server attribute 31 mac format ietf upper-case
radius-server attribute 31 send nas-port-detail mac-only
radius-server dead-criteria time 10 tries 3
radius-server deadtime 5
!
aaa server radius dynamic-author
{%-for k,v in ise_addr|dictsort(false)%}
 client {{v}} server-key 0 {{radius_key}}
{%-endfor%}
!
!-- Default authentication method list to use TACACS, fallback to local (ssh).
 aaa authentication login default group ISE-T local
!
!-- Authentication method list to use local creds only (console).
 aaa authentication login CONSOLE local
!
!-- Required to authorize console to exec mode (console).
 aaa authorization console
!
!-- Default authorization exec and commands 15 method lists to use local or TACACS (console, ssh).
!-- 'local' must be defined here first for console authorization to work.
 aaa authorization exec default local group ISE-T if-authenticated 
 aaa authorization commands 15 default local group ISE-T if-authenticated
!
!-- Accounting commands to send all exec start-stop, and priv 15 commands to TACACS (console, ssh).
 aaa accounting exec default start-stop group ISE-T
 aaa accounting commands 15 default start-stop group ISE-T
!
!-- AAA commands for dot1x/mab.
aaa authentication dot1x default group ISE-R
aaa authorization network default group ISE-R
aaa accounting dot1x default start-stop group ISE-R
aaa accounting update newinfo periodic 2880
!
!---------------------------------------------------------------- CONFIG: DOT1X/ISE-MISC
!
dot1x system-auth-control
dot1x critical eapol
!
authentication mac-move permit
!
device-sensor filter-list dhcp list DHCP-ISE
 option name host-name
 option name requested-address
 option name parameter-request-list
 option name class-identifier
 option name client-identifier
!
device-sensor filter-list cdp list CDP-ISE
 tlv name device-name
 tlv name address-type
 tlv name capabilities-type
 tlv name version-type
 tlv name platform-type
!
device-sensor filter-spec dhcp include list DHCP-ISE
device-sensor filter-spec cdp include list CDP-ISE
device-sensor accounting
device-sensor notify all-changes
!
!---------------------------------------------------------------- CONFIG: VLANS
!
{%-for k,v in vlan|dictsort(false,"value")%}
vlan {{v.id}}
 name {{v.name}}
{%endfor-%}
!
!---------------------------------------------------------------- CONFIG: INTERFACES
!
interface GigabitEthernet0/0
 shutdown
!
!-- Configure misc interfaces for all stack members.
!
interface range {{prov_ints}}
 description TMP//PROVISION
 switchport
 switchport mode access
 switchport nonegotiate
 switchport access vlan 1
 spanning-tree portfast
 ip dhcp snooping trust
 no shutdown
!
{%-for sw in idarray%}
{%-set i=loop.index%}
interface range Gi{{i}}/1/1-2
 shutdown
!
interface range Te{{i}}/1/3-4
 channel-group 1 mode active
 no shutdown
!
interface range Gi{{i}}/1/3-4,Te{{i}}/1/1-2,Te{{i}}/1/5-8,Fo{{i}}/1/1-2
 description ***NULL//DoNotConfigure
 shutdown
{%endfor-%}
!
interface Port-channel1
 description DIST-01:Po{{po_num}}
 switchport trunk native vlan 999
 switchport trunk allowed vlan {{vlan_list}}
 switchport mode trunk
 switchport nonegotiate
 ip dhcp snooping trust
 no shutdown
!
!---------------------------------------------------------------- CONFIG: IP/SOURCE/NMS
!
interface Vlan1
 desc TMP//PROVISION
 ip address dhcp
 no shutdown
!
interface Vlan{{vlan.mgmt_sw.id}}
 description {{vlan.mgmt_sw.name}}
 no ip redirects
 no ip proxy-arp
 ip address {{mgmt.ip}} {{mgmt.mask}}
 no shutdown
!
ip tftp source-interface Vlan{{vlan.mgmt_sw.id}}
ip tftp blocksize 8192
!
ip route 0.0.0.0 0.0.0.0 {{mgmt.net}}.1 name {{mgmt.rt_name}}
!
logging source-interface Vlan{{vlan.mgmt_sw.id}}
logging host {{syslog_addr}}
!
ntp authentication-key 1 md5 {{ntp_key}}
ntp authenticate
ntp trusted-key 1
{%-for k,v in ntp_addr|dictsort(false)%}
ntp server {{v.addr}} key 1 {{v.pref}} source vlan{{vlan.mgmt_sw.id}}
{%-endfor%}
!
snmp-server group {{snmpv3.grp}} v3 priv
snmp-server user {{snmpv3.user}} {{snmpv3.grp}} v3 auth sha {{snmpv3.auth}} priv aes 256 {{snmpv3.priv}}
snmp-server location Provisioned with switch template: {{"%s (rev: %s)"|format(tmpl.file,tmpl.rev)}}
snmp-server contact Provisioned hostname ({{hostname}}) and stack members: {{serials|join("-")}}
!
line console 0
 login authentication CONSOLE
 logging synchronous
 exec-timeout 15 0
line vty 0 15
 logging synchronous
 exec-timeout 30 0
 transport input ssh
!
!---------------------------------------------------------------- CONFIG: EEM
!
event manager session cli username {{creds.keys().1}}
!
event manager applet int-auth
 event none maxrun 60
 action 00.00 puts "## Enter interface to configure with authentication; e.g. gi1/0/1, te1/0/37"
 action 00.01 gets intf
 action 00.02 cli command "enable"
 action 00.03 cli command "show int $intf status"
 action 00.04 regexp "Invalid" "$_cli_result"
 action 00.05 if $_regexp_result eq "1"
 action 00.06  puts "## Invalid interface ($intf), verify prefix; i.e. 'gi' or 'te'."
 action 00.07  exit
 action 00.08 end
 action 00.10 regexp "connected\ +([0-9]+)" "$_cli_result" match currvl
 action 00.11 if $_regexp_result eq "1"
 action 00.12  puts "## Interface $intf has a device connected in VLAN $currvl, continue (y|n)..."
 action 00.13  gets cont
 action 00.14  if $cont ne "y"
 action 00.15   exit
 action 00.16  end
 action 00.17 end
 action 01.00 cli command "conf t"
 action 01.01 cli command "default int $intf"
 action 01.02 cli command "int $intf"
 action 01.03 cli command "macro apply int-auth"
 action 01.04 cli command "end"
 action 01.05 cli command "write mem" pattern "confirm|#"
 action 01.06 cli command ""
 action 01.10 puts "## Interface $intf configured with authentication."
!
event manager applet int-noauth
 event none maxrun 60
 action 00.00 puts "## Enter interface to configure without authentication; e.g. gi1/0/1, te1/0/37"
 action 00.01 gets intf
 action 00.02 cli command "enable"
 action 00.03 cli command "show int $intf status"
 action 00.04 regexp "Invalid" "$_cli_result"
 action 00.05 if $_regexp_result eq "1"
 action 00.06  puts "## Invalid interface ($intf), verify prefix; i.e. 'gi' or 'te'."
 action 00.07  exit
 action 00.08 end
 action 00.10 regexp "connected\ +([0-9]+)" "$_cli_result" match currvl
 action 00.11 if $_regexp_result eq "1"
 action 00.12  puts "## Interface $intf has a device connected in VLAN $currvl, continue (y|n)..."
 action 00.13  gets cont
 action 00.14  if $cont ne "y"
 action 00.15   exit
 action 00.16  end
 action 00.17 end
 action 00.20 puts "## Enter access VLAN ID; e.g. 800"
 action 00.21 gets vl
 action 00.22 cli command "show vlan id $vl"
 action 00.23 regexp "not found" "$_cli_result"
 action 00.24 if $_regexp_result eq "1"
 action 00.25  puts "## VLAN $vl is not configured on this switch."
 action 00.26  exit
 action 00.27 end
 action 01.00 cli command "conf t"
 action 01.01 cli command "default int $intf"
 action 01.02 cli command "int $intf"
 action 01.03 cli command "macro apply int-noauth \$VL $vl"
 action 01.04 cli command "end"
 action 01.05 cli command "write mem" pattern "confirm|#"
 action 01.06 cli command ""
 action 01.10 puts "## Interface $intf configured without authentication in VL-$vl, please describe accordingly."
!
!---------------------------------------------------------------- CONFIG: EEM-TEMP
!
event manager environment q "
!
event manager applet post_ztp_1
!-- Renumber/prioritize switches in stack, write mem, and then download->upgrade/reload (if needed).
 event syslog occurs 1 pattern "Configured from tftp://{{tftp_addr}}" maxrun 960
 action 00.00 syslog msg "  ## Configuration received via TFTP, run 'post_ztp_1' EEM applet in 120s."
 action 00.01 wait 120
 action 00.03 cli command "enable"
 action 00.05 cli command "show mod | i ^.[1-9]"
 action 00.06 set stack "$_cli_result"
 action 00.07 syslog msg "  ## Checking all switches' version and stack membership, adjusting where necessary.\n  ## Current order;\n$stack"
 action 00.08 set error_list ""
 action 00.09 set change_list ""
 action 00.10 set upgrade_list ""
 {%for sw in idarray%}
 {%-  set i=loop.index%}
 action 0{{i}}.00 set sw_num "{{i}}"
 action 0{{i}}.01 set pri "16"
 action 0{{i}}.02 decrement pri {{i}}
 action 0{{i}}.03 regexp "{{sw}}" "$stack"
 action 0{{i}}.04 if $_regexp_result ne "1"
 action 0{{i}}.05  syslog msg "\n  ## {{sw}} (Sw-{{i}} serial) not found in the stack, check 'show mod' output."
 action 0{{i}}.06  append error_list "\n  ##  {{sw}} is allocated (idarray_{{i}}) but was not found in the stack."
 action 0{{i}}.07 else
 action 0{{i}}.08  set i "0"
 action 0{{i}}.09  foreach line "$stack" "\n"
 action 0{{i}}.10   increment i
 action 0{{i}}.11   if $i le "{{sw_count}}"
 action 0{{i}}.12    string trim "$line"
 action 0{{i}}.13    set line "$_string_result"
 action 0{{i}}.14    regexp "{{sw}}" "$line"
 action 0{{i}}.15    if $_regexp_result eq "1"
 action 0{{i}}.16     regexp "([0-9\.A-Z]+$)" "$line" curr_ver
 action 0{{i}}.17     if $curr_ver ne "{{image.ver}}"
 action 0{{i}}.18      append upgrade_list "{{i}}"
 action 0{{i}}.19     end
 action 0{{i}}.20     cli command "switch $i priority $pri" pattern "continue|#"
 action 0{{i}}.21     cli command "y"
 action 0{{i}}.22     if $i eq $sw_num
 action 0{{i}}.23      append change_list "\n  ##  {{sw}} (Priority: $pri // Numbered:       $sw_num  // Version: $curr_ver)"
 action 0{{i}}.24     else
 action 0{{i}}.25      cli command "switch $i renumber $sw_num" pattern "continue|#"
 action 0{{i}}.26      cli command "y"
 action 0{{i}}.27      append change_list "\n  ##  {{sw}} (Priority: $pri // Renumbered: $i > $sw_num* // Version: $curr_ver)"
 action 0{{i}}.28     end
 action 0{{i}}.29     break
 action 0{{i}}.30    end
 action 0{{i}}.31   end
 action 0{{i}}.32  end
 action 0{{i}}.33 end
 {%endfor%}
 action 10.00 wait 5
 action 10.01 if $error_list ne ""
 action 10.02  syslog msg "\n  ## The following errors occurred; $error_list"
 action 10.03 end
 action 10.04 syslog msg "\n  ## Switches below have been assigned a priority and renumbered* as needed; $change_list"
 action 13.00 cli command "conf t"
 action 13.01 cli command "no event man app post_ztp_1"
 action 14.00 if $upgrade_list eq ""
 action 14.01  syslog msg "  ## All switches are running version {{image.ver}}, skipping download->upgrade/reload; finalizing config in 20s."
 action 14.02  cli command "no event man env q"
 action 14.03  cli command "event man env ztp_upgraded no"
 action 14.04  cli command " event man app post_ztp_2"
 action 14.05  cli command " event timer countdown time 20 maxrun 480"
 action 14.06  cli command " no action 00.00"
 action 14.07  cli command " no action 00.01"
 action 14.08  cli command " end"
 action 14.09  cli command "write mem" pattern "confirm|#"
 action 14.10  cli command ""
 action 14.11 else
 action 14.12  syslog msg "  ## One or more switches require an upgrade to version {{image.ver}} ($upgrade_list); proceeding with download->upgrade/reload."
 action 14.13  cli command "event man env ztp_upgraded yes"
 action 14.14  cli command "event man app post_ztp_2"
 action 14.15  cli command " event syslog occurs 1 pattern $q%IOSXE_REDUNDANCY-6-PEER$q maxrun 630"
 action 14.16  cli command " no event man env q"
 action 14.17  cli command "end"
 action 14.18  cli command "write mem" pattern "confirm|#"
 action 14.19  cli command ""
 action 14.20  syslog msg "  ## (Standby) Downloading image..."
 action 14.21  cli command "copy tftp://{{tftp_addr}}/{{image.bin}} flash:"
 action 14.22  syslog msg "  ## (Standby) Image downloaded, upgrading..."
 action 14.23  cli command "software install file flash:{{image.bin}} new force" pattern "proceed|#"
 action 14.24  syslog msg "  ## Upgrade complete, rebooting."
 action 14.25  cli command "y"
 action 14.26 end
!
event manager applet post_ztp_2
!-- Apply final configs, push running config via TFTP, clean up VL-1 and Te#/0/48 configs, write mem, and then perform package clean.
 event none
 action 00.00 syslog msg "  ## Switch reloaded on new image, running 'post_ztp_2' EEM applet in 150s."
 action 00.01 wait 150
 action 00.02 syslog msg "  ## (Standby) Applying global configs ignored by smart-install and generating crypto key..."
 action 00.03 cli command "enable"
 action 00.04 set upgr "$ztp_upgraded"
 action 01.00 cli command "conf t"
 action 01.01 cli command "no event man env ztp_upgraded"
 action 01.02 cli command "no event man app post_ztp_2"
 action 01.04 cli command "no vstack"
 action 01.05 cli command "no ip http ser"
 action 01.06 cli command "no ip http secure-s"
 action 01.07 cli command "no ip http authen"
 action 01.08 cli command "no coap http enable"
 action 01.09 cli command "device-tracking policy IPDT"
 action 01.10 cli command " no protocol udp"
 action 01.11 cli command " tracking enable"
 action 01.12 cli command "cry key gen rsa mod 2048"
 action 02.00 syslog msg "  ## (Standby) Applying `int-auth` macro config to ports 1-48 on all stack members..."
 action 02.01 cli command "int range {{gi_ints}}"
 action 02.02 cli command " macro apply int-auth"
 action 02.03 cli command " no macro desc"
 action 02.04 cli command "int range {{te_ints}}"
 action 02.05 cli command " macro apply int-auth"
 action 02.06 cli command " no macro desc"
 action 03.00 syslog msg "  ## Pushing config to the following location: tftp://{{tftp_addr}}/provisioned/CAS/{{cfg_file}}"
 action 03.01 cli command "do copy run tftp://{{tftp_addr}}/provisioned/CAS/{{cfg_file}}"
 action 04.00 syslog msg "  ## Disabling VL-1 SVI, updating Te#/0/48 configs, and writing startup config."
 action 04.01 cli command "int vl 1"
 action 04.02 cli command " no desc"
 action 04.03 cli command " no ip addr"
 action 04.04 cli command " shut"
 action 04.05 cli command "default int range {{prov_ints}}"
 action 04.06 cli command "int range {{prov_ints}}"
 action 04.07 cli command " macro apply int-auth"
 action 04.08 cli command " no macro desc"
 action 04.09 cli command "end"
 action 04.10 cli command "write mem" pattern "confirm|#"
 action 04.11 cli command ""
 action 05.00 if $upgr eq "yes"
 action 05.01  syslog msg "  ## (Standby) ZTP upgrade detected, performing software package clean..."
 action 05.02  cli command "req plat soft pack clean sw all" pattern "proceed|#"
 action 05.03  cli command "y"
 action 05.04  syslog msg "  ## Unused .bin or .pkg files from previous version(s) have been deleted."
 action 05.05 else
 action 05.06  syslog msg "  ## ZTP upgrade not detected, skipping software package clean."
 action 05.07 end
 action 05.08 syslog msg "  ## Start-up config written, ({{hostname}}) is ready for deployment, OK to power off."
!
end

Merged Config

##############################
!-- --------------------------------------------------------------------------- --!
!--                          ***IMPORTANT (Example)***                          --!
!--          Hostname (keystore_id in CSV) has strict formatting rules.         --!
!--                                                                             --!
!--  * TR and switch/stack numbers must each be two digits (use leading '0').   --!
!--                           * TRs are numbered 1-9.                           --!
!--     * Any TR can only accommodate up to 9 switches/stacks; i.e. 01-09.      --!
!--      * Any stack is only intended to have 4 switches; i.e. idarray_1-4.     --!
!--                                                                             --!
!--                     Example for TR-4, switch/stack 2...                     --!
!--                                                                             --!
!--      Access Switch      |       TR Number        |   Switch/Stack Number    --!
!--   ----------------------|------------------------|----------------------    --!
!--           ASW           |          TR04          |            02            --!
!--                                                                             --!
!--                        Example hostname: ASW-TR04-02                        --!
!-- --------------------------------------------------------------------------- --!
!
!-- Variables (keys) parsed from CSV keystore.
!
!---- KEYSTORE_ID     > ASW-TR04-02
!---- ASSOCIATION     > ASW
!---- IDARRAY_1       > FOC11111111
!---- IDARRAY_2       > FOC22222222
!---- IDARRAY_3       > FOC33333333
!---- IDARRAY_4       > FOC44444444
!---- IDARRAY         > ['FOC11111111', 'FOC22222222', 'FOC33333333', 'FOC44444444']
!
!---------------------------------------------------------------- VARIABLES: STATIC
!
!-- Variables (keys) statically defined within the template.
!-- Variables with multiple values are JSON formatted.
!
!---- TFTP_ADDR       > 172.17.251.251
!---- DOMAIN          > exmaple.local
!---- STP_PRIORITY    > 28672
!---- ENABLE_SEC      > enable-secret
!---- TACACS_KEY      > tacacs-secret
!---- RADIUS_KEY      > radius-secret
!---- NTP_KEY         > ntp-secret
!---- SYSLOG_ADDR     > 10.255.1.1
!---- [IMAGE]         > ('bin', 'cat3k_caa-universalk9.16.03.07.SPA.bin')
!                       ('ver', '16.3.7')
!---- [CREDS]         > ('admin', 'admin-secret')
!                       ('eem_svc', 'eem-secret')
!---- [NTP_ADDR]      > ('pri', {'pref': 'prefer', 'addr': '10.255.1.11'})
!                       ('sec', {'pref': '', 'addr': '10.255.1.12'})
!---- [ISE_ADDR]      > ('01', '10.255.1.21')
!                       ('02', '10.255.1.22')
!                       ('03', '10.255.1.23')
!---- [SNMPV3]        > ('auth', 'snmp-auth')
!                       ('grp', 'SNMP_ASW')
!                       ('priv', 'smp-priv')
!                       ('user', 'snmp_asw')
!---- [VLAN]          > ('global', {'id': '998', 'name': 'GLOBAL'})
!                       ('native', {'id': '999', 'name': 'NATIVE'})
!
!---------------------------------------------------------------- VARIABLES: DYNAMIC
!
!-- Variables (keys) with dynamic values based on variables paresed from the CSV keystore.
!
!-- Copy HOSTNAME from KEYSTORE_ID, get SW_COUNT and SERIALS from IDARRAY.
!---- HOSTNAME        > ASW-TR04-02
!---- SW_COUNT        > 4
!---- SERIALS         > ['FOC11111111', 'FOC22222222', 'FOC33333333', 'FOC44444444']
!
!-- Join HOSTNAME and SERIALS to make CFG_FILE; used by EEM applet for TFTP config push
!---- CFG_FILE        > ASW-TR04-02_FOC11111111-FOC22222222-FOC33333333-FOC44444444.cfg
!
!-- Create range lists for all access interfaces and all provisioning interfaces based on IDARRAY.
!---- GI_INTS         > Gi1/0/1-36,Gi2/0/1-36,Gi3/0/1-36,Gi4/0/1-36
!---- TE_INTS         > Te1/0/37-47,Te2/0/37-47,Te3/0/37-47,Te4/0/37-47
!---- PROV_INTS       > Te1/0/48,Te2/0/48,Te3/0/48,Te4/0/48
!
!-- Split hostname to get TR (numeric only) and SW.
!---- TR              > 04
!---- TRN             > 4
!---- SW              > 02
!
!-- Determine PO_NUM from TR (second digit) and (int of) SW; also used as last octet of MGMT IP.
!---- PO_NUM          > 142
!
!-- Determine MGMT subnet; IP from subnet and PO_NUM; append a name for the default route.
!---- [MGMT]          > ('net', u'10.254.4')
!                       ('ip', u'10.254.4.142')
!                       ('mask', '255.255.255.0')
!                       ('rt_name', 'Default>MGMT:DIST-01')
!
!-- Determine TR_VLS from TR, append them to the (static) [VLAN] dictionary.
!---- [TR_VLS]        > ('grt_crit', {'id': u'1499', 'name': u'GRT:TR04/CRIT-AUTH'})
!                       ('grt_thick', {'id': u'1402', 'name': u'GRT:TR04/THICK-CLIENTS'})
!                       ('grt_thin', {'id': u'1401', 'name': u'GRT:TR04/THIN-CLIENTS'})
!                       ('grt_voice', {'id': u'1403', 'name': u'GRT:TR04/VOICE'})
!                       ('mgmt_ap', {'id': u'2402', 'name': u'MGMT:TR04/CAPWAP'})
!                       ('mgmt_sw', {'id': u'2401', 'name': u'MGMT:TR04/SW-WLC'})
!
!-- Create VLAN_LIST from [VLAN].
!---- VLAN_LIST       > 1401,1402,1403,1499,2401,2402,998,999
!
...
Expand for Full Merged Config Example
Expand for Merge Request & Resulting Logs
# ztp request merge-test ASW-TR04-02 
2019-03-07 23:49:00:   cfact.get_keystore_id: Checking Keystores and IDArrays for (ASW-TR04-02)

2019-03-07 23:49:00:   cfact.get_keystore_id: ID (ASW-TR04-02) resolved directly to an external-keystore

2019-03-07 23:49:00:   cfact.get_template: Looking up association for identity (ASW-TR04-02)

2019-03-07 23:49:00:   cfact.get_template: Found associated template (ASW) in an external keystore

2019-03-07 23:49:00:   cfact.get_template: Template (ASW) exists as an external-template. Returning

2019-03-07 23:49:00:   cfact.pull_keystore_values: Inserting IDArray keys

2019-03-07 23:49:00:   cfact.merge_final_config: Merging with values:
{
    "idarray_1": "FOC11111111", 
    "idarray_4": "FOC44444444", 
    "idarray_2": "FOC22222222", 
    "idarray_3": "FOC33333333", 
    "keystore_id": "ASW-TR04-02", 
    "idarray": [
        "FOC11111111", 
        "FOC22222222", 
        "FOC33333333", 
        "FOC44444444"
    ], 
    "association": "ASW"
}
##############################
!-- --------------------------------------------------------------------------- --!
!--                          ***IMPORTANT (Example)***                          --!
!--          Hostname (keystore_id in CSV) has strict formatting rules.         --!
!--                                                                             --!
!--  * TR and switch/stack numbers must each be two digits (use leading '0').   --!
!--                           * TRs are numbered 1-9.                           --!
!--     * Any TR can only accommodate up to 9 switches/stacks; i.e. 01-09.      --!
!--      * Any stack is only intended to have 4 switches; i.e. idarray_1-4.     --!
!--                                                                             --!
!--                     Example for TR-4, switch/stack 2...                     --!
!--                                                                             --!
!--      Access Switch      |       TR Number        |   Switch/Stack Number    --!
!--   ----------------------|------------------------|----------------------    --!
!--           ASW           |          TR04          |            02            --!
!--                                                                             --!
!--                        Example hostname: ASW-TR04-02                        --!
!-- --------------------------------------------------------------------------- --!
!
!-- Variables (keys) parsed from CSV keystore.
!
!---- KEYSTORE_ID     > ASW-TR04-02
!---- ASSOCIATION     > ASW
!---- IDARRAY_1       > FOC11111111
!---- IDARRAY_2       > FOC22222222
!---- IDARRAY_3       > FOC33333333
!---- IDARRAY_4       > FOC44444444
!---- IDARRAY         > ['FOC11111111', 'FOC22222222', 'FOC33333333', 'FOC44444444']
!
!---------------------------------------------------------------- VARIABLES: STATIC
!
!-- Variables (keys) statically defined within the template.
!-- Variables with multiple values are JSON formatted.
!
!---- TFTP_ADDR       > 172.17.251.251
!---- DOMAIN          > exmaple.local
!---- STP_PRIORITY    > 28672
!---- ENABLE_SEC      > enable-secret
!---- TACACS_KEY      > tacacs-secret
!---- RADIUS_KEY      > radius-secret
!---- NTP_KEY         > ntp-secret
!---- SYSLOG_ADDR     > 10.255.1.1
!---- [IMAGE]         > ('bin', 'cat3k_caa-universalk9.16.03.07.SPA.bin')
!                       ('ver', '16.3.7')
!---- [CREDS]         > ('admin', 'admin-secret')
!                       ('eem_svc', 'eem-secret')
!---- [NTP_ADDR]      > ('pri', {'pref': 'prefer', 'addr': '10.255.1.11'})
!                       ('sec', {'pref': '', 'addr': '10.255.1.12'})
!---- [ISE_ADDR]      > ('01', '10.255.1.21')
!                       ('02', '10.255.1.22')
!                       ('03', '10.255.1.23')
!---- [SNMPV3]        > ('auth', 'snmp-auth')
!                       ('grp', 'SNMP_ASW')
!                       ('priv', 'smp-priv')
!                       ('user', 'snmp_asw')
!---- [VLAN]          > ('global', {'id': '998', 'name': 'GLOBAL'})
!                       ('native', {'id': '999', 'name': 'NATIVE'})
!
!---------------------------------------------------------------- VARIABLES: DYNAMIC
!
!-- Variables (keys) with dynamic values based on variables paresed from the CSV keystore.
!
!-- Copy HOSTNAME from KEYSTORE_ID, get SW_COUNT and SERIALS from IDARRAY.
!---- HOSTNAME        > ASW-TR04-02
!---- SW_COUNT        > 4
!---- SERIALS         > ['FOC11111111', 'FOC22222222', 'FOC33333333', 'FOC44444444']
!
!-- Join HOSTNAME and SERIALS to make CFG_FILE; used by EEM applet for TFTP config push
!---- CFG_FILE        > ASW-TR04-02_FOC11111111-FOC22222222-FOC33333333-FOC44444444.cfg
!
!-- Create range lists for all access interfaces and all provisioning interfaces based on IDARRAY.
!---- GI_INTS         > Gi1/0/1-36,Gi2/0/1-36,Gi3/0/1-36,Gi4/0/1-36
!---- TE_INTS         > Te1/0/37-47,Te2/0/37-47,Te3/0/37-47,Te4/0/37-47
!---- PROV_INTS       > Te1/0/48,Te2/0/48,Te3/0/48,Te4/0/48
!
!-- Split hostname to get TR (numeric only) and SW.
!---- TR              > 04
!---- TRN             > 4
!---- SW              > 02
!
!-- Determine PO_NUM from TR (second digit) and (int of) SW; also used as last octet of MGMT IP.
!---- PO_NUM          > 142
!
!-- Determine MGMT subnet; IP from subnet and PO_NUM; append a name for the default route.
!---- [MGMT]          > ('net', u'10.254.4')
!                       ('ip', u'10.254.4.142')
!                       ('mask', '255.255.255.0')
!                       ('rt_name', 'Default>MGMT:DIST-01')
!
!-- Determine TR_VLS from TR, append them to the (static) [VLAN] dictionary.
!---- [TR_VLS]        > ('grt_crit', {'id': u'1499', 'name': u'GRT:TR04/CRIT-AUTH'})
!                       ('grt_thick', {'id': u'1402', 'name': u'GRT:TR04/THICK-CLIENTS'})
!                       ('grt_thin', {'id': u'1401', 'name': u'GRT:TR04/THIN-CLIENTS'})
!                       ('grt_voice', {'id': u'1403', 'name': u'GRT:TR04/VOICE'})
!                       ('mgmt_ap', {'id': u'2402', 'name': u'MGMT:TR04/CAPWAP'})
!                       ('mgmt_sw', {'id': u'2401', 'name': u'MGMT:TR04/SW-WLC'})
!
!-- Create VLAN_LIST from [VLAN].
!---- VLAN_LIST       > 1401,1402,1403,1499,2401,2402,998,999
!
!---------------------------------------------------------------- CONFIG: BASE
!
service timestamps debug datetime msec localtime show-timezone year
service timestamps log datetime msec localtime show-timezone year
service password-encryption
!
hostname ASW-TR04-02
!
clock timezone PST -8 0
clock summer-time PDT recurring
!
switch 1 provision ws-c3850-12x48u
switch 2 provision ws-c3850-12x48u
switch 3 provision ws-c3850-12x48u
switch 4 provision ws-c3850-12x48u
!
no ip domain lookup
ip domain-name exmaple.local
!
logging buffered 409600 debugging
logging console debug
!
!ip ssh logging events
ip ssh version 2
!
spanning-tree mode rapid-pvst
spanning-tree vlan 1-4094 priority 28672
!
vtp mode transparent
!
file prompt quiet
!
ip dhcp snooping vlan 1-4094
no ip dhcp snooping information option
ip dhcp snooping
!
udld aggressive
!
macro name int-auth
 switchport
 switchport mode access
 switchport nonegotiate
 switchport voice vlan 1403
 device-tracking attach-policy IPDT
 authentication event server dead action reinitialize vlan 1499
 authentication event server dead action authorize voice
 authentication event server alive action reinitialize
 authentication host-mode multi-auth
 authentication order mab dot1x
 authentication priority dot1x mab
 authentication port-control auto
 authentication violation restrict
 mab
 dot1x pae authenticator
 dot1x timeout tx-period 7
 dot1x max-reauth-req 3
 spanning-tree portfast
 ip dhcp snooping limit rate 25
 no shutdown
 no downshift
 no macro description
 @
!
macro name int-noauth
 # Unauthenticated interface macro, variables not mandatory.
 # > Assign appropriate access vlan using $VL
 #
 # Example config for a thin client in VL-1201;
 # (config)# interface gi#/#/#
 # (config-if)# macro apply int-noauth $VL 1201
 #
 switchport
 switchport access vlan $VL
 switchport mode access
 switchport nonegotiate
 device-tracking attach-policy IPDT
 spanning-tree portfast
 ip dhcp snooping limit rate 25
 no shutdown
 no downshift
 no macro description
 @
!
banner login @
UNAUTHORIZED ACCESS TO THIS DEVICE IS PROHIBITED
You must have explicit, authorized permission to access or configure this device.
Unauthorized attempts and actions to access or use this system may result in civil and/or
criminal penalties.
All activities performed on this device are logged and monitored.
-----------------------------------------------------------------------------------------
@
!
alias exec intstat show int status
alias exec ipint show ip int br | e una
alias exec ver show ver | i Soft|file
alias exec vlbr show vlan br | i active
alias exec rundiff show arch config diff
alias exec auth show auth sess int gi
alias exec radstat sh aaa server | i RADIUS|Platform State
alias exec int-auth event man run int-auth
alias exec int-noauth event man run int-noauth
!
!---------------------------------------------------------------- CONFIG: AUTH
!
enable secret 0 enable-secret
!
username admin privilege 15 secret 0 admin-secret
username eem_svc privilege 15 secret 0 eem-secret
!
aaa new-model
!
!-- TACACS servers and group config.
!
tacacs server ISE01-T
 address ipv4 10.255.1.21
 key 0 tacacs-secret
 single-connection

tacacs server ISE02-T
 address ipv4 10.255.1.22
 key 0 tacacs-secret
 single-connection

tacacs server ISE03-T
 address ipv4 10.255.1.23
 key 0 tacacs-secret
 single-connection
!
aaa group server tacacs+ ISE-T
 server name ISE01-T
 server name ISE02-T
 server name ISE03-T
 ip tacacs source-interface Vlan2401
!
!-- RADIUS servers and group config.
!
radius server ISE01-R
 address ipv4 10.255.1.21 auth-port 1812 acct-port 1813
 automate-tester username test-user ignore-acct-port probe-on
 key 0 radius-secret

radius server ISE02-R
 address ipv4 10.255.1.22 auth-port 1812 acct-port 1813
 automate-tester username test-user ignore-acct-port probe-on
 key 0 radius-secret

radius server ISE03-R
 address ipv4 10.255.1.23 auth-port 1812 acct-port 1813
 automate-tester username test-user ignore-acct-port probe-on
 key 0 radius-secret
!
aaa group server radius ISE-R
 server name ISE01-R
 server name ISE02-R
 server name ISE03-R
 ip radius source-interface Vlan2401
 load-balance method least-outstanding batch-size 5
!
radius-server attribute 6 on-for-login-auth
radius-server attribute 8 include-in-access-req
radius-server attribute 25 access-request include
radius-server attribute 31 mac format ietf upper-case
radius-server attribute 31 send nas-port-detail mac-only
radius-server dead-criteria time 10 tries 3
radius-server deadtime 5
!
aaa server radius dynamic-author
 client 10.255.1.21 server-key 0 radius-secret
 client 10.255.1.22 server-key 0 radius-secret
 client 10.255.1.23 server-key 0 radius-secret
!
!-- Default authentication method list to use TACACS, fallback to local (ssh).
 aaa authentication login default group ISE-T local
!
!-- Authentication method list to use local creds only (console).
 aaa authentication login CONSOLE local
!
!-- Required to authorize console to exec mode (console).
 aaa authorization console
!
!-- Default authorization exec and commands 15 method lists to use local or TACACS (console, ssh).
!-- 'local' must be defined here first for console authorization to work.
 aaa authorization exec default local group ISE-T if-authenticated 
 aaa authorization commands 15 default local group ISE-T if-authenticated
!
!-- Accounting commands to send all exec start-stop, and priv 15 commands to TACACS (console, ssh).
 aaa accounting exec default start-stop group ISE-T
 aaa accounting commands 15 default start-stop group ISE-T
!
!-- AAA commands for dot1x/mab.
aaa authentication dot1x default group ISE-R
aaa authorization network default group ISE-R
aaa accounting dot1x default start-stop group ISE-R
aaa accounting update newinfo periodic 2880
!
!---------------------------------------------------------------- CONFIG: DOT1X/ISE-MISC
!
dot1x system-auth-control
dot1x critical eapol
!
authentication mac-move permit
!
device-sensor filter-list dhcp list DHCP-ISE
 option name host-name
 option name requested-address
 option name parameter-request-list
 option name class-identifier
 option name client-identifier
!
device-sensor filter-list cdp list CDP-ISE
 tlv name device-name
 tlv name address-type
 tlv name capabilities-type
 tlv name version-type
 tlv name platform-type
!
device-sensor filter-spec dhcp include list DHCP-ISE
device-sensor filter-spec cdp include list CDP-ISE
device-sensor accounting
device-sensor notify all-changes
!
!---------------------------------------------------------------- CONFIG: VLANS
!
vlan 1401
 name GRT:TR04/THIN-CLIENTS

vlan 1402
 name GRT:TR04/THICK-CLIENTS

vlan 1403
 name GRT:TR04/VOICE

vlan 1499
 name GRT:TR04/CRIT-AUTH

vlan 2401
 name MGMT:TR04/SW-WLC

vlan 2402
 name MGMT:TR04/CAPWAP

vlan 998
 name GLOBAL

vlan 999
 name NATIVE
!
!---------------------------------------------------------------- CONFIG: INTERFACES
!
interface GigabitEthernet0/0
 shutdown
!
!-- Configure misc interfaces for all stack members.
!
interface range Te1/0/48,Te2/0/48,Te3/0/48,Te4/0/48
 description TMP//PROVISION
 switchport
 switchport mode access
 switchport nonegotiate
 switchport access vlan 1
 spanning-tree portfast
 ip dhcp snooping trust
 no shutdown
!
interface range Gi1/1/1-2
 shutdown
!
interface range Te1/1/3-4
 channel-group 1 mode active
 no shutdown
!
interface range Gi1/1/3-4,Te1/1/1-2,Te1/1/5-8,Fo1/1/1-2
 description ***NULL//DoNotConfigure
 shutdown

interface range Gi2/1/1-2
 shutdown
!
interface range Te2/1/3-4
 channel-group 1 mode active
 no shutdown
!
interface range Gi2/1/3-4,Te2/1/1-2,Te2/1/5-8,Fo2/1/1-2
 description ***NULL//DoNotConfigure
 shutdown

interface range Gi3/1/1-2
 shutdown
!
interface range Te3/1/3-4
 channel-group 1 mode active
 no shutdown
!
interface range Gi3/1/3-4,Te3/1/1-2,Te3/1/5-8,Fo3/1/1-2
 description ***NULL//DoNotConfigure
 shutdown

interface range Gi4/1/1-2
 shutdown
!
interface range Te4/1/3-4
 channel-group 1 mode active
 no shutdown
!
interface range Gi4/1/3-4,Te4/1/1-2,Te4/1/5-8,Fo4/1/1-2
 description ***NULL//DoNotConfigure
 shutdown
!
interface Port-channel1
 description DIST-01:Po142
 switchport trunk native vlan 999
 switchport trunk allowed vlan 1401,1402,1403,1499,2401,2402,998,999
 switchport mode trunk
 switchport nonegotiate
 ip dhcp snooping trust
 no shutdown
!
!---------------------------------------------------------------- CONFIG: IP/SOURCE/NMS
!
interface Vlan1
 desc TMP//PROVISION
 ip address dhcp
 no shutdown
!
interface Vlan2401
 description MGMT:TR04/SW-WLC
 no ip redirects
 no ip proxy-arp
 ip address 10.254.4.142 255.255.255.0
 no shutdown
!
ip tftp source-interface Vlan2401
ip tftp blocksize 8192
!
ip route 0.0.0.0 0.0.0.0 10.254.4.1 name Default>MGMT:DIST-01
!
logging source-interface Vlan2401
logging host 10.255.1.1
!
ntp authentication-key 1 md5 ntp-secret
ntp authenticate
ntp trusted-key 1
ntp server 10.255.1.11 key 1 prefer source vlan2401
ntp server 10.255.1.12 key 1  source vlan2401
!
snmp-server group SNMP_ASW v3 priv
snmp-server user snmp_asw SNMP_ASW v3 auth sha snmp-auth priv aes 256 smp-priv
snmp-server location Provisioned with switch template: tmpl_test.j2 (rev: 8)
snmp-server contact Provisioned hostname (ASW-TR04-02) and stack members: FOC11111111-FOC22222222-FOC33333333-FOC44444444
!
line console 0
 login authentication CONSOLE
 logging synchronous
 exec-timeout 15 0
line vty 0 15
 logging synchronous
 exec-timeout 30 0
 transport input ssh
!
!---------------------------------------------------------------- CONFIG: EEM
!
event manager session cli username eem_svc
!
event manager applet int-auth
 event none maxrun 60
 action 00.00 puts "## Enter interface to configure with authentication; e.g. gi1/0/1, te1/0/37"
 action 00.01 gets intf
 action 00.02 cli command "enable"
 action 00.03 cli command "show int $intf status"
 action 00.04 regexp "Invalid" "$_cli_result"
 action 00.05 if $_regexp_result eq "1"
 action 00.06  puts "## Invalid interface ($intf), verify prefix; i.e. 'gi' or 'te'."
 action 00.07  exit
 action 00.08 end
 action 00.10 regexp "connected\ +([0-9]+)" "$_cli_result" match currvl
 action 00.11 if $_regexp_result eq "1"
 action 00.12  puts "## Interface $intf has a device connected in VLAN $currvl, continue (y|n)..."
 action 00.13  gets cont
 action 00.14  if $cont ne "y"
 action 00.15   exit
 action 00.16  end
 action 00.17 end
 action 01.00 cli command "conf t"
 action 01.01 cli command "default int $intf"
 action 01.02 cli command "int $intf"
 action 01.03 cli command "macro apply int-auth"
 action 01.04 cli command "end"
 action 01.05 cli command "write mem" pattern "confirm|#"
 action 01.06 cli command ""
 action 01.10 puts "## Interface $intf configured with authentication."
!
event manager applet int-noauth
 event none maxrun 60
 action 00.00 puts "## Enter interface to configure without authentication; e.g. gi1/0/1, te1/0/37"
 action 00.01 gets intf
 action 00.02 cli command "enable"
 action 00.03 cli command "show int $intf status"
 action 00.04 regexp "Invalid" "$_cli_result"
 action 00.05 if $_regexp_result eq "1"
 action 00.06  puts "## Invalid interface ($intf), verify prefix; i.e. 'gi' or 'te'."
 action 00.07  exit
 action 00.08 end
 action 00.10 regexp "connected\ +([0-9]+)" "$_cli_result" match currvl
 action 00.11 if $_regexp_result eq "1"
 action 00.12  puts "## Interface $intf has a device connected in VLAN $currvl, continue (y|n)..."
 action 00.13  gets cont
 action 00.14  if $cont ne "y"
 action 00.15   exit
 action 00.16  end
 action 00.17 end
 action 00.20 puts "## Enter access VLAN ID; e.g. 800"
 action 00.21 gets vl
 action 00.22 cli command "show vlan id $vl"
 action 00.23 regexp "not found" "$_cli_result"
 action 00.24 if $_regexp_result eq "1"
 action 00.25  puts "## VLAN $vl is not configured on this switch."
 action 00.26  exit
 action 00.27 end
 action 01.00 cli command "conf t"
 action 01.01 cli command "default int $intf"
 action 01.02 cli command "int $intf"
 action 01.03 cli command "macro apply int-noauth \$VL $vl"
 action 01.04 cli command "end"
 action 01.05 cli command "write mem" pattern "confirm|#"
 action 01.06 cli command ""
 action 01.10 puts "## Interface $intf configured without authentication in VL-$vl, please describe accordingly."
!
!---------------------------------------------------------------- CONFIG: EEM-TEMP
!
event manager environment q "
!
event manager applet post_ztp_1
!-- Renumber/prioritize switches in stack, write mem, and then download->upgrade/reload (if needed).
 event syslog occurs 1 pattern "Configured from tftp://172.17.251.251" maxrun 960
 action 00.00 syslog msg "  ## Configuration received via TFTP, run 'post_ztp_1' EEM applet in 120s."
 action 00.01 wait 120
 action 00.03 cli command "enable"
 action 00.05 cli command "show mod | i ^.[1-9]"
 action 00.06 set stack "$_cli_result"
 action 00.07 syslog msg "  ## Checking all switches' version and stack membership, adjusting where necessary.\n  ## Current order;\n$stack"
 action 00.08 set error_list ""
 action 00.09 set change_list ""
 action 00.10 set upgrade_list ""
 
 action 01.00 set sw_num "1"
 action 01.01 set pri "16"
 action 01.02 decrement pri 1
 action 01.03 regexp "FOC11111111" "$stack"
 action 01.04 if $_regexp_result ne "1"
 action 01.05  syslog msg "\n  ## FOC11111111 (Sw-1 serial) not found in the stack, check 'show mod' output."
 action 01.06  append error_list "\n  ##  FOC11111111 is allocated (idarray_1) but was not found in the stack."
 action 01.07 else
 action 01.08  set i "0"
 action 01.09  foreach line "$stack" "\n"
 action 01.10   increment i
 action 01.11   if $i le "4"
 action 01.12    string trim "$line"
 action 01.13    set line "$_string_result"
 action 01.14    regexp "FOC11111111" "$line"
 action 01.15    if $_regexp_result eq "1"
 action 01.16     regexp "([0-9\.A-Z]+$)" "$line" curr_ver
 action 01.17     if $curr_ver ne "16.3.7"
 action 01.18      append upgrade_list "1"
 action 01.19     end
 action 01.20     cli command "switch $i priority $pri" pattern "continue|#"
 action 01.21     cli command "y"
 action 01.22     if $i eq $sw_num
 action 01.23      append change_list "\n  ##  FOC11111111 (Priority: $pri // Numbered:       $sw_num  // Version: $curr_ver)"
 action 01.24     else
 action 01.25      cli command "switch $i renumber $sw_num" pattern "continue|#"
 action 01.26      cli command "y"
 action 01.27      append change_list "\n  ##  FOC11111111 (Priority: $pri // Renumbered: $i > $sw_num* // Version: $curr_ver)"
 action 01.28     end
 action 01.29     break
 action 01.30    end
 action 01.31   end
 action 01.32  end
 action 01.33 end
 
 action 02.00 set sw_num "2"
 action 02.01 set pri "16"
 action 02.02 decrement pri 2
 action 02.03 regexp "FOC22222222" "$stack"
 action 02.04 if $_regexp_result ne "1"
 action 02.05  syslog msg "\n  ## FOC22222222 (Sw-2 serial) not found in the stack, check 'show mod' output."
 action 02.06  append error_list "\n  ##  FOC22222222 is allocated (idarray_2) but was not found in the stack."
 action 02.07 else
 action 02.08  set i "0"
 action 02.09  foreach line "$stack" "\n"
 action 02.10   increment i
 action 02.11   if $i le "4"
 action 02.12    string trim "$line"
 action 02.13    set line "$_string_result"
 action 02.14    regexp "FOC22222222" "$line"
 action 02.15    if $_regexp_result eq "1"
 action 02.16     regexp "([0-9\.A-Z]+$)" "$line" curr_ver
 action 02.17     if $curr_ver ne "16.3.7"
 action 02.18      append upgrade_list "2"
 action 02.19     end
 action 02.20     cli command "switch $i priority $pri" pattern "continue|#"
 action 02.21     cli command "y"
 action 02.22     if $i eq $sw_num
 action 02.23      append change_list "\n  ##  FOC22222222 (Priority: $pri // Numbered:       $sw_num  // Version: $curr_ver)"
 action 02.24     else
 action 02.25      cli command "switch $i renumber $sw_num" pattern "continue|#"
 action 02.26      cli command "y"
 action 02.27      append change_list "\n  ##  FOC22222222 (Priority: $pri // Renumbered: $i > $sw_num* // Version: $curr_ver)"
 action 02.28     end
 action 02.29     break
 action 02.30    end
 action 02.31   end
 action 02.32  end
 action 02.33 end
 
 action 03.00 set sw_num "3"
 action 03.01 set pri "16"
 action 03.02 decrement pri 3
 action 03.03 regexp "FOC33333333" "$stack"
 action 03.04 if $_regexp_result ne "1"
 action 03.05  syslog msg "\n  ## FOC33333333 (Sw-3 serial) not found in the stack, check 'show mod' output."
 action 03.06  append error_list "\n  ##  FOC33333333 is allocated (idarray_3) but was not found in the stack."
 action 03.07 else
 action 03.08  set i "0"
 action 03.09  foreach line "$stack" "\n"
 action 03.10   increment i
 action 03.11   if $i le "4"
 action 03.12    string trim "$line"
 action 03.13    set line "$_string_result"
 action 03.14    regexp "FOC33333333" "$line"
 action 03.15    if $_regexp_result eq "1"
 action 03.16     regexp "([0-9\.A-Z]+$)" "$line" curr_ver
 action 03.17     if $curr_ver ne "16.3.7"
 action 03.18      append upgrade_list "3"
 action 03.19     end
 action 03.20     cli command "switch $i priority $pri" pattern "continue|#"
 action 03.21     cli command "y"
 action 03.22     if $i eq $sw_num
 action 03.23      append change_list "\n  ##  FOC33333333 (Priority: $pri // Numbered:       $sw_num  // Version: $curr_ver)"
 action 03.24     else
 action 03.25      cli command "switch $i renumber $sw_num" pattern "continue|#"
 action 03.26      cli command "y"
 action 03.27      append change_list "\n  ##  FOC33333333 (Priority: $pri // Renumbered: $i > $sw_num* // Version: $curr_ver)"
 action 03.28     end
 action 03.29     break
 action 03.30    end
 action 03.31   end
 action 03.32  end
 action 03.33 end
 
 action 04.00 set sw_num "4"
 action 04.01 set pri "16"
 action 04.02 decrement pri 4
 action 04.03 regexp "FOC44444444" "$stack"
 action 04.04 if $_regexp_result ne "1"
 action 04.05  syslog msg "\n  ## FOC44444444 (Sw-4 serial) not found in the stack, check 'show mod' output."
 action 04.06  append error_list "\n  ##  FOC44444444 is allocated (idarray_4) but was not found in the stack."
 action 04.07 else
 action 04.08  set i "0"
 action 04.09  foreach line "$stack" "\n"
 action 04.10   increment i
 action 04.11   if $i le "4"
 action 04.12    string trim "$line"
 action 04.13    set line "$_string_result"
 action 04.14    regexp "FOC44444444" "$line"
 action 04.15    if $_regexp_result eq "1"
 action 04.16     regexp "([0-9\.A-Z]+$)" "$line" curr_ver
 action 04.17     if $curr_ver ne "16.3.7"
 action 04.18      append upgrade_list "4"
 action 04.19     end
 action 04.20     cli command "switch $i priority $pri" pattern "continue|#"
 action 04.21     cli command "y"
 action 04.22     if $i eq $sw_num
 action 04.23      append change_list "\n  ##  FOC44444444 (Priority: $pri // Numbered:       $sw_num  // Version: $curr_ver)"
 action 04.24     else
 action 04.25      cli command "switch $i renumber $sw_num" pattern "continue|#"
 action 04.26      cli command "y"
 action 04.27      append change_list "\n  ##  FOC44444444 (Priority: $pri // Renumbered: $i > $sw_num* // Version: $curr_ver)"
 action 04.28     end
 action 04.29     break
 action 04.30    end
 action 04.31   end
 action 04.32  end
 action 04.33 end
 
 action 10.00 wait 5
 action 10.01 if $error_list ne ""
 action 10.02  syslog msg "\n  ## The following errors occurred; $error_list"
 action 10.03 end
 action 10.04 syslog msg "\n  ## Switches below have been assigned a priority and renumbered* as needed; $change_list"
 action 13.00 cli command "conf t"
 action 13.01 cli command "no event man app post_ztp_1"
 action 14.00 if $upgrade_list eq ""
 action 14.01  syslog msg "  ## All switches are running version 16.3.7, skipping download->upgrade/reload; finalizing config in 20s."
 action 14.02  cli command "no event man env q"
 action 14.03  cli command "event man env ztp_upgraded no"
 action 14.04  cli command " event man app post_ztp_2"
 action 14.05  cli command " event timer countdown time 20 maxrun 480"
 action 14.06  cli command " no action 00.00"
 action 14.07  cli command " no action 00.01"
 action 14.08  cli command " end"
 action 14.09  cli command "write mem" pattern "confirm|#"
 action 14.10  cli command ""
 action 14.11 else
 action 14.12  syslog msg "  ## One or more switches require an upgrade to version 16.3.7 ($upgrade_list); proceeding with download->upgrade/reload."
 action 14.13  cli command "event man env ztp_upgraded yes"
 action 14.14  cli command "event man app post_ztp_2"
 action 14.15  cli command " event syslog occurs 1 pattern $q%IOSXE_REDUNDANCY-6-PEER$q maxrun 630"
 action 14.16  cli command " no event man env q"
 action 14.17  cli command "end"
 action 14.18  cli command "write mem" pattern "confirm|#"
 action 14.19  cli command ""
 action 14.20  syslog msg "  ## (Standby) Downloading image..."
 action 14.21  cli command "copy tftp://172.17.251.251/cat3k_caa-universalk9.16.03.07.SPA.bin flash:"
 action 14.22  syslog msg "  ## (Standby) Image downloaded, upgrading..."
 action 14.23  cli command "software install file flash:cat3k_caa-universalk9.16.03.07.SPA.bin new force" pattern "proceed|#"
 action 14.24  syslog msg "  ## Upgrade complete, rebooting."
 action 14.25  cli command "y"
 action 14.26 end
!
event manager applet post_ztp_2
!-- Apply final configs, push running config via TFTP, clean up VL-1 and Te#/0/48 configs, write mem, and then perform package clean.
 event none
 action 00.00 syslog msg "  ## Switch reloaded on new image, running 'post_ztp_2' EEM applet in 150s."
 action 00.01 wait 150
 action 00.02 syslog msg "  ## (Standby) Applying global configs ignored by smart-install and generating crypto key..."
 action 00.03 cli command "enable"
 action 00.04 set upgr "$ztp_upgraded"
 action 01.00 cli command "conf t"
 action 01.01 cli command "no event man env ztp_upgraded"
 action 01.02 cli command "no event man app post_ztp_2"
 action 01.04 cli command "no vstack"
 action 01.05 cli command "no ip http ser"
 action 01.06 cli command "no ip http secure-s"
 action 01.07 cli command "no ip http authen"
 action 01.08 cli command "no coap http enable"
 action 01.09 cli command "device-tracking policy IPDT"
 action 01.10 cli command " no protocol udp"
 action 01.11 cli command " tracking enable"
 action 01.12 cli command "cry key gen rsa mod 2048"
 action 02.00 syslog msg "  ## (Standby) Applying `int-auth` macro config to ports 1-48 on all stack members..."
 action 02.01 cli command "int range Gi1/0/1-36,Gi2/0/1-36,Gi3/0/1-36,Gi4/0/1-36"
 action 02.02 cli command " macro apply int-auth"
 action 02.03 cli command " no macro desc"
 action 02.04 cli command "int range Te1/0/37-47,Te2/0/37-47,Te3/0/37-47,Te4/0/37-47"
 action 02.05 cli command " macro apply int-auth"
 action 02.06 cli command " no macro desc"
 action 03.00 syslog msg "  ## Pushing config to the following location: tftp://172.17.251.251/provisioned/CAS/ASW-TR04-02_FOC11111111-FOC22222222-FOC33333333-FOC44444444.cfg"
 action 03.01 cli command "do copy run tftp://172.17.251.251/provisioned/CAS/ASW-TR04-02_FOC11111111-FOC22222222-FOC33333333-FOC44444444.cfg"
 action 04.00 syslog msg "  ## Disabling VL-1 SVI, updating Te#/0/48 configs, and writing startup config."
 action 04.01 cli command "int vl 1"
 action 04.02 cli command " no desc"
 action 04.03 cli command " no ip addr"
 action 04.04 cli command " shut"
 action 04.05 cli command "default int range Te1/0/48,Te2/0/48,Te3/0/48,Te4/0/48"
 action 04.06 cli command "int range Te1/0/48,Te2/0/48,Te3/0/48,Te4/0/48"
 action 04.07 cli command " macro apply int-auth"
 action 04.08 cli command " no macro desc"
 action 04.09 cli command "end"
 action 04.10 cli command "write mem" pattern "confirm|#"
 action 04.11 cli command ""
 action 05.00 if $upgr eq "yes"
 action 05.01  syslog msg "  ## (Standby) ZTP upgrade detected, performing software package clean..."
 action 05.02  cli command "req plat soft pack clean sw all" pattern "proceed|#"
 action 05.03  cli command "y"
 action 05.04  syslog msg "  ## Unused .bin or .pkg files from previous version(s) have been deleted."
 action 05.05 else
 action 05.06  syslog msg "  ## ZTP upgrade not detected, skipping software package clean."
 action 05.07 end
 action 05.08 syslog msg "  ## Start-up config written, (ASW-TR04-02) is ready for deployment, OK to power off."
!
end
##############################