New to Kestra?
Use blueprints to kickstart your first workflows.
Audit a phpIPAM subnet for in-use addresses with no hostname or description and alert Slack naming the real IPs found.
An IP address marked in-use in phpIPAM with no hostname and no description is a ghost allocation, something is consuming that address, the record even says so, but nobody looking at phpIPAM today can tell what it is or who owns it. Subnet capacity dashboards do not catch this, a fully-utilized subnet full of unlabeled addresses looks exactly as healthy as one full of well-documented ones. This blueprint lists a subnet's addresses on a schedule, finds the real ones marked in-use with both fields blank, and alerts Slack naming the exact IPs, never an invented count.
probe_subnet_addresses (io.kestra.plugin.phpipam.ipam.address.List) reads every
address recorded for subnet_id, including its tag, hostname, and description,
with allowFailure: true so a bad URL, app ID, or token does not stop the flow before
it can be classified as PROBE_FAILED below.classify_address_hygiene_status (io.kestra.plugin.core.debug.Return) returns
PROBE_FAILED when the probe did not return at all, CRITICAL or WARNING from the
real count of addresses where tag equals used_tag_id and both hostname and
description are blank, against the two thresholds, HEALTHY otherwise.route_by_address_hygiene_status (io.kestra.plugin.core.flow.Switch) branches on
that status: HEALTHY logs it, the other three each alert Slack, the WARNING and
CRITICAL alerts name the real unlabeled IPs found, never an invented list. An
unexpected value falls through to a defaults branch that logs it rather than being
silently dropped.log_audit_summary always runs last and logs the final classification.errors block posts to Slack if the flow itself fails outside the gate logic
above.ipam.address.List and the plugin's app-token authentication.phpIPAM records whether an address is in use, it has no built-in alert for an in-use address with no owner or hostname attached. Kestra adds that check as a scheduled, declarative flow: the probe, the classification against your own thresholds, and the Slack alert naming the real IPs are all one auditable execution history.
For local testing, the official phpIPAM Docker Compose stack:
services:
phpipam-web:
image: phpipam/phpipam-www:latest
ports:
- "80:80"
environment:
- IPAM_DATABASE_HOST=phpipam-mariadb
- IPAM_DATABASE_PASS=my_secret_phpipam_pass
- IPAM_DATABASE_WEBHOST=%
depends_on:
- phpipam-mariadb
phpipam-mariadb:
image: mariadb:latest
environment:
- MYSQL_ROOT_PASSWORD=my_secret_mysql_root_pass
volumes:
- phpipam-db-data:/var/lib/mysql
volumes:
phpipam-db-data:
Open http://localhost, finish the setup wizard, then generate an App token under
Administration, API, with an application that has at least read permission.
A subnet with at least one address manually marked Used (tag 2 by default) and left
without a hostname or description, to see WARNING/CRITICAL rather than HEALTHY.
A Slack incoming webhook for alerts.
PHPIPAM_APP_TOKEN: the static App token generated in phpIPAM's API administration
panel.
SLACK_WEBHOOK: Slack incoming webhook URL used by the alert tasks and the errors
block.
In Kestra OSS, set these as base64-encoded environment variables, for example
SECRET_PHPIPAM_APP_TOKEN, SECRET_SLACK_WEBHOOK.
| Input | Type | Default | Allowed values |
|---|---|---|---|
base_url |
STRING | http://localhost |
any reachable phpIPAM base URL |
app_id |
STRING | myapp |
an application ID configured under Administration, API |
subnet_id |
STRING | 1 |
any existing subnet's numeric ID |
used_tag_id |
STRING | 2 |
any tag ID configured on this install |
unlabeled_count_warning_threshold |
INT | 1 |
any non-negative integer |
unlabeled_count_critical_threshold |
INT | 10 |
any non-negative integer, greater than the warning threshold |
insecure_tls |
BOOL | false |
true, false |
Used, and leave its hostname and
description blank.PHPIPAM_APP_TOKEN and SLACK_WEBHOOK secrets.subnet_id set to that subnet, it should report WARNING
and name the address you just created.daily trigger.io.kestra.plugin.core.flow.Loop across every subnet ID returned
by io.kestra.plugin.phpipam.ipam.subnet.List to audit an entire phpIPAM instance from
one flow.hostname but a blank owner, a different hygiene gap than
a fully blank record.mac to attempt auto-attribution
before falling back to a human-reviewed alert.used_tag_id defaults to phpIPAM's stock value of 2, but tags are configurable per
install. Confirm yours with GET /api/{appId}/addresses/tags/ before trusting the
default on an existing, customized instance.fetchType: FETCH loads every address in the subnet into memory at once, fine for a
typical subnet, switch to STORE and process the file instead for a very large one.description field in phpIPAM's API can come back as an empty string or be
entirely absent from the JSON response depending on how the address was created, the
// "" fallback in this gate's jq expressions treats both the same way.