New to Kestra?
Use blueprints to kickstart your first workflows.
Audit Azure Machine Learning job status on a schedule, alert Slack with a Studio link on failure, and optionally cancel after review.
A training or pipeline job that fails on Azure Machine Learning does not page anyone,
it just sits there in a workspace nobody is watching until a data scientist happens to
check Studio. A job that should have finished in an hour and is still RUNNING a day
later is burning compute the whole time, with the same silence. GetJob already reports
the exact JobState and a direct Studio link, this blueprint is what reads that report on
a schedule and turns it into an alert, with a narrow, operator-reviewed path to cancel the
job if that is the right call.
probe_job_status (io.kestra.plugin.azure.ml.GetJob) reads jobName's current
JobState, studioUrl, best-effort MLflow metrics, and named outputs, with
allowFailure: true so a bad credential or a renamed job does not stop the flow before
it can be classified as PROBE_FAILED below.classify_job_status (io.kestra.plugin.core.debug.Return) returns PROBE_FAILED when
probe_job_status produced no output at all (guarded with is not defined),
CRITICAL or WARNING on FAILED, CANCELED, or NOT_RESPONDING depending on
failure_is_critical, COMPLETED on a clean finish, and IN_PROGRESS for every other,
non-terminal JobState (QUEUED, RUNNING, PROVISIONING, and so on).route_by_job_status (io.kestra.plugin.core.flow.Switch) branches on that status:
COMPLETED and IN_PROGRESS log it, WARNING, CRITICAL, and PROBE_FAILED each
alert Slack, and the two real failure alerts include probe_job_status.studioUrl, a
direct link into Azure ML Studio for the exact run, not a generic workspace link.remediate_gate (io.kestra.plugin.core.flow.If) runs only when request_cancel is
true and dry_run is false, independent of the classification above, since an operator
may want to cancel a job for a reason this gate cannot see on its own, such as a known
bad input dataset:cancel_job (io.kestra.plugin.azure.ml.CancelJob) requests cancellation and, per
the task's own documented behavior, polls until the job actually reaches a terminal
state before returning, since Azure ML cancellation is asynchronous.report_cancel_result (io.kestra.plugin.core.flow.If) fails the flow with
io.kestra.plugin.core.execution.Fail if the cancel call produced no output at all.log_audit_summary always runs last and logs the final classification.errors block posts to Slack if the flow itself fails outside the gate logic above.JobState, not a timeout guess.request_cancel is never set by the
classification itself, only by an operator who has already looked at the alert and the
Studio run.ml.GetJob and ml.CancelJob together, including the real JobState values and the
Studio deep link helper both tasks share.GetJob and CancelJob are single point-in-time calls, Azure ML itself has no concept of
a schedule, a policy on what counts as a failure worth paging for, or an alert built from
the job's own Studio link. Kestra adds the recurring audit, the failure_is_critical
policy choice, the Slack alert carrying a working deep link, and the reviewed cancel path,
all as one declarative YAML file you can version and review like code.
Microsoft.MachineLearningServices/workspaces/jobs/read permission
(roles/AzureML Data Scientist or a narrower custom role is enough to read, cancelling
additionally needs write on the job).taskRunner and no Docker network to join.AZURE_TENANT_ID, AZURE_CLIENT_ID, AZURE_CLIENT_SECRET, AZURE_SUBSCRIPTION_ID:
service principal credentials used by probe_job_status and cancel_job.
SLACK_WEBHOOK: Slack incoming webhook URL used by the alert tasks and the errors
block.
In Kestra OSS, set all five as base64-encoded environment variables, for example
SECRET_AZURE_TENANT_ID, SECRET_AZURE_SUBSCRIPTION_ID, SECRET_SLACK_WEBHOOK.
| Input | Type | Default | Allowed values |
|---|---|---|---|
resource_group_name |
STRING | ml-rg |
any resource group name |
workspace_name |
STRING | ml-workspace |
any Azure ML workspace name |
job_name |
STRING | training-run-001 |
any Azure ML job name |
job_type_label |
SELECT | command |
command, pipeline, sweep |
failure_is_critical |
BOOL | true |
true, false |
request_cancel |
BOOL | false |
true, false |
dry_run |
BOOL | true |
true, false |
{{ outputs.probe_job_status.status }}: current JobState string of the Azure ML job.{{ outputs.probe_job_status.studioUrl }}: deep link URL directly to the job run in Azure Machine Learning Studio.{{ outputs.probe_job_status.metrics }}: map of MLflow logged metrics (best-effort).{{ outputs.probe_job_status.outputs }}: map of named job output artifacts.{{ outputs.classify_job_status.value }}: governance classification (CRITICAL, WARNING, IN_PROGRESS, COMPLETED, or PROBE_FAILED).{{ outputs.cancel_job.status }}: verified terminal status of the job after cancellation.SLACK_WEBHOOK secrets.SubmitCommandJob or your own pipeline) and note its name.job_name to that job and run this flow manually, read log_audit_summary for the
classification.CRITICAL or WARNING and cancellation is the right call, set
request_cancel: true and dry_run: false and run it again.every_fifteen_minutes trigger, which always ships in safe mode.job_name values with
io.kestra.plugin.core.flow.Loop to audit every job in a pipeline run from one flow
instead of one job at a time.outputs.probe_job_status.metrics (best-effort, from
MLflow) and add a metric-based classification, for example a validation loss that never
improved, alongside the state-based one.SubmitCommandJob before this gate in the same flow to submit, then
immediately audit, a retrain job end to end.GetJob's output has no start time or duration, only the current JobState. This gate
cannot tell you a job has been RUNNING too long on its own, only that it is not yet
terminal, track duration separately if runaway jobs are the specific risk you care about.metrics is best-effort: if the service principal lacks MLflow tracking scope, or the
tracking server is unreachable, it comes back as an empty map and a warning is logged,
not a task failure, do not treat an empty metrics map as proof a job logged nothing.CancelJob treats cancelling an already-terminal job as a no-op, not an error, running
remediate_gate against a job that finished between your last check and now is safe.NOT_RESPONDING is itself a real JobState, not only FAILED and CANCELED count as
failures per the plugin's own JobState.isFailure(), this blueprint checks all three.