Hi! I'm your Kestra AI assistant. Ask me anything about workflows.
EXAMPLE QUESTIONS
How to run a Ansible playbook?
How to write expression for previous tasks outputs?
How to set up CI/CD for kestra flows?
Orchestrate Azure without the Logic Apps glue.
Trigger on Blob and Data Lake arrivals, stream Event Hub and Service Bus messages, call Functions, and kick off Data Factory pipelines and Batch jobs, all from one flow. Chain storage, compute, and messaging into a single execution history instead of wiring Logic Apps, Event Grid subscriptions, and Function bindings between every pair of services.
Azure's Blob Storage, Event Hub, Service Bus, Functions, Batch, and Data Factory each work well alone, but chaining them today usually means a Logic App, an Event Grid subscription, and a Function binding wired one pair at a time. Kestra triggers on a blob arrival or an Event Hub message, calls a Function, publishes to Service Bus, and runs a Data Factory pipeline or a Microsoft Fabric job, all inside one execution history. Provision the underlying resources with Terraform and the whole estate runs from one control plane.
Route Azure Blob arrivals into a cross-cloud ETL and dbt Cloud buildOpen blueprint
Page on-call automatically when Event Hub volume dropsOpen blueprint
id: azure-eventhub-volume-alertnamespace: company.teamdescription: | Poll an Azure Event Hub on a schedule, count how many events arrived in the window, and page on-call automatically when volume drops below the expected minimum, catching a silent producer outage before downstream consumers notice missing data.tasks: - id: consume_batch type: io.kestra.plugin.azure.eventhubs.Consume description: Pull whatever landed in the last 30 seconds of the telemetry hub. eventHubName: telemetry namespace: kestra-eventhub-ns connectionString: "{{ secret('EVENTHUBS_CONNECTION') }}" bodyDeserializer: JSON maxDuration: PT30S checkpointStoreProperties: containerName: eventhub-checkpoints connectionString: "{{ secret('BLOB_CONNECTION') }}" - id: check_volume type: io.kestra.plugin.core.flow.If description: Compare the batch size against the expected floor for this window. condition: "{{ outputs.consume_batch.eventsCount < 5 }}" then: - id: alert_low_volume type: io.kestra.plugin.pagerduty.PagerDutyAlert description: Page on-call, the producer may be down. url: "{{ secret('PAGERDUTY_EVENT_URL') }}" payload: | { "routing_key": "{{ secret('PAGERDUTY_ROUTING_KEY') }}", "event_action": "trigger", "dedup_key": "azure-eventhub-telemetry-low-volume", "payload": { "summary": "Event Hub telemetry volume dropped to {{ outputs.consume_batch.eventsCount }} events in the last 30s window", "source": "kestra-azure-eventhub-monitor", "severity": "warning" } }triggers: - id: every_5_minutes type: io.kestra.plugin.core.trigger.Schedule cron: "*/5 * * * *"
Chain Blob, an Azure Function, Service Bus, and Data Factory in one flowOpen blueprint
id: azure-blob-function-servicebus-chainnamespace: company.teamdescription: | Chain Blob Storage, an Azure Function, Service Bus, and Data Factory in one execution. A blob arrival triggers a Function transform, the result is published to Service Bus for downstream consumers, and a Data Factory pipeline runs the heavier processing, all under one execution ID.tasks: - id: transform type: io.kestra.plugin.azure.function.HttpFunction description: Call the Azure Function that validates and reshapes the new blob. httpMethod: POST url: "https://{{ secret('AZURE_FUNCTION_APP') }}.azurewebsites.net/api/TransformOrder?code={{ secret('AZURE_FUNCTION_CODE') }}" httpBody: container: "{{ trigger.blobs[0].name }}" name: "{{ trigger.blobs[0].name }}" - id: publish_to_servicebus type: io.kestra.plugin.azure.servicebus.Publish description: Announce the transformed order to every downstream subscriber. queueName: orders-transformed tenantId: "{{ secret('AZURE_TENANT_ID') }}" clientId: "{{ secret('AZURE_CLIENT_ID') }}" clientSecret: "{{ secret('AZURE_CLIENT_SECRET') }}" serdeType: JSON from: body: "{{ outputs.transform.repsonseBody }}" - id: run_pipeline type: io.kestra.plugin.azure.datafactory.CreateRun description: Kick off the Data Factory pipeline that lands the order in the warehouse. factoryName: "{{ secret('ADF_FACTORY_NAME') }}" pipelineName: process_transformed_orders resourceGroupName: "{{ secret('AZURE_RESOURCE_GROUP') }}" subscriptionId: "{{ secret('AZURE_SUBSCRIPTION_ID') }}" tenantId: "{{ secret('AZURE_TENANT_ID') }}" clientId: "{{ secret('AZURE_CLIENT_ID') }}" clientSecret: "{{ secret('AZURE_CLIENT_SECRET') }}" - id: notify type: io.kestra.plugin.microsoft365.teams.TeamsIncomingWebhook description: Post the chain's outcome to the ops channel. url: "{{ secret('TEAMS_WEBHOOK') }}" payload: | { "type": "message", "attachments": [ { "contentType": "application/vnd.microsoft.card.adaptive", "content": { "$schema": "http://adaptivecards.io/schemas/adaptive-card.json", "type": "AdaptiveCard", "version": "1.4", "body": [ { "type": "TextBlock", "size": "Large", "weight": "Bolder", "text": "Blob to Data Factory chain complete" }, { "type": "TextBlock", "text": "Blob: {{ trigger.blobs[0].name }}", "wrap": true }, { "type": "TextBlock", "text": "Data Factory run: {{ outputs.run_pipeline.runId }}", "wrap": true } ] } } ] }triggers: - id: blob_landed type: io.kestra.plugin.azure.storage.blob.Trigger description: Fires when a new order file lands in the incoming container. interval: PT1M endpoint: "https://{{ secret('AZURE_STORAGE_ACCOUNT') }}.blob.core.windows.net" connectionString: "{{ secret('AZURE_CONNECTION_STRING') }}" container: incoming prefix: orders/ action: MOVE moveTo: container: incoming name: archive/orders/
Scale Batch pools on idle utilization and purge cold blobs on the same scheduleOpen blueprint
id: azure-batch-cost-guardrailnamespace: company.teamdescription: | Scale an Azure Batch pool down when it is sitting idle and purge blobs past their retention prefix, both on the same hourly schedule, both fully automated. No dashboard click to resize a pool, no manual sweep of a storage container.tasks: - id: query_pool_metric type: io.kestra.plugin.azure.monitoring.Query description: Pull the current idle node count for the target Batch pool from Azure Monitor. tenantId: "{{ secret('AZURE_TENANT_ID') }}" clientId: "{{ secret('AZURE_CLIENT_ID') }}" clientSecret: "{{ secret('AZURE_CLIENT_SECRET') }}" endpoint: "https://eastus.metrics.monitor.azure.com" resourceIds: - "{{ secret('AZURE_BATCH_ACCOUNT_RESOURCE_ID') }}" metricNames: - "PoolIdleNodeCount" metricsNamespace: "Microsoft.Batch/batchAccounts" window: PT1H aggregations: - "Average" - id: scale_decision type: io.kestra.plugin.core.flow.If description: Scale the pool down only when idle nodes have stayed high for the whole window. condition: "{{ outputs.query_pool_metric.datapoints > 0 and (outputs.query_pool_metric.results | first).average > 3 }}" then: - id: resize_pool type: io.kestra.plugin.azure.batch.pool.Resize description: Scale dedicated and low-priority nodes down to the idle floor. account: "{{ secret('AZURE_BATCH_ACCOUNT') }}" accessKey: "{{ secret('AZURE_BATCH_ACCESS_KEY') }}" endpoint: "{{ secret('AZURE_BATCH_ENDPOINT') }}" poolId: "kestra-worker-pool" targetDedicatedNodes: 2 targetLowPriorityNodes: 0 - id: purge_cold_blobs type: io.kestra.plugin.azure.storage.blob.DeleteList description: Delete blobs past the retention prefix in the same run, no separate lifecycle policy needed. connectionString: "{{ secret('AZURE_STORAGE_CONNECTION') }}" endpoint: "{{ secret('AZURE_STORAGE_ENDPOINT') }}" container: "batch-scratch" prefix: "expired/"triggers: - id: hourly type: io.kestra.plugin.core.trigger.Schedule description: Check pool utilization and sweep cold storage every hour. cron: "0 * * * *"errors: - id: alert_on_failure type: io.kestra.plugin.slack.notifications.SlackIncomingWebhook description: Alert Slack if the metric query, resize, or purge step fails. url: "{{ secret('SLACK_WEBHOOK_URL') }}" payload: | { "text": "Azure Batch cost guardrail failed (execution {{ execution.id }}). Check whether the pool is over-provisioned or scratch blobs are piling up." }
Archive backups into a dated prefix and gate the expiry cleanup behind a human reviewOpen blueprint
A weekly flow lists a container, copies each blob into a dated archive prefix, and notifies Slack with the count. It then pauses before deleting expired backups so only the destructive step waits on a named reviewer.
The full YAML and topology will appear here once the azure-blob-backup-retention-cutover blueprint is published in the catalog.
Above Azure's per-service triggers and Logic Apps glue.
Azure ships 41 tasks and 7 triggers across storage, messaging, compute, and Batch, each with its own trigger surface. Kestra wires them into one flow, with one execution history, without Logic Apps connectors, Event Grid subscriptions, or Function bindings between every pair of services.
Blob and Data Lake triggers fire on arrival, not on a Data Factory schedule
blob.Trigger and adls.Trigger poll a container on an interval, filter by prefix and regex, and fire once per matched file. The action property moves or deletes processed blobs so the same file never fires twice. No Event Grid subscription or Data Factory storage-event trigger is required just to react to a new file.
Event Hub streaming, batch and real-time, in the same flow
eventhubs.RealtimeTrigger starts an event processor that fires one execution per message; eventhubs.Consume polls in batches on a schedule for aggregation and threshold checks. Both checkpoint to Blob Storage automatically, so no separate Stream Analytics job is needed just to track what has already been read.
Service Bus queues and topics orchestrated without a Logic App relay
servicebus.Publish, servicebus.Consume, and the polling and real-time triggers read and write queues and topic subscriptions directly. A message becomes a flow input or output without a Logic App connector translating between the bus and whatever runs next.
Functions callable from any trigger, not just Event Grid or Blob bindings
function.HttpFunction invokes an Azure Function over HTTP from any Kestra trigger: a webhook, a Kafka message, a dbt completion, a schedule. A Function no longer needs its own Event Grid subscription or Blob-triggered binding to run; it becomes one step in a larger flow instead of an isolated endpoint.
Cross-service chains span the whole estate in one execution
A single flow can move from a blob arrival through a Function transform, a Service Bus publish, and a Data Factory run, with retries scoped to each step and one execution ID covering every hop. Today that same chain usually means a Logic App workflow stitching four services' native triggers together by hand.
Cost-aware Batch scaling and storage lifecycle, enforced automatically
monitoring.Query reads a pool's utilization metric, an If task decides whether to scale, and batch.pool.Resize adjusts dedicated and low-priority node counts. blob.DeleteList clears blobs past a retention prefix on the same schedule. Both run as an automated gate, not a person approving a cost dashboard.
How teams use Azure and Kestra
Patterns cloud platform teams run in production today. Each one shows the flow end to end, with the real plugin classes in play.
Event-driven
Route blob arrivals into cross-cloud and cross-service pipelines
blob.Trigger polls the stage container under a prefix, moves each processed file to an archive path, and fans it out with a ForEach. Files can load into a cross-cloud warehouse, trigger a dbt build, or feed a Data Factory pipeline, all from one arrival event, without an Event Grid rule per destination.
One trigger, many destinations
Prefix-based routing lives in the flow, not in a Data Factory linked service per target.
Move-after-processing prevents duplicates
The action property moves processed blobs so the next poll never reprocesses them.
Audit trail covers the whole chain
One execution links the blob event, the load, and the transform, across clouds if needed.
blob trigger
prefix filter
for each blob
parallel fan-out
load downstream
cross-cloud warehouse
dbt build
transform
Streaming
Watch Event Hub volume and page on-call before a silent drop becomes an outage
A schedule runs eventhubs.Consume every 5 minutes to pull whatever landed in a 30-second window. An If task compares the count against the floor for a healthy producer. Below it, PagerDutyAlert fires with a dedup key, so a stalled producer pages on-call before a downstream job fails on missing data.
Checkpointed, never double-counts
Consume checkpoints to Blob Storage, so each window only counts events not yet read.
One incident, not one per poll
The PagerDuty dedup key keeps repeated low-volume windows on a single incident.
Tunable per hub
The volume floor is a flow value, not a hardcoded threshold buried in a Function.
every 5 min
schedule
consume batch
window count
below floor?
If gate
page on-call
dedup key
Cross-service
Chain Blob, Functions, and Service Bus into one execution across the estate
A blob arrival calls function.HttpFunction to validate and reshape the file, publishes the result with servicebus.Publish for any downstream subscriber, then runs datafactory.CreateRun for the heavier processing. Teams gets one notification for the whole chain, not one per service.
Four services, one execution ID
Blob, Functions, Service Bus, and Data Factory share one audit trail end to end.
No Logic App relay
Each hop calls the next service's plugin task directly, no connector translating between them.
Retries stay local
A Function timeout retries the Function step only, not the whole chain.
blob trigger
new file
azure function
transform
service bus publish
notify subscribers
data factory pipeline
downstream ETL
notify
Teams
Cost & lifecycle
Scale Batch pools on utilization and retire cold blobs on the same schedule
An hourly flow runs monitoring.Query against a Batch pool's utilization metric. An If task decides whether to scale, and batch.pool.Resize sets new dedicated and low-priority counts. The same run purges blobs past a retention prefix with blob.DeleteList, both automated, no dashboard click.
Automated, not a human click
The scale and purge decisions are both an If gate, not an approval step.
Cost and storage in one run
Compute scaling and blob retention share the same schedule and execution ID.
Dedicated and low-priority tuned separately
targetDedicatedNodes and targetLowPriorityNodes scale independently.
hourly schedule
cron
query pool metric
Monitor.Query
scale decision
If gate
resize pool
scale nodes
purge cold blobs
retention cleanup
Disaster recovery
Archive backups into a dated prefix and gate the expiry cleanup behind a human review
A weekly flow lists a container, copies each blob into a dated archive prefix with blob.Copy, and notifies Slack with the count. The storage account's own GRS or RA-GRS replication handles the actual cross-region copy. The flow then Pauses before deleting expired backups; a named reviewer resumes it, and only then does blob.DeleteList remove them. Deleting a backup is worth a second pair of eyes.
Cross-region copy runs unattended
Only the destructive step, deleting expired backups, waits on a human.
Named reviewer, logged
The Pause task records who approved the deletion and when.
Replication and cleanup share one history
The copy count, the approval, and the delete all trace to one execution.
weekly schedule
cron
list container
source container
copy to archive
dated prefix
notify
Slack summary
approval gate
before expiry delete
delete expired
after resume
Kestra vs the orchestration alternatives Azure teams evaluate
Capability
Azure Data Factory
Microsoft Fabric
Microsoft Fabric
Airflow
Trigger on Blob/ADLS arrival, Event Hub, or Service Bus message
Native triggers across all three, no glue
Storage event trigger via Event Grid wiring
Eventstream item, Fabric-scoped
Sensors and hooks per source, custom code
Chain Functions, Service Bus, and Data Factory in one flow
Native plugin tasks, any order
Web/Webhook activities glue services together
Fabric-centric, limited outside the workspace
Custom operators per service
Real-time Event Hub consumption, one execution per message
eventhubs.RealtimeTrigger
Stream Analytics job required
Eventstream item inside the workspace
Kafka sensor plus custom consumer code
Cross-service audit trail in one place
One execution ID spans every service
Per-pipeline monitor, Functions and Service Bus separate
Find answers to your questions right here, and don't hesitate to Contact Us if you couldn't find what you're looking for.
Data Factory is Microsoft's managed ETL and pipeline service, strong at Azure-native data movement and mapping data flows inside the Azure portal. Kestra is a general-purpose orchestrator that treats Data Factory as one plugin among many: it triggers datafactory.CreateRun on a blob arrival or Event Hub message, chains it with Functions and Service Bus, and adds approval gates and cross-cloud steps that Data Factory's own trigger surface cannot reach. Use Data Factory for the pipeline itself; use Kestra for what happens before and after it.
Event-driven triggers across Blob, ADLS, Event Hub, and Service Bus that fire without an Event Grid subscription per source; a single execution history spanning Functions, Service Bus, and Data Factory instead of per-service logs; and chaining into non-Azure tools like Kubernetes or Slack, all inside the same flow.
Two ways. io.kestra.plugin.azure.eventhubs.RealtimeTrigger starts an event processor that fires one execution per message, for true real-time handling. io.kestra.plugin.azure.eventhubs.Consume polls in batches on a schedule, useful for aggregation, volume checks, or a windowed summary downstream. Both checkpoint to Blob Storage automatically, so reprocessing never happens across runs. Event Hubs also speaks the Kafka protocol; teams standardizing on Kafka semantics can compare notes on the Kafka orchestration page.
io.kestra.plugin.core.flow.Pause stops the flow and waits for a named reviewer to resume it from the UI or the API, optionally with a timeout and an automatic behavior (resume, warn, fail) if nobody responds in time. It is reserved for the one step worth a second pair of eyes, like deleting expired backup blobs, not bolted onto every routine Blob or Service Bus run.
Yes. io.kestra.plugin.azure.batch.job.Create runs tasks on an existing pool and waits for completion. Pair it with io.kestra.plugin.azure.monitoring.Query to read pool utilization and io.kestra.plugin.azure.batch.pool.Resize to adjust targetDedicatedNodes and targetLowPriorityNodes automatically, so the pool scales to the workload instead of running at a fixed size around the clock.
Kestra offers multiple deployment options: fully self-hosted on Docker or Kubernetes, a managed cloud version, or air-gapped for regulated environments. The open-source edition includes unlimited workflows and event-driven triggers. Self-hosted gives complete control over where your pipeline execution and credentials live.
Trigger on Blob and Event Hub arrivals, chain Functions and Service Bus, run Data Factory and Batch jobs, and gate the one step that deserves a human review. Open source, self-hosted, event-driven.