Skip to content

Netbox Create Device Interfaces Task¤

task api name: create_device_interfaces

Task to create network interfaces on one or more devices in NetBox. This task creates interfaces in bulk and only if the interfaces do not already exist in NetBox, making it idempotent and safe to run multiple times.

The task supports alphanumeric range expansion, allowing you to create multiple interfaces with a single pattern. This is particularly useful for creating large numbers of similar interfaces efficiently.

Tip

The create_device_interfaces task automatically skips interfaces that already exist, preventing duplicate creation attempts and allowing for safe re-runs of automation tasks.

Inputs¤

Parameter Required Description
devices Yes Device names or device objects to create interfaces for
interface_name Conditional Interface name, list of names, or range pattern to create
interfaces_data Conditional Per-interface payload dictionaries; each item must include name
interface_type No NetBox interface type, default other
description No Interface description
speed No Interface speed in Kbit/s
mtu No Interface MTU in bytes
instance No NetBox instance name to target
branch No NetBox Branching plugin branch name to write to
dry_run No Preview create actions without writing

Provide either interface_name or interfaces_data.

Output¤

Returns per-device create results with created and skipped interface details. Existing interfaces are skipped rather than recreated.

Interface Name Range Expansion¤

The task supports powerful range expansion patterns for creating multiple interfaces:

Numeric Ranges¤

Use [start-end] syntax to expand numeric ranges:

interface_name: "Ethernet[1-5]"
# Expands to: Ethernet1, Ethernet2, Ethernet3, Ethernet4, Ethernet5

interface_name: "Loopback[10-12]"
# Expands to: Loopback10, Loopback11, Loopback12

Comma-Separated Lists¤

Use [option1,option2,...] syntax to expand lists:

interface_name: "[ge,xe,fe]-0/0/0"
# Expands to: ge-0/0/0, xe-0/0/0, fe-0/0/0

interface_name: "Port-[A,B,C]"
# Expands to: Port-A, Port-B, Port-C

Multiple Range Patterns¤

Combine multiple range patterns in a single interface name:

interface_name: "[ge,xe]-0/0/[0-3]"
# Expands to: ge-0/0/0, ge-0/0/1, ge-0/0/2, ge-0/0/3,
#             xe-0/0/0, xe-0/0/1, xe-0/0/2, xe-0/0/3

Multiple Interface Names¤

Pass a list of interface names (with or without ranges):

interface_name:
  - "Loopback[1-3]"
  - "Management1"
  - "[ge,xe]-0/1/0"
# Expands to: Loopback1, Loopback2, Loopback3, Management1, ge-0/1/0, xe-0/1/0

Branching Support¤

Create Device Interfaces task is branch aware and can create interfaces within a branch. Netbox Branching Plugin needs to be installed on the Netbox instance.

When using branches, interfaces are created in the specified branch and can be reviewed before merging into the main database.

Examples¤

Create loopback interfaces on two devices:

nf#netbox create device-interfaces devices switch-01 switch-02 interface_name "Loopback[0-5]" interface-type virtual description "Test interfaces"

Preview interface creation:

nf#netbox create device-interfaces devices switch-01 interface_name "Ethernet[1-4]" interface-type 1000base-t dry-run

Create interfaces in a NetBox branch:

nf#netbox create device-interfaces devices switch-01 interface_name "Ethernet[1-4]" branch my-branch

Context manager:

from norfab.core.nfapi import NorFab

with NorFab(inventory="./inventory.yaml") as nf:
    client = nf.make_client()

    result = client.run_job(
        "netbox",
        "create_device_interfaces",
        workers="any",
        kwargs={
            "devices": ["switch-01", "switch-02"],
            "interface_name": "Loopback[0-5]",
            "interface_type": "virtual",
            "description": "Test interfaces",
        },
    )

Direct lifecycle:

from norfab.core.nfapi import NorFab

nf = NorFab(inventory="./inventory.yaml")
try:
    nf.start()
    client = nf.make_client()

    result = client.run_job(
        "netbox",
        "create_device_interfaces",
        workers="any",
        kwargs={
            "devices": ["switch-01"],
            "interfaces_data": [
                {"name": "Ethernet1", "type": "1000base-t", "mtu": 1500},
                {"name": "Port-Channel1", "type": "lag"},
            ],
        },
    )
finally:
    nf.destroy()

Interface Types¤

