First Steps
This guide walks you through configuring Cisco ISE using Network as Code — starting from a single YAML file and a one-line Terraform module, then progressively introducing ISE’s identity, network access, and TrustSec constructs that let you manage ISE’s full configuration as data.
Prerequisites:
- Terraform 1.9.0 or later
- Your preferred text editor / IDE (such as Visual Studio Code)
- An ISE Policy Administration Node (PAN) reachable from your workstation, with API access enabled (see step 1)
-
Enable ISE API Access
Section titled “Enable ISE API Access”The ISE REST APIs must be enabled before Terraform can communicate with your ISE instance. Log in to the ISE admin portal and:
- Navigate to Administration -> Settings -> API Settings.
- Under API Service Settings, enable ERS (Read/Write) and Open API (Read/Write).
- Click Save.
-
Set Environment Variables
Section titled “Set Environment Variables”The ISE provider authenticates using environment variables. Set the following in your terminal:
Terminal window export ISE_URL=https://10.1.1.100export ISE_USERNAME=adminexport ISE_PASSWORD=password -
Create Your First Data File
Section titled “Create Your First Data File”In a new project directory, create a file named
ise.nac.yamlwith the following content. Replace theipwith the management address of a device you want ISE to authenticate:ise:network_resources:network_devices:- name: Router1ips:- ip: 10.1.2.3radius:shared_secret: Cisco123This file describes a single network device — a RADIUS client that ISE will authenticate. The structure mirrors the sections you would otherwise click through in the ISE admin portal.
-
Create the Terraform Configuration
Section titled “Create the Terraform Configuration”In the same directory, create a file named
main.tf:module "ise" {source = "netascode/nac-ise/ise"version = "0.3.0"yaml_files = ["ise.nac.yaml"]}This is the only Terraform configuration required. The
nac-isemodule reads the YAML file and maps its contents to the underlying ISE provider resources automatically. -
Initialize Terraform
Section titled “Initialize Terraform”Run the following command to download the provider and module:
Terminal window terraform init -
Apply the Configuration
Section titled “Apply the Configuration”Preview and apply your changes:
Terminal window terraform applyReview the plan and type
yesto confirm. Terraform pushes the configuration to ISE via its REST APIs.
Congratulations! You have created your first ISE policy object using Network as Code. The sections below show how to expand this foundation into ISE’s other configuration areas — network device groups, identity management, network access policy, and TrustSec.
All of the sections below build on the same ise.nac.yaml file — there is only ever one file, and every top-level key (network_resources, identity_management, network_access, trust_sec) lives side by side under the single root ise: key. Each code block below is complete and paste-able as shown:
- When a block updates a section you already have (like
network_resourcesbelow), it shows that section in full — replace what you had with it. - When a block adds a new section, a
# ...comment marks where your existing sections go; only the new section needs to be added to your file, alongside what is already there.
A “Putting It All Together” snippet at the end shows the complete file after all the sections have been added.
Going Further
Section titled “Going Further”Network Device Groups
Section titled “Network Device Groups”Network device groups let you organize devices by role, location, or device type, so policy can reference the group instead of each device individually. This is useful for anything that applies to a class of device (e.g., all devices at a given site sharing the same authorization policy).
Every device belongs to exactly one group in each of ISE’s three built-in top-level categories — Device Type, Location, and IPSEC Device — defaulting to the catch-all All Device Types, All Locations, and Is IPSEC Device groups when not set explicitly. Because Terraform manages network_device_groups as a complete list, you must declare a value for every category the device belongs to; otherwise each apply will keep trying to remove the categories ISE fills in by default, and never converge.
Update the network_resources block in ise.nac.yaml — replacing what you added in step 3 — to add the group definition and reference it (along with the two default categories) from the device, using the full path#name for the nested group:
ise: network_resources: network_device_groups: - name: Routers path: "All Device Types"
network_devices: - name: Router1 ips: - ip: 10.1.2.3 network_device_groups: - "All Device Types#Routers" - "All Locations" - "Is IPSEC Device" radius: shared_secret: Cisco123Router1 now belongs to the Routers group, nested under the built-in All Device Types root. When a device references a nested group, use the same path#name value used to define the group (here, All Device Types#Routers) rather than just the group’s bare name — the bare name only works when referencing a built-in root group directly (e.g. All Device Types on its own). A device can belong to multiple groups, and groups can be nested further by chaining additional #-delimited segments in path.
Identity Management
Section titled “Identity Management”Identity management covers the users and endpoints ISE authenticates. The simplest resource here is an internal user — an account defined directly in ISE’s local identity store.
identity_management is a new top-level section. Add it to ise.nac.yaml alongside the network_resources block from the previous step:
ise: # ... network_resources from the previous step ...
identity_management: internal_users: - name: jdoe password: Cisco123 first_name: Jane Doe change_password: falseThis creates a local user that can authenticate against policies referencing the Internal Users identity source, without needing an external Active Directory or LDAP store.
Network Access Policy
Section titled “Network Access Policy”Network access policy determines what a session is allowed to do once it authenticates, and is built from a few connected pieces: policy elements (dACLs, authorization profiles) that define what to grant, and policy sets with authorization rules that define when to grant it.
network_access is another new top-level section. Start by adding it to ise.nac.yaml, alongside network_resources and identity_management, with a downloadable ACL (dACL) that defines the traffic permitted for a matching session:
ise: # ... network_resources and identity_management from previous steps ...
network_access: policy_elements: downloadable_acls: - name: DACL1 dacl_content: | permit ip any host 192.168.1.1 deny ip any anyNext, reference the dACL from an authorization profile — the result that gets applied once a rule matches. Add authorization_profiles alongside downloadable_acls, under the same policy_elements key:
ise: # ... network_resources and identity_management from previous steps ...
network_access: policy_elements: downloadable_acls: - name: DACL1 dacl_content: | permit ip any host 192.168.1.1 deny ip any any
authorization_profiles: - name: Employee_Access access_type: ACCESS_ACCEPT dacl_name: DACL1Finally, add a policy set with an authorization rule that matches on a condition and applies the profile. A policy set always needs its own top-level condition to scope which sessions it evaluates — here, wired sessions — in addition to the rule’s own condition, which narrows further to a specific identity group. Add policy_sets as a sibling of policy_elements, under the same network_access key:
ise: # ... network_resources and identity_management from previous steps ...
network_access: policy_elements: downloadable_acls: - name: DACL1 dacl_content: | permit ip any host 192.168.1.1 deny ip any any
authorization_profiles: - name: Employee_Access access_type: ACCESS_ACCEPT dacl_name: DACL1
policy_sets: - name: Employee_Policy_Set service_name: Default Network Access condition: type: ConditionAttributes dictionary_name: Radius attribute_name: NAS-Port-Type operator: equals attribute_value: Ethernet authorization_rules: - name: Employees state: enabled condition: type: ConditionAttributes dictionary_name: IdentityGroup attribute_name: Name operator: equals attribute_value: "User Identity Groups:Employees" profiles: - Employee_AccessTogether, these four pieces form a complete policy: for wired sessions (matching the policy set’s condition) where the identity group condition also matches, the rule grants Employee_Access, which pushes DACL1 to restrict what that session can reach. A condition — at either the policy set or rule level — can be a single attribute match (as above), a ConditionAndBlock/ConditionOrBlock combining several, or a ConditionReference to a reusable condition defined under policy_elements.conditions.
TrustSec
Section titled “TrustSec”TrustSec lets you segment the network using Security Group Tags (SGTs) instead of IP-based ACLs. Sessions and devices are tagged with an SGT, and a policy matrix decides — for each pair of source and destination SGTs — which Security Group ACL (SGACL) is applied to the traffic between them.
trust_sec is the last new top-level section. Add it to ise.nac.yaml, alongside network_resources, identity_management, and network_access, with a Security Group and a matrix entry that uses it:
ise: # ... network_resources, identity_management, and network_access from previous steps ...
trust_sec: security_groups: - name: Sgt97 value: 97
matrix_entries: - source_sgt: Sgt97 destination_sgt: Sgt97 sgacl_name: Permit IPThis assigns Sgt97 the tag value 97, then adds an entry to the built-in Production matrix permitting traffic between endpoints tagged Sgt97 and each other, using the default Permit IP SGACL. matrix_entries is a shorthand for the Production matrix and works on every ISE version; if you need additional named matrices (e.g. PreProd), use the matrices list instead — see the Matrices data model for details.
Putting It All Together
Section titled “Putting It All Together”After working through the sections above, your ise.nac.yaml should look like this in full:
ise: network_resources: network_device_groups: - name: Routers path: "All Device Types"
network_devices: - name: Router1 ips: - ip: 10.1.2.3 network_device_groups: - "All Device Types#Routers" - "All Locations" - "Is IPSEC Device" radius: shared_secret: Cisco123
identity_management: internal_users: - name: jdoe password: Cisco123 first_name: Jane Doe change_password: false
network_access: policy_elements: downloadable_acls: - name: DACL1 dacl_content: | permit ip any host 192.168.1.1 deny ip any any
authorization_profiles: - name: Employee_Access access_type: ACCESS_ACCEPT dacl_name: DACL1
policy_sets: - name: Employee_Policy_Set service_name: Default Network Access condition: type: ConditionAttributes dictionary_name: Radius attribute_name: NAS-Port-Type operator: equals attribute_value: Ethernet authorization_rules: - name: Employees state: enabled condition: type: ConditionAttributes dictionary_name: IdentityGroup attribute_name: Name operator: equals attribute_value: "User Identity Groups:Employees" profiles: - Employee_Access
trust_sec: security_groups: - name: Sgt97 value: 97
matrix_entries: - source_sgt: Sgt97 destination_sgt: Sgt97 sgacl_name: Permit IPRun terraform apply once more to apply everything in one pass.
You now have the full building blocks of a Network as Code ISE deployment. Explore the ISE Data Models section for the complete list of configurable attributes, and the simple or comprehensive example repositories for reference implementations.