Skip to content

NetBox Create VLAN Task¤

task API name: create_vlan

The create_vlan task creates or updates one VLAN in an existing VLAN group. Supply vid for an explicit VLAN ID, or omit it to allocate the next available ID from the group's configured ranges.

Inputs¤

Parameter Required Default Description
vlan_group Yes — Existing VLAN group name
name Yes — VLAN name
vid No None Explicit VLAN ID from 1 through 4094; omit to allocate
status No active VLAN status
description No None VLAN description
tenant No None Tenant name
role No None IPAM role name
tags No None Tag names
custom_fields No None NetBox custom-field values
instance No Worker default NetBox instance to target
dry_run No False Return the selected VID without writing
branch No None NetBox Branching plugin branch name

Output¤

{
    "vid": 1000,
    "name": "USERS",
    "vlan_group": "CAMPUS",
    "status": "created",
}

status is create or update during dry-run and created or updated after an applied operation.

Examples¤

A dedicated NFCLI command is not currently registered for create_vlan.

# Use the Python API, REST API, MCP task interface, or design filter.
from norfab.core.nfapi import NorFab

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

    allocated = client.run_job(
        "netbox",
        "create_vlan",
        workers="any",
        kwargs={"vlan_group": "CAMPUS", "name": "USERS"},
    )

    explicit = client.run_job(
        "netbox",
        "create_vlan",
        workers="any",
        kwargs={
            "vlan_group": "CAMPUS",
            "name": "SERVERS",
            "vid": 1100,
        },
    )

In a NetBox design:

{% set users_vid = "CAMPUS" | netbox.create_vlan("USERS") %}

vlans:
  - group: CAMPUS
    vid: {{ users_vid }}
    name: USERS

Notes / Gotchas¤

  • The VLAN group must already exist and have available VID ranges for allocation.
  • With an explicit vid, identity is VLAN group plus VID.
  • Without vid, an existing VLAN with the same group and name is reused.
  • Branch writes require the NetBox Branching plugin.

Troubleshooting¤

  • VLAN group not found: verify the exact group name in NetBox.
  • No available VLAN IDs: add or expand the group's VID ranges.
  • Ambiguous identity: remove duplicate VLANs with the same group and identity fields.

Task Command Shell Reference¤

create_vlan does not currently have a dedicated NFCLI command model.

Python API Reference¤

Create, update, or allocate one VLAN in a VLAN group.

Source code in norfab\workers\netbox_worker\vlan_tasks.py
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
@Task(
    fastapi={"methods": ["POST"], "schema": NetboxFastApiArgs.model_json_schema()},
    input=CreateVlanInput,
    output=CreateVlanResult,
    mcp={
        "annotations": {
            "title": "Create VLAN",
            "readOnlyHint": False,
            "destructiveHint": False,
            "idempotentHint": True,
            "openWorldHint": True,
        }
    },
)
def create_vlan(
    self,
    job: Job,
    vlan_group: str,
    name: str,
    vid: Union[None, int] = None,
    status: str = "active",
    description: Union[None, str] = None,
    tenant: Union[None, str] = None,
    role: Union[None, str] = None,
    tags: Union[None, list] = None,
    custom_fields: Union[None, dict] = None,
    instance: Union[None, str] = None,
    dry_run: bool = False,
    branch: Union[None, str] = None,
) -> Result:
    """Create, update, or allocate one VLAN in a VLAN group."""
    instance = instance or self.default_instance
    ret = Result(
        task=f"{self.name}:create_vlan",
        result={},
        resources=[instance],
        dry_run=dry_run,
    )
    nb = self._get_pynetbox(instance, branch=branch, job=job)
    group = nb.ipam.vlan_groups.get(name=vlan_group)
    if not group:
        raise ValueError(f"VLAN group '{vlan_group}' not found in NetBox")

    filters = (
        {"group_id": group.id, "vid": vid}
        if vid
        else {
            "group_id": group.id,
            "name": name,
        }
    )
    matches = self.bulk_filter(nb.ipam.vlans, **filters)
    if len(matches) > 1:
        raise ValueError(f"VLAN identity {filters} matched more than one VLAN")
    nb_vlan = matches[0] if matches else None

    if not nb_vlan and vid is None:
        available = group.available_vlans.list()
        if not available:
            raise ValueError(f"VLAN group '{vlan_group}' has no available VLAN IDs")
        vid = int(getattr(available[0], "vid", available[0]))
    if dry_run:
        ret.result = {
            "vid": int(nb_vlan.vid) if nb_vlan else vid,
            "name": name,
            "vlan_group": vlan_group,
            "status": "update" if nb_vlan else "create",
        }
        return ret

    payload = {"name": name, "status": status}
    if description is not None:
        payload["description"] = description
    if tenant is not None:
        payload["tenant"] = {"name": tenant}
    if role is not None:
        payload["role"] = {"name": role}
    if tags is not None:
        payload["tags"] = [{"name": tag} for tag in tags]
    if custom_fields is not None:
        payload["custom_fields"] = custom_fields

    if nb_vlan:
        nb_vlan.update(payload)
        ret.status = "updated"
    elif filters.get("vid") is None:
        nb_vlan = group.available_vlans.create(payload)
        ret.status = "created"
    else:
        nb_vlan = nb.ipam.vlans.create({"vid": vid, "group": group.id, **payload})
        ret.status = "created"

    ret.result = {
        "vid": int(nb_vlan.vid),
        "name": nb_vlan.name,
        "vlan_group": vlan_group,
        "status": ret.status,
    }
    job.event(f"{ret.status} VLAN {nb_vlan.vid} '{nb_vlan.name}'")
    return ret