Common interface types supported by NetBox:

  • virtual - Virtual interfaces (loopbacks, tunnel interfaces)
  • lag - Link Aggregation Group (Port-Channel, bond)
  • 1000base-t - 1G copper Ethernet
  • 10gbase-x-sfpp - 10G SFP+ Ethernet
  • 25gbase-x-sfp28 - 25G SFP28 Ethernet
  • 40gbase-x-qsfpp - 40G QSFP+ Ethernet
  • 100gbase-x-qsfp28 - 100G QSFP28 Ethernet
  • other - Generic interface type (default)

Refer to your NetBox instance for the complete list of available interface types.

Error Handling¤

Task handles several error conditions gracefully:

  1. Non-existent Device: If a device does not exist in NetBox, an error is logged but processing continues for other devices
  2. Duplicate Interfaces: Existing interfaces are automatically skipped and listed in the skipped array
  3. Invalid Interface Type: NetBox will reject invalid interface types with an error message
  4. Branch Not Found: If a specified branch does not exist and the branching plugin is not available, the task will fail

Best Practices¤

  1. Use Dry Run First: Always test with dry_run: True before creating interfaces in production
  2. Meaningful Names: Use descriptive interface names that match your device's actual interface naming
  3. Consistent Types: Use appropriate interface types that match the physical/virtual nature of the interfaces
  4. Batch Operations: Create multiple interfaces in a single call for efficiency
  5. Branch Usage: Use branches for testing bulk operations before committing to the main database
  6. Idempotency: The task is idempotent - running it multiple times with the same parameters is safe

NORFAB Netbox Create Device Interfaces Command Shell Reference¤

NorFab shell supports these command options for Netbox create_device_interfaces task:

nf# man tree netbox.create.device-interfaces
root
└── netbox:    Netbox service
    └── create:    Create objects in Netbox
        └── device-interfaces:    Create devices interfaces
            ├── instance:    Netbox instance name to target
            ├── dry-run:    Do not commit to database
            ├── branch:    Branching plugin branch name to use
            ├── *devices:    List of device names or device objects to create interfaces for
            ├── *interface_name:    Name(s) of the interface(s) to create
            ├── interface-type:    Interface type value, for example 'other', 'virtual', 'lag', or '1000base-t', default 'other'
            ├── description:    Interface description
            ├── speed:    Interface speed in Kbps
            ├── mtu:    Maximum transmission unit size in bytes
            ├── timeout:    Job timeout
            ├── workers:    Filter worker to target, default 'any'
            ├── verbose-result:    Control output details, default 'False'
            └── progress:    Display progress events, default 'True'
nf#

Python API Reference¤

Create interfaces for one or more devices in NetBox. This task creates interfaces in bulk and only if interfaces does not exist in Netbox.

Parameters:

Name Type Description Default
job Job

The job object containing execution context and metadata.

required
devices list

List of device names or device objects to create interfaces for.

required
interface_name Union[list, str]

Name(s) of the interface(s) to create. Can be a single interface name as a string or multiple names as a list. Alphanumeric ranges are supported for bulk creation:

  • Ethernet[1-3] -> Ethernet1, Ethernet2, Ethernet3
  • [ge,xe]-0/0/[0-9] -> ge-0/0/0, ..., xe-0/0/0 etc.
None
interface_type str

Type of interface (e.g., "other", "virtual", "lag", "1000base-t"). Defaults to "other".

'other'
instance Union[None, str]

NetBox instance identifier to use. If None, uses the default instance. Defaults to None.

None
dry_run bool

If True, simulates the operation without making actual changes. Defaults to False.

False
branch str

NetBox branch to use for the operation. Defaults to None.

None
kwargs dict

Any additional interface attributes

{}
interfaces_data list

List of per-interface payload dictionaries. Each dictionary supports all NetBox interface create fields and must include name. This is used for true bulk create with heterogeneous interface attributes.

None

Returns:

Name Type Description
Result Result

Result object containing the task name, execution results, and affected resources. The result dictionary contains status and details of interface creation operations.

