An IP address handed out without a hostname is the start of a long-term mystery: six months
later nobody can tell what is actually using it, whether it is safe to reclaim, or who to ask.
phpIPAM records every new address as it is assigned, but it has no policy that catches a
blank hostname the moment it happens, only a static record waiting for someone to notice.
This blueprint watches a subnet in real time, checks every new address against a
hostname-required policy, and can flag the gap for review automatically once you have
explicitly said this flow is allowed to.
This blueprint was created by Mohit Jeswani.
How it works
watch_subnet (io.kestra.plugin.phpipam.ipam.address.NewAddressTrigger) polls a
subnet and fires one execution per newly detected address, deduplicated via the
namespace KV store so each address fires exactly once.
get_address (io.kestra.plugin.phpipam.ipam.address.Get) re-fetches the full address
record by trigger.addressId, reading the authoritative current hostname rather than
trusting the trigger's own summary. Read-only; nothing is changed here.
evaluate (io.kestra.plugin.core.output.OutputValues) classifies the address as
COMPLIANT, NONCOMPLIANT_AUTO_FLAG, NONCOMPLIANT_NEEDS_HUMAN, or PROBE_FAILED,
folding confirm_flag_noncompliant into the non-compliant classification itself.
route (io.kestra.plugin.core.flow.Switch):
- COMPLIANT: logs the assigned hostname.
- NONCOMPLIANT_AUTO_FLAG: runs
io.kestra.plugin.phpipam.ipam.address.Update (the
safe remediation, since writing a review note into the description changes no network
configuration), then posts a Slack notice.
- NONCOMPLIANT_NEEDS_HUMAN / PROBE_FAILED: posts a Slack alert instead of flagging.
summary logs the severity, closing the governance cycle.
errors alerts Slack if the flow itself fails, for example an expired app token.
What you get
- A hostname policy enforced the moment an address is assigned, not discovered during a
quarterly subnet audit.
- A flag that only ever edits the address's description, never its hostname or network
configuration, and only once you have explicitly set
confirm_flag_noncompliant to allow
it.
- A real-time Slack alert naming the exact IP and subnet, so whoever owns that subnet can
chase down the assignment while it is still fresh.
Who it's for
- Network engineering and IPAM-owning teams who want a hostname-required policy enforced on
every new address, not just checked during an occasional sweep.
- Teams evaluating the phpIPAM plugin who want a worked example of
address.NewAddressTrigger used as a standing event-driven compliance gate, distinct from
this repo's existing phpIPAM blueprints, which scan a subnet on a schedule for capacity,
stale reservations, or VLAN/VRF drift rather than reacting to a single new assignment in
real time.
Why orchestrate this with Kestra
phpIPAM records a new address as it happens, but it has no hostname policy, no escalation,
and no Slack integration of its own. Kestra keeps confirm_flag_noncompliant as an explicit
flow input, turns a trigger detection into a policy decision, and keeps a full execution
history of every address this flow has seen and what it did about it.
Prerequisites
- A phpIPAM instance with the API enabled and an app token with read and update access to
the watched subnet's addresses.
- At least one address you can create without a hostname, to confirm the non-compliant path.
- A Slack incoming webhook URL.
A local phpIPAM instance can be run via the official phpipam/phpipam-www and
phpipam/phpipam-cron Docker images (with a MariaDB/MySQL container) for a fully local
end-to-end test, no managed account required.
Secrets
PHPIPAM_APP_TOKEN: the phpIPAM API app token used by get_address and flag_address,
and by the watch_subnet trigger. In Kestra OSS, provide it as the base64-encoded
environment variable SECRET_PHPIPAM_APP_TOKEN.
SLACK_WEBHOOK_URL: used by all three Slack alert cases and the errors block. In Kestra
OSS, provide it as the base64-encoded environment variable SECRET_SLACK_WEBHOOK_URL.
Inputs
| Input |
Default |
Purpose |
base_url |
http://localhost |
phpIPAM instance root URL |
app_id |
myapp |
phpIPAM API application identifier |
confirm_flag_noncompliant |
false |
Authorizes this flow to flag a missing hostname itself |
The watched subnet is set directly on the watch_subnet trigger's subnetId, not as a flow
input; see Pitfalls.
Outputs
{{ trigger.addressId }} / .ip / .hostname / .subnetId: the newly detected address,
as NewAddressTrigger reports it.
{{ outputs.get_address.address }}: the full, re-fetched address record (hostname,
description, owner, tag, mac, and more).
{{ outputs.evaluate.values.severity }}: COMPLIANT, NONCOMPLIANT_AUTO_FLAG,
NONCOMPLIANT_NEEDS_HUMAN, or PROBE_FAILED.
Pitfalls
NewAddressTrigger's own Javadoc example uses io.kestra.core.tasks.log.Log for its
companion Log task, which is not a real Kestra class; the correct, verified class is
io.kestra.plugin.core.log.Log, used throughout this blueprint instead. Flagging this as a
stale example in the plugin's own source, not something this blueprint itself does.
address.Update's field for the address description is named resourceDescription, not
description, even though it updates phpIPAM's description field; verified directly
against the task's source, since its own Javadoc example does not show this field at all.
subnetId, baseUrl, appId, and auth are trigger-level configuration, not flow
inputs; a polling trigger has no per-execution input overrides the way a Schedule
trigger does, so watching a different subnet means editing the trigger block directly.
NewAddressTrigger emits one execution per newly detected address, one at a time per poll
cycle (the remaining new addresses in the same poll fire on the next cycle); a burst of
address assignments produces a burst of flow executions and possible Slack alerts, not one
aggregated message.
Quick start
- Add the
PHPIPAM_APP_TOKEN and SLACK_WEBHOOK_URL secrets.
- Run phpIPAM locally via Docker, create a subnet, and set
watch_subnet's subnetId to
its real ID.
- Enable the trigger, assign a new address with a hostname set, and confirm
log_compliant fires.
- Assign another address with no hostname and
confirm_flag_noncompliant: false; confirm
alert_noncompliant fires.
- Set
confirm_flag_noncompliant: true, assign a third address with no hostname, and
confirm flag_address writes the review note and alert_auto_flagged fires.
How to extend
- Extend
evaluate to also require a non-blank owner or description, not just a
hostname, for a stricter asset-tracking policy.
- Chain a lookup of the subnet's VLAN via
io.kestra.plugin.phpipam.ipam.subnet.Get to
route the Slack alert to the team that owns that VLAN instead of one shared channel.
- Watch several subnets by deploying one copy of this flow per
watch_subnet trigger
configuration, one per subnet your team is responsible for.
Links