Phpipam Configuration
This role populates phpIPAM with the homelab network infrastructure via the phpIPAM REST API.
phpIPAM Configuration Role
Overview
This role populates phpIPAM with the homelab network infrastructure via the phpIPAM REST API. It creates sections, VLANs, subnets, device types, devices, and IP addresses with full interface and switch port metadata. The role uses “App code with SSL” authentication and is fully idempotent.
Purpose
- IP Address Management: Register all static IPs in phpIPAM
- Device Inventory: Create devices with types, descriptions, and management IPs
- Network Documentation: Store interface names, switch port numbers, tagged/untagged info
- VLAN/Subnet Mapping: Associate subnets with VLANs under a dedicated section
- Gateway Tracking: Mark gateway addresses (OPNsense interfaces, ISP router)
- Code as Configuration: Define the entire network infrastructure in YAML
- Idempotent: Safe to run multiple times - existing resources are skipped
Requirements
- Ansible 2.9 or higher
- phpIPAM instance with API access enabled
- API application created with “App code with SSL” authentication
- API token stored in Ansible Vault as
vault_phpipam_configuration_api_token - Network connectivity to the phpIPAM instance
- Valid SSL certificate on phpIPAM (or set
validate_certs: false)
Role Variables
Required Variables
| Variable | Required | Description |
|---|---|---|
vault_phpipam_configuration_api_token | Yes | phpIPAM API token (in vault) |
phpipam_configuration_vlans | Yes | List of VLANs and subnets (loaded from vars/vlans.yml) |
phpipam_configuration_device_types | Yes | List of device types (loaded from vars/device_types.yml) |
phpipam_configuration_devices | Yes | List of devices and their addresses (loaded from vars/devices.yml) |
Optional Variables
| Variable | Default | Description |
|---|---|---|
phpipam_configuration_api_url | https://server.homelab/api | phpIPAM API base URL |
phpipam_configuration_api_app_id | ansible | phpIPAM API application ID |
phpipam_configuration_section_name | Homelab | Section name in phpIPAM |
phpipam_configuration_section_description | Homelab infrastructure | Section description |
Variable Details
phpipam_configuration_vlans
List of VLANs and their associated subnets. Defined in vars/vlans.yml.
Structure:
phpipam_configuration_vlans:
- name: "VLAN10"
number: 10
description: "Management network"
subnet: "192.168.x.x"
mask: 24
Field descriptions:
| Field | Required | Description |
|---|---|---|
name | Yes | VLAN display name (used as key for idempotency and subnet association) |
number | Yes | 802.1Q VLAN ID (0 for non-VLAN networks like Wireguard) |
description | Yes | Human-readable description of the network purpose |
subnet | Yes | Network address of the subnet |
mask | Yes | CIDR prefix length |
phpipam_configuration_device_types
List of device types to create. Defined in vars/device_types.yml.
Structure:
phpipam_configuration_device_types:
- name: "Server"
description: "Server / Virtual machine"
Field descriptions:
| Field | Required | Description |
|---|---|---|
name | Yes | Device type name (must match the type field in devices) |
description | Yes | Human-readable description of the device category |
Note: phpIPAM ships with built-in types (Switch, Router, Firewall, Hub, Wireless, Database, Workstation, Laptop, Other). Existing types are skipped automatically.
phpipam_configuration_devices
List of devices with their IP addresses and metadata. Defined in vars/devices.yml.
Structure:
phpipam_configuration_devices:
- hostname: "grafana"
type: "Server"
description: "Grafana monitoring server"
management_ip: "{{ hostvars['grafana']['ip_vlan10'] }}"
addresses:
- ip: "{{ hostvars['grafana']['ip_vlan10'] }}"
vlan: "VLAN10"
interface: "ens19"
- ip: "{{ hostvars['grafana']['ip_vlan12'] }}"
vlan: "VLAN12"
interface: "ens18"
Device field descriptions:
| Field | Required | Description |
|---|---|---|
hostname | Yes | Device hostname (used as key for idempotency) |
type | Yes | Device type name (must match an entry in device_types.yml) |
description | Yes | Human-readable description (stored on the device record) |
management_ip | Yes | Primary management IP (used as ip_addr in phpIPAM) |
addresses | Yes | List of IP addresses to register for this device |
Address field descriptions:
| Field | Required | Default | Description |
|---|---|---|---|
ip | Yes | - | IP address to register |
vlan | Yes | - | VLAN name (must match an entry in vlans.yml) |
interface | Yes | - | Network interface name (stored in phpIPAM port field) |
is_gateway | No | false | Mark as gateway address |
note | No | "" | Free text (switch port, tagged/untagged, trunk info) |
IP sources: IPs from inventory hosts use hostvars lookups. IPs for devices not in inventory are hardcoded directly.
Dependencies
This role has no dependencies on other Ansible roles, but requires:
- phpIPAM instance running and accessible
- API application with “App code with SSL” authentication
- Ansible Vault for storing the API token
- Inventory must be loaded before running (for
hostvarslookups)
Workflow
- Create section: Create the “Homelab” section if it doesn’t exist
- Create VLANs: Create VLANs that don’t already exist (matched by name)
- Create subnets: Create subnets linked to their VLANs and section (matched by subnet address)
- Create device types: Create device types that don’t already exist (matched by name)
- Create devices: Create devices with management IP, type, and description (matched by hostname)
- Create IP addresses: Register IPs linked to devices, with interface, note, and gateway flag (matched by IP)
All steps are idempotent: existing resources are skipped.
Example Playbook
---
- name: Configure phpIPAM with homelab infrastructure
hosts: mint-vm
gather_facts: false
pre_tasks:
- name: Load VLANs configuration
ansible.builtin.include_vars:
file: "{{ playbook_dir }}/../roles/phpipam_configuration/vars/vlans.yml"
- name: Load device types configuration
ansible.builtin.include_vars:
file: "{{ playbook_dir }}/../roles/phpipam_configuration/vars/device_types.yml"
- name: Load devices configuration
ansible.builtin.include_vars:
file: "{{ playbook_dir }}/../roles/phpipam_configuration/vars/devices.yml"
roles:
- role: phpipam_configuration
phpIPAM API Endpoints
Sections
GET /api/{app_id}/sections/ - List all sections
POST /api/{app_id}/sections/ - Create a section
VLANs
GET /api/{app_id}/vlan/ - List all VLANs
POST /api/{app_id}/vlan/ - Create a VLAN
Note: VLAN IDs are returned as vlanId (not id).
Subnets
GET /api/{app_id}/subnets/ - List all subnets
POST /api/{app_id}/subnets/ - Create a subnet
Device Types
GET /api/{app_id}/tools/device_types/ - List all device types
POST /api/{app_id}/tools/device_types/ - Create a device type
Note: Device type fields use tname, tdescription, and tid (not name, description, id).
Devices
GET /api/{app_id}/devices/ - List all devices
POST /api/{app_id}/devices/ - Create a device
Addresses
GET /api/{app_id}/addresses/ - List all addresses
POST /api/{app_id}/addresses/ - Create an address
Address fields used: ip, subnetId, hostname, description, deviceId, port (interface name), note, is_gateway.
Note: The description field on addresses has a character limit in MariaDB. The role uses the device hostname as description to stay within this limit.
Idempotency
The role is idempotent:
- Sections: Matched by name, skipped if exists
- VLANs: Matched by name, skipped if exists
- Subnets: Matched by subnet address, skipped if exists
- Device types: Matched by
tname, skipped if exists - Devices: Matched by hostname, skipped if exists
- IP addresses: Matched by IP, skipped if exists
First run: Creates all resources, reports changed
Subsequent runs: No changes, reports ok
Security Considerations
- API Token: Stored in Ansible Vault, never in plaintext
- HTTPS: All API calls use SSL/TLS
- Token Authentication: Uses phpIPAM “App code with SSL” method
- validate_certs: Enabled by default, validates SSL certificates
File Structure
roles/phpipam_configuration/
├── README.md # This file
├── defaults/main.yml # API config, section name, empty list defaults
├── tasks/main.yml # API tasks with idempotency checks
└── vars/
├── vlans.yml # VLANs and subnets definitions
├── device_types.yml # Device type definitions
└── devices.yml # Devices with IP addresses and metadata
Troubleshooting
”Data too long for column ‘description’” error
Cause: Device descriptions that are too long are passed to the address description field, which has a column length limit in MariaDB.
Solution: The role uses the device hostname as the address description. Long descriptions should only be on the device record, not addresses.
”object of type ‘dict’ has no attribute ‘id’” error
Cause: phpIPAM API uses non-standard field names for IDs.
Solution: Use the correct field names:
- VLANs:
vlanId(notid) - Device types:
tid(notid) - Subnets and sections:
id(standard)
Device types skipped during creation
Cause: phpIPAM ships with built-in device types (Switch, Router, Firewall, etc.).
Solution: This is expected. The role only creates types that don’t already exist.
Fresh install required
To start clean, drop and recreate the phpIPAM database:
docker exec -it <mariadb_container> mysql -u root -p -e "
DROP DATABASE phpipam;
CREATE DATABASE phpipam;
GRANT ALL PRIVILEGES ON phpipam.* TO 'phpipam_db_user'@'%';
FLUSH PRIVILEGES;
"
docker restart <phpipam_container>
Then redo the initial phpIPAM setup (admin password, API app creation) before re-running the role.
License
MIT
Author
Created for homelab infrastructure management.