Source code in norfab\workers\netbox_worker\interfaces_tasks.py
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
@Task(
    fastapi={"methods": ["GET"], "schema": NetboxFastApiArgs.model_json_schema()},
    input=CreateDeviceInterfacesInput,
    output=CreateDeviceInterfacesResult,
    mcp={
        "annotations": {
            "title": "Create Device Interfaces",
            "readOnlyHint": False,
            "destructiveHint": False,
            "idempotentHint": False,
            "openWorldHint": True,
        }
    },
)
def create_device_interfaces(
    self,
    job: Job,
    devices: list,
    interface_name: Union[None, list, str] = None,
    interfaces_data: Union[None, list] = None,
    interface_type: str = "other",
    instance: Union[None, str] = None,
    dry_run: bool = False,
    branch: str = None,
    **kwargs: dict,
) -> Result:
    """
    Create interfaces for one or more devices in NetBox. This task creates interfaces in bulk and only
    if interfaces does not exist in Netbox.

    Args:
        job (Job): The job object containing execution context and metadata.
        devices (list): List of device names or device objects to create interfaces for.
        interface_name (Union[list, str]): Name(s) of the interface(s) to create. Can be a single
            interface name as a string or multiple names as a list. Alphanumeric ranges are
            supported for bulk creation:

            - Ethernet[1-3] -> Ethernet1, Ethernet2, Ethernet3
            - [ge,xe]-0/0/[0-9] -> ge-0/0/0, ..., xe-0/0/0 etc.

        interface_type (str, optional): Type of interface (e.g., "other", "virtual", "lag",
            "1000base-t"). Defaults to "other".
        instance (Union[None, str], optional): NetBox instance identifier to use. If None,
            uses the default instance. Defaults to None.
        dry_run (bool, optional): If True, simulates the operation without making actual changes.
            Defaults to False.
        branch (str, optional): NetBox branch to use for the operation. Defaults to None.
        kwargs (dict, optional): Any additional interface attributes
        interfaces_data (list, optional): List of per-interface payload dictionaries.
            Each dictionary supports all NetBox interface create fields and must
            include ``name``. This is used for true bulk create with heterogeneous
            interface attributes.

    Returns:
        Result: Result object containing the task name, execution results, and affected resources.
            The result dictionary contains status and details of interface creation operations.
    """
    instance = instance or self.default_instance
    result = {}
    kwargs = kwargs or {}
    ret = Result(
        task=f"{self.name}:create_device_interfaces",
        result=result,
        resources=[instance],
    )
    nb = self._get_pynetbox(instance, branch=branch)
    log.info(
        f"{self.name} - Create device interfaces: Creating interfaces for {len(devices)} device(s) in '{instance}'"
    )

    payloads_by_name = {}
    if interfaces_data:
        for item in interfaces_data:
            payloads_by_name[str(item["name"])] = dict(item)
        all_interface_names = sorted(payloads_by_name.keys())
        job.event(
            f"received {len(all_interface_names)} interface create payload(s)"
        )
    else:
        # Normalize interface_name to a list and expand patterns
        interface_names = (
            [interface_name]
            if isinstance(interface_name, str)
            else (interface_name or [])
        )
        all_interface_names = []
        for name_pattern in interface_names:
            all_interface_names.extend(expand_alphanumeric_range(name_pattern))
        job.event(
            f"expanded interface names to {len(all_interface_names)} interface(s)"
        )

    # Process each device
    for device_name in devices:
        result[device_name] = {
            "created": [],
            "skipped": [],
        }

        nb_device = nb.dcim.devices.get(name=device_name)
        if not nb_device:
            msg = f"device '{device_name}' not found in NetBox"
            ret.errors.append(msg)
            job.event(msg, severity="WARNING")
            log.warning(f"{self.name} - {msg}")
            continue

        existing_interface_names = {
            intf.name
            for intf in self.bulk_filter(nb.dcim.interfaces, device=device_name)
        }
        interfaces_to_create = []

        for intf_name in all_interface_names:
            if intf_name in existing_interface_names:
                result[device_name]["skipped"].append(intf_name)
                job.event(
                    f"skipping '{intf_name}' on '{device_name}' - already exists"
                )
                continue
            if payloads_by_name:
                intf_data = {"device": nb_device.id, **payloads_by_name[intf_name]}
                intf_data.setdefault("type", interface_type)
            else:
                intf_data = {
                    "device": nb_device.id,
                    "name": intf_name,
                    "type": interface_type,
                    **kwargs,
                }
            interfaces_to_create.append(intf_data)
            result[device_name]["created"].append(intf_name)

        if interfaces_to_create:
            if dry_run is True:
                job.event(
                    f"dry-run, would create {len(interfaces_to_create)} interface(s) on '{device_name}'"
                )
            else:
                try:
                    nb.dcim.interfaces.create(interfaces_to_create)
                    msg = f"created {len(interfaces_to_create)} interface(s) on '{device_name}'"
                    job.event(msg)
                    log.info(f"{self.name} - {msg}")
                except Exception as e:
                    msg = f"failed to create interfaces on '{device_name}': {e}"
                    ret.errors.append(msg)
                    log.error(f"{self.name} - {msg}")
                    job.event(msg, severity="ERROR")

    return ret