GetJob icon
Return icon
Switch icon
Log icon
SlackIncomingWebhook icon
If icon
CancelJob icon
Fail icon
Schedule icon

Azure ML Training Job Governance Gate

Audit Azure Machine Learning job status on a schedule, alert Slack with a Studio link on failure, and optionally cancel after review.

Categories
CloudInfrastructure

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.

How it works

  1. 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.
  2. 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).
  3. 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.
  4. 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.
  5. log_audit_summary always runs last and logs the final classification.
  6. The errors block posts to Slack if the flow itself fails outside the gate logic above.

What you get

  • A real answer to whether a specific training or pipeline job actually finished, built from Azure ML's own JobState, not a timeout guess.
  • A Slack alert that lands with a direct Studio link to the exact failing run, not just a job name someone has to go search for.
  • A deliberately decoupled cancel path: request_cancel is never set by the classification itself, only by an operator who has already looked at the alert and the Studio run.

Who it's for

  • ML platform and MLOps teams running training or pipeline jobs on Azure Machine Learning who need to know a job failed, was cancelled, or stopped responding, without opening Studio to check.
  • Teams evaluating the Azure Machine Learning plugin who want a working example of ml.GetJob and ml.CancelJob together, including the real JobState values and the Studio deep link helper both tasks share.

Why orchestrate this with Kestra

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.

Prerequisites

  • An Azure Machine Learning workspace with a job to audit, and a service principal with 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).
  • A Slack incoming webhook for alerts.
  • Azure Machine Learning is a managed service with no self-hostable open-source emulator, there is no local container to start for it. This plugin calls the Azure Resource Manager API directly from the Kestra worker process, there is no sub-container taskRunner and no Docker network to join.

Secrets

  • 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.

Inputs

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

  • {{ 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.

Quick start

  1. Add the Azure and SLACK_WEBHOOK secrets.
  2. Submit a job on Azure ML (SubmitCommandJob or your own pipeline) and note its name.
  3. Set job_name to that job and run this flow manually, read log_audit_summary for the classification.
  4. If it reports CRITICAL or WARNING and cancellation is the right call, set request_cancel: true and dry_run: false and run it again.
  5. Enable the every_fifteen_minutes trigger, which always ships in safe mode.

How to extend

  • Loop this flow over a list of 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.
  • Read a specific metric from 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.
  • Chain SubmitCommandJob before this gate in the same flow to submit, then immediately audit, a retrain job end to end.

Pitfalls

  • 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.

Links

See How

New to Kestra?

Use blueprints to kickstart your first workflows.