Schedule icon
Get icon
Request icon
Return icon
Switch icon
Log icon
SlackIncomingWebhook icon

phpIPAM Subnet Capacity Sentinel

Check a phpIPAM subnet's free IP address capacity on a schedule, change nothing in phpIPAM, and alert Slack before it runs out.

Categories
CloudInfrastructure

A subnet quietly fills up one DHCP lease and one static assignment at a time, and nothing in phpIPAM pages anyone until a new device fails to get an address. This blueprint turns a scheduled, read-only subnet lookup into a Day 2 resource-quota gate: io.kestra.plugin.phpipam.ipam.subnet.Get reports the subnet's current free-address percentage without creating, updating, or deleting anything, a single Pebble expression classifies that percentage against two thresholds, and the result routes to a quiet log, an informational Slack note, or a Slack page depending on severity.

How it works

  1. get_subnet (io.kestra.plugin.phpipam.ipam.subnet.Get) retrieves subnet_id from the phpIPAM instance at phpipam_base_url under application ID phpipam_app_id. The response includes subnet.usage, with used, maxHosts, freeHosts, freeHostsPercent, and offlinePercent as reported by phpIPAM's own calculation for that subnet. allowFailure: true keeps a server or lookup failure from aborting the flow outright.
  2. classify_subnet_capacity (io.kestra.plugin.core.debug.Return) first guards for a missing subnet/usage output (UNREACHABLE), then converts freeHostsPercent from phpIPAM's string representation to a number with a jq tonumber filter and classifies it: at or below critical_free_percent becomes CRITICAL, at or below warning_free_percent becomes WARNING, otherwise HEALTHY.
  3. route_by_capacity (io.kestra.plugin.core.flow.Switch) branches on that classification: HEALTHY logs a quiet confirmation; WARNING posts an informational Slack note; CRITICAL and UNREACHABLE each page Slack with the free-address count and percentage; a defaults fallback logs the raw value for any classification outside the four known cases.
  4. log_audit_result (io.kestra.plugin.core.log.Log) writes one audit line per run, independent of which Switch branch fired, carrying the free, used, and max host counts, so the check's own history stays queryable even if Slack delivery fails.
  5. The flow-level errors block pages Slack separately if the flow fails outright, so a broken sentinel is never mistaken for a subnet with healthy capacity.
  6. The subnet_capacity_schedule trigger checks every 6 hours and ships disabled: true, with explicit inputs: overrides for warning_free_percent and critical_free_percent so the scheduled run's thresholds are pinned regardless of this flow's own defaults.

What you get

  • A scheduled, read-only capacity check for any phpIPAM subnet with zero custom phpIPAM API scripting.
  • A single status output (HEALTHY / WARNING / CRITICAL / UNREACHABLE) other flows can gate on.
  • Three distinct Slack messages, one per severity, each carrying the exact free-host count and percentage.
  • An audit log line on every run, separate from Slack, so the check's own history survives a webhook outage.

Who it's for

  • Network engineering and infrastructure teams running phpIPAM as their IP address management system of record.
  • Platform teams who need to know a DHCP-serviced subnet is about to run out of addresses before a new device or VM fails to provision.
  • Teams standardizing Day 2 resource-quota governance across infrastructure systems inside Kestra instead of a cron job that emails a spreadsheet.

Why orchestrate this with Kestra

phpIPAM shows subnet utilization in its own dashboard, but nothing pages anyone when a subnet crosses a capacity threshold unless someone is actively looking. Kestra's scheduled, read-only Get check gives that page automatically: the schedule, the threshold classification, and the Slack call are all one auditable execution history, with the Switch branch ready to grow into real remediation (requesting a supernet split, opening a ticket) without rewriting the check itself.

Prerequisites

  • A phpIPAM instance reachable at phpipam_base_url, with API access enabled under Administration > API for application ID phpipam_app_id.
  • A phpIPAM API token (static app token auth) with read access to the target subnet.
  • A Slack incoming webhook for the WARNING, CRITICAL, UNREACHABLE, and flow-failure alerts.

Secrets

  • PHPIPAM_APP_TOKEN: phpIPAM static application API token used for authentication.
  • SLACK_WEBHOOK: Slack incoming webhook used for every alert branch and the flow-failure alert.

In Kestra OSS (no Enterprise secrets backend), secrets are supplied as environment variables prefixed SECRET_, base64-encoded, and read back in flows with {{ secret('NAME') }}, for example SECRET_SLACK_WEBHOOK=$(echo -n 'https://hooks.slack.com/services/<placeholder>' | base64). This keeps credentials out of the flow YAML but, per Kestra's own documentation, offers no encryption at rest or access control beyond the host environment; use the Enterprise secrets backend for stronger guarantees.

Quick start

  1. Start a local phpIPAM instance with its bundled MariaDB, mapping its web port off host 8080 since Kestra's own UI already uses it: docker run -d --name phpipam-app -p 8880:80 -e TZ=UTC -e IPAM_DATABASE_HOST=phpipam-db -e IPAM_DATABASE_PASS=phpipam phpipam/phpipam-www:latest docker run -d --name phpipam-db -e MYSQL_ROOT_PASSWORD=phpipam phpipam/phpipam-db:latest
  2. If Kestra is also running in Docker, check docker network ls for Kestra's network and attach the app container to it: docker network connect YOUR_KESTRA_NETWORK phpipam-app Then set phpipam_base_url to http://phpipam-app. From a host-based Kestra install, use http://localhost:8880 instead.
  3. Complete the phpIPAM web setup wizard, create a section and a small test subnet, then enable the API under Administration > API and create a static-token application to get phpipam_app_id and the app token.
  4. Add the PHPIPAM_APP_TOKEN and SLACK_WEBHOOK secrets, set subnet_id to the test subnet's numeric ID, and run the flow once manually to confirm status reads HEALTHY.
  5. Enable subnet_capacity_schedule once pointed at a real instance and subnet.

How to extend

  • Loop this flow's subnet_id input over every subnet a team owns using io.kestra.plugin.core.flow.Loop, instead of checking one subnet per execution.
  • Use io.kestra.plugin.phpipam.ipam.subnet.Search or List to discover all subnets under a section automatically, instead of hardcoding one subnet_id.
  • Chain a CRITICAL branch into a ticketing system to request a supernet split before addresses actually run out.
  • Push status into a KV store and alert only on state transitions instead of every run.

Inputs

  • phpipam_base_url: root URL of the phpIPAM instance.
  • phpipam_app_id: phpIPAM API application ID.
  • subnet_id: numeric ID of the subnet to check.
  • warning_free_percent: free-address percentage floor at or below which the subnet is classified WARNING.
  • critical_free_percent: free-address percentage floor at or below which the subnet is classified CRITICAL.

Outputs

  • status: one of HEALTHY, WARNING, CRITICAL, UNREACHABLE.
  • Example path: {{ outputs.classify_subnet_capacity.value }}, also exposed as the flow output {{ outputs.status }}.

Links

Pitfalls

  • phpIPAM reports usage fields (freeHostsPercent, freeHosts, maxHosts) as JSON strings, not numbers; this flow converts freeHostsPercent with a jq tonumber filter before comparing it to the integer thresholds. Confirm the field is still present before trusting the classification, since a renamed or missing field would otherwise be silently miscompared.
  • subnetId must be the subnet's internal numeric phpIPAM ID, not its CIDR notation; use List or Search to look up the ID if it is not already known.
  • This flow only ever calls subnet.Get; it never calls Create, Update, or Delete, so there is nothing destructive to default to dry-run for.
  • insecureTls exists on the connection for development instances with self-signed certificates; do not enable it against a production phpIPAM instance.
See How

New to Kestra?

Use blueprints to kickstart your first workflows.