New to Kestra?
Use blueprints to kickstart your first workflows.
Audit Azure Table Storage for entities stuck in a failed status with a server-side OData filter and alert Slack.
A background job queue, sync worker, or device registry built on Azure Table Storage can leave entities stuck on a failed status forever - Table Storage itself has no scheduler and no "alert me if entities match this filter" primitive. This blueprint runs a server-side OData filter for a specific stuck-status value and alerts Slack with the count the moment any exist.
periodic_stuck_entity_check (io.kestra.plugin.core.trigger.Schedule) runs every 15 minutes, explicitly passing storage_account_endpoint, table_name, status_field, stuck_status_value, and max_entities_to_check through its own inputs: block so the flow is self-contained when the trigger fires. Shipped disabled so a manual run can be verified first.probe_stuck_entities (io.kestra.plugin.azure.storage.table.List) runs an OData eq filter on status_field server-side, capped at max_entities_to_check, returning count.evaluate_stuck_entities (io.kestra.plugin.core.flow.If) branches on whether count > 0.then (NEEDS_ATTENTION): alert_stuck_entities reports the count to Slack.else (HEALTHY): log_table_healthy records a clean check.log_audit_complete always runs last, printing the stuck count regardless of which branch fired.errors block alerts Slack separately if the flow itself fails outright - an unreachable storage account or bad credentials must not read as HEALTHY.maxFiles) so a large stuck-entity backlog cannot turn a routine health check into an expensive, unbounded read.io.kestra.plugin.azure.storage.table.List's filter property, which every example in the plugin's own documentation leaves unused.List's OData filter used as a governance probe rather than a bulk export step.Table Storage itself has no concept of a schedule or a "notify me when this filter matches" trigger - checking requires either instrumenting every writer with its own alerting or someone remembering to run the query by hand. Kestra supplies the schedule, the HEALTHY/NEEDS_ATTENTION classification as a first-class branch, and an execution history that shows exactly when a backlog started and how large it grew.
storage_account_endpoint, table_name, status_field), or Azurite for local testing.Local testing (Azurite, no Azure account needed):
docker run -d --name azurite -p 10000:10000 -p 10001:10001 -p 10002:10002 mcr.microsoft.com/azure-storage/azurite
docker network connect YOUR_KESTRA_NETWORK azurite
Run docker network ls first to find the active Kestra network name. Azurite's table service listens on port 10002 by default, which does not collide with Kestra's own UI on port 8080. Set storage_account_endpoint to http://127.0.0.1:10002/devstoreaccount1 (or http://azurite:10002/devstoreaccount1 from inside the Kestra worker's Docker network), and set AZURE_STORAGE_CONNECTION_STRING to Azurite's own publicly documented, fixed well-known connection string (AccountName=devstoreaccount1 with its standard published key) - this is not a real secret, it is the same public value every Azurite instance ships with. Create the table and a test entity with the az CLI or any Table Storage SDK pointed at that same endpoint before running the flow.
AZURE_STORAGE_CONNECTION_STRING: connection string used by io.kestra.plugin.azure.storage.table.List. Against Azurite, this is the public well-known value described above, not a secret that needs protecting.SLACK_WEBHOOK_URL: Slack incoming webhook used by alert_stuck_entities and the errors block.SECRET_, base64-encoded - for example SECRET_SLACK_WEBHOOK_URL=$(echo -n '<your-webhook-url>' | base64) - and read back in flows with {{ secret('NAME') }}.storage_account_endpoint (STRING, default https://<your-account>.table.core.windows.net, must be replaced): Table Storage endpoint.table_name (STRING, default syncjobs): table to audit.status_field (STRING, default Status): property compared with eq.stuck_status_value (STRING, default FAILED): value identifying a stuck entity.max_entities_to_check (INT, default 100): cap on entities returned per run.outputs.probe_stuck_entities.count: number of matching entities found this run.outputs.probe_stuck_entities.uri: Kestra storage URI of the full matching entity list, in case triage needs more than the count.syncjobs table with one entity whose Status is not FAILED.AZURE_STORAGE_CONNECTION_STRING (Azurite's public well-known value) and SLACK_WEBHOOK_URL secrets.log_table_healthy fires.Status set to FAILED and re-run to confirm alert_stuck_entities fires with a count of at least 1.periodic_stuck_entity_check once you trust the check.table_name / stuck_status_value combinations with io.kestra.plugin.core.flow.Loop to audit multiple job queues' stuck-entity states in one run.PartitionKey eq '...' clause to scope the check to one tenant or device family within a shared table.status_field value that does not match the casing used when entities were written will silently return zero matches, reading as HEALTHY.maxFiles caps the count this blueprint sees, not the true backlog size. If the real stuck-entity count exceeds max_entities_to_check, count reports the capped value and the task logs a warning (visible in the execution log) rather than the true total.List task's filter/maxFiles properties and Output fields are taken from the plugin's own source on main, and the partition scalability figures are taken from Microsoft's own Table Storage documentation - but no Docker container or Kestra engine was run to execute this flow end to end.