Device
Devices represent individual IOS-XE network devices (switches, routers) that will be configured and managed. Each device is identified by a unique name and connects via either NETCONF or RESTCONF.
Device configurations have the highest precedence in the configuration hierarchy, allowing you to override global and device group settings for specific devices. This provides fine-grained control while maintaining consistency across your network infrastructure.
Key device attributes:
- name: Unique identifier for the device
- url: Management URL for RESTCONF connectivity (e.g.,
https://10.1.1.10). Only used whenprotocolis set torestconf. Deprecated in favor ofhost - host: Management IP or hostname for device connectivity. Works with both NETCONF (default) and RESTCONF. Use
"ip:port"syntax for custom ports (e.g., devices behind PAT) - protocol: Connection protocol —
netconf(default) orrestconf - managed: Boolean flag to temporarily exclude devices from configuration (useful for maintenance)
- device_groups: List of device groups this device belongs to
- variables: Device-specific variables that override group and global variables
- interface_group_policy: Controls how interface groups are resolved between device_group and device layers —
merge(default) orreplace - configuration: Device-specific configuration that takes highest precedence
Devices connect using one of two protocols:
- NETCONF (default): Set
hostto the device IP or hostname. The default port is830. To use a custom NETCONF port (e.g., for devices behind PAT), specifyhostas"ip:port"(e.g.,"10.1.1.10:10022"). Theprotocolattribute can be omitted sincenetconfis the default. - RESTCONF: Set
hostto the device IP or hostname andprotocol: restconf. The default port is443. Alternatively, useurlwith a full URL (e.g.,https://10.1.1.10), thoughurlis deprecated.
Device credentials are configured via the IOSXE_USERNAME and IOSXE_PASSWORD environment variables. All devices share a single credential set — per-device credentials are not currently supported.
The managed attribute can be set to false to temporarily skip a device during deployment, which is useful for maintenance windows or staged rollouts.
Device Transactional Commits
Section titled “Device Transactional Commits”By default, when the NETCONF candidate datastore is enabled on the device (netconf-yang feature candidate-datastore), each resource (interface, routing protocol, ACL, etc.) is independently pushed to the candidate datastore, validated, and committed before moving on to the next resource. Without the candidate datastore, the Terraform provider edits the running configuration directly. In either case, the default behavior applies changes on a per-resource basis, meaning that a failure partway through a terraform apply can leave the device in a partially configured state.
Enabling transactional commits for devices changes this by grouping all configuration changes into a single atomic commit. All resources are staged in the candidate datastore without being committed, and only after every resource has been successfully staged does a single commit push all changes to the running configuration at once. If any resource fails during the staging phase, the commit never fires and the device remains untouched.
This is particularly useful when partial configuration would leave the device in an invalid or insecure state, when compliance requirements mandate all-or-nothing deployments, or when changes span interdependent resources that must be applied together.
Device Prerequisite
Section titled “Device Prerequisite”The device must have the NETCONF candidate datastore feature enabled:
netconf-yangnetconf-yang feature candidate-datastoreVerify with show netconf-yang datastores — both running and candidate must be listed:
Router#show netconf-yang datastoresDatastore Name : runningDatastore Name : candidate
Router#Without the candidate datastore, the Terraform provider falls back to editing the running configuration directly and the transaction mode has no effect.
Module Configuration
Section titled “Module Configuration”Set device_transaction = true when invoking the Terraform module through your main.tf file:
module "iosxe" { source = "netascode/nac-iosxe/iosxe" yaml_directories = ["./data"] device_transaction = true save_config = true # Optional: persist to startup-config after commit}With this attribute set, all data model changes defined in your YAML files will be staged and committed as a single transaction per device — either every change is applied, or none are.
Verifying Transaction Behavior
Section titled “Verifying Transaction Behavior”You can verify this behavior using a simple data model with multiple resources, such as three loopback interfaces:
iosxe: devices: - name: Router1 host: 10.1.1.1 protocol: netconf configuration: interfaces: loopbacks: - id: "301" description: "Transaction Test 1" ipv4: address: 30.0.1.1 address_mask: 255.255.255.255 - id: "302" description: "Transaction Test 2" ipv4: address: 30.0.2.1 address_mask: 255.255.255.255 - id: "303" description: "Transaction Test 3" ipv4: address: 30.0.3.1 address_mask: 255.255.255.255Enable the IOS-XE configuration archive to observe commit boundaries:
archive log config logging enable logging size 500After a terraform apply, run show archive log config all to inspect the commit history. Each commit is assigned a unique session number in the sess column.
In the default mode, each resource gets its own session, as shown by the show archive log config all command:
Router#show archive log config all idx sess user@line Logged command 110 19 admin@vty42949 |interface Loopback301 111 19 admin@vty42949 | description Transaction Test 1 112 19 admin@vty42949 | no shutdown 113 19 admin@vty42949 | ip address 30.0.1.1 255.255.255.255 114 19 admin@vty42949 | exit 115 20 admin@vty42949 |interface Loopback302 116 20 admin@vty42949 | description Transaction Test 2 117 20 admin@vty42949 | no shutdown 118 20 admin@vty42949 | ip address 30.0.2.1 255.255.255.255 119 20 admin@vty42949 | exit 120 21 admin@vty42949 |interface Loopback303 121 21 admin@vty42949 | description Transaction Test 3 122 21 admin@vty42949 | no shutdown 123 21 admin@vty42949 | ip address 30.0.3.1 255.255.255.255 124 21 admin@vty42949 | exit
Router#In the transaction mode, all resources share a single session, as shown by the show archive log config all command:
Router#show archive log config all idx sess user@line Logged command 192 38 admin@vty42949 |interface Loopback301 193 38 admin@vty42949 | description Transaction Test 1 194 38 admin@vty42949 | no shutdown 195 38 admin@vty42949 | ip address 30.0.1.1 255.255.255.255 196 38 admin@vty42949 | exit 197 38 admin@vty42949 |interface Loopback302 198 38 admin@vty42949 | description Transaction Test 2 199 38 admin@vty42949 | no shutdown 200 38 admin@vty42949 | ip address 30.0.2.1 255.255.255.255 201 38 admin@vty42949 | exit 202 38 admin@vty42949 |interface Loopback303 203 38 admin@vty42949 | description Transaction Test 3 204 38 admin@vty42949 | no shutdown 205 38 admin@vty42949 | ip address 30.0.3.1 255.255.255.255 206 38 admin@vty42949 | exit
Router#Failure Behavior
Section titled “Failure Behavior”To demonstrate the difference in failure handling between the two modes, consider a data model where one resource references a VRF that does not exist on the device:
iosxe: devices: - name: Router1 host: 10.1.1.1 protocol: netconf configuration: interfaces: loopbacks: - id: "301" description: "Transaction Test 1" ipv4: address: 30.0.1.1 address_mask: 255.255.255.255 - id: "302" description: "Transaction Test 2" ipv4: address: 30.0.2.1 address_mask: 255.255.255.255 - id: "303" description: "This will fail" vrf_forwarding: "NONEXISTENT_VRF" ipv4: address: 30.0.3.1 address_mask: 255.255.255.255In the default mode, Loopback301 and Loopback302 are each committed independently before Loopback303 is attempted. When Loopback303 fails, the device is left in a partially configured state — the archive log confirms that Loopback301 and Loopback302 were committed in separate sessions:
Router#show ip interface brief | include LoopbackLoopback301 30.0.1.1 YES other up upLoopback302 30.0.2.1 YES other up up
Router#show archive log config all idx sess user@line Logged command 237 56 admin@vty42949 |interface Loopback301 238 56 admin@vty42949 | description Transaction Test 1 239 56 admin@vty42949 | no shutdown 240 56 admin@vty42949 | ip address 30.0.1.1 255.255.255.255 241 56 admin@vty42949 | exit 242 58 admin@vty42949 |interface Loopback302 243 58 admin@vty42949 | description Transaction Test 2 244 58 admin@vty42949 | no shutdown 245 58 admin@vty42949 | ip address 30.0.2.1 255.255.255.255 246 58 admin@vty42949 | exit
Router#In the transaction mode, all resources are staged to the candidate datastore before any commit occurs. When Loopback303 fails during staging, the iosxe_commit resource never executes and no changes are applied to the running configuration. The device remains completely untouched — the archive log confirms that no new entries were recorded:
Router#show ip interface brief | include Loopback
Router#show archive log config all idx sess user@line Logged command
Router#Limitations and Caveats
Section titled “Limitations and Caveats”State Drift on Failed Transactional Deletes
Section titled “State Drift on Failed Transactional Deletes”When destroying resources in transaction mode, Terraform removes each resource from its state file as the individual destroy operations succeed during the staging phase. However, the actual device configuration is not removed until the final iosxe_commit resource executes the atomic commit. If that commit fails (for example, because another resource in the same transaction has a validation error), the device retains all of its original configuration, but Terraform’s state no longer tracks the “deleted” resources.
The result is a divergence: the device still has the configuration, but Terraform believes it has been removed. A subsequent terraform plan will not detect the orphaned configuration because Terraform has no state entry to compare against.
To recover from this situation:
- Re-import the affected resources using
terraform importto bring them back into state - Alternatively, run
terraform planwith the resources re-declared in your data model — Terraform will detect them as new and plan to create them (resulting in a no-op on the device since the configuration already exists)
This limitation is inherent to how Terraform processes destroy operations and is not specific to IOS-XE as Code — it applies to any provider using a deferred-commit pattern.
Diagram
Section titled “Diagram”Classes
Section titled “Classes”devices (iosxe)
Section titled “devices (iosxe)”| Name | Type | Constraint | Mandatory | Default Value |
|---|---|---|---|---|
| name | String | Yes | ||
| url | String | No | ||
| host | String | No | ||
| managed | Boolean | true, false | No | true |
| protocol | Choice | restconf, netconf | No | |
| version | String | No | ||
| device_groups | List | String | No | |
| variables | Map | No | ||
| templates | List | String | No | |
| interface_group_policy | Choice | merge, replace | No |
Examples
Section titled “Examples”iosxe: devices: # Basic device configuration, where NETCONF is used (since it is the default). - name: Access-SW-01 host: 10.1.1.10 managed: true
# Basic device configuration, where RESTCONF is used. - name: Access-SW-02 host: 10.1.1.11 protocol: restconf managed: true
# Basic device configuration with non-default NETCONF port. - name: Access-SW-03 host: "10.1.11.12:9999" managed: true
# Device with group membership and variables - name: Core-SW-01 host: 10.1.1.1 device_groups: - CORE_SWITCHES - OSPF_AREA_0 variables: router_id: "1.1.1.1" mgmt_vlan: 100
# Device with specific configuration override - name: Edge-SW-01 host: 10.1.1.20 device_groups: [ACCESS_SWITCHES] configuration: system: hostname: EDGE-SWITCH-01 interfaces: ethernets: - type: GigabitEthernet id: "1/0/1" description: "Uplink to Core"
# Device temporarily excluded from management - name: Maint-SW-01 host: 10.1.1.99 managed: false # Skip during maintenanceDevices represent individual IOS-XE network devices (switches, routers) that will be configured and managed. Each device is identified by a unique name and requires a management URL for connectivity.
Device configurations have the highest precedence in the configuration hierarchy, allowing you to override global and device group settings for specific devices. This provides fine-grained control while maintaining consistency across your network infrastructure.
Key device attributes:
- name: Unique identifier for the device
- host: Management IP for device connectivity (HTTPS/RESTCONF)
- managed: Boolean flag to temporarily exclude devices from configuration (useful for maintenance)
- device_groups: List of device groups this device belongs to
- variables: Device-specific variables that override group and global variables
- configuration: Device-specific configuration that takes highest precedence
The managed attribute can be set to false to temporarily skip a device during deployment, which is useful for maintenance windows or staged rollouts.
Diagram
Section titled “Diagram”Classes
Section titled “Classes”devices (iosxe)
Section titled “devices (iosxe)”| Name | Type | Constraint | Mandatory | Default Value |
|---|---|---|---|---|
| name | String | Yes | ||
| host | String | No | ||
| managed | Boolean | true, false | No | true |
| protocol | Choice | restconf, netconf | No | |
| version | String | No | ||
| device_groups | List | String | No | |
| variables | Map | No | ||
| templates | List | String | No |
Examples
Section titled “Examples”iosxe: devices: # Basic device configuration - name: Access-SW-01 host: 10.1.1.10 managed: true
# Device with group membership and variables - name: Core-SW-01 host: 10.1.1.1 device_groups: - CORE_SWITCHES - OSPF_AREA_0 variables: router_id: "1.1.1.1" mgmt_vlan: 100
# Device with specific configuration override - name: Edge-SW-01 host: 10.1.1.20 device_groups: [ACCESS_SWITCHES] configuration: system: hostname: EDGE-SWITCH-01 interfaces: ethernets: - type: GigabitEthernet id: "1/0/1" description: "Uplink to Core"
# Device temporarily excluded from management - name: Maint-SW-01 host: 10.1.1.99 managed: false # Skip during maintenance