New to Kestra?
Use blueprints to kickstart your first workflows.
Check a phpIPAM subnet's free IP address capacity on a schedule, change nothing in phpIPAM, and alert Slack before it runs out.
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.
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.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.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.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.errors block pages Slack separately if the flow fails outright, so a broken sentinel is never mistaken for a subnet with healthy capacity.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.status output (HEALTHY / WARNING / CRITICAL / UNREACHABLE) other flows can gate on.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.
phpipam_base_url, with API access enabled under Administration > API for application ID phpipam_app_id.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.
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:latestdocker 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.phpipam_app_id and the app token.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.subnet_capacity_schedule once pointed at a real instance and subnet.subnet_id input over every subnet a team owns using io.kestra.plugin.core.flow.Loop, instead of checking one subnet per execution.io.kestra.plugin.phpipam.ipam.subnet.Search or List to discover all subnets under a section automatically, instead of hardcoding one subnet_id.CRITICAL branch into a ticketing system to request a supernet split before addresses actually run out.status into a KV store and alert only on state transitions instead of every run.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.status: one of HEALTHY, WARNING, CRITICAL, UNREACHABLE.{{ outputs.classify_subnet_capacity.value }}, also exposed as the flow output {{ outputs.status }}.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.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.