Training a model in a notebook is the easy part. Getting that model into production — with reproducible pipelines, automated retraining, version control, fairness checks, and zero-downtime deployments — is where most ML projects fail. Azure Machine Learning is Microsoft’s enterprise platform for the full machine learning lifecycle, and MLOps is the discipline that ties it all together. This guide covers the end-to-end journey from raw data to monitored production models.
We will walk through workspace provisioning, component-based training pipelines, AutoML, the model registry, managed endpoints, the Responsible AI dashboard, and CI/CD automation with GitHub Actions. Every section includes production-ready code using the Azure ML SDK v2 and CLI v2.
The MLOps lifecycle
MLOps applies DevOps principles to machine learning. The goal is to make model training, validation, deployment, and monitoring as repeatable and automated as building and shipping software. The diagram below shows how each stage feeds into the next in a continuous loop:
Each stage in this lifecycle maps to a specific Azure ML capability. The platform layer at the bottom provides the shared infrastructure that every stage relies on.
Provisioning an Azure ML workspace
The workspace is the top-level resource in Azure ML. It groups compute, data, models, endpoints, and experiments. Behind the scenes it creates a Storage Account, Key Vault, Application Insights, and an optional Container Registry.
CLI v2 setup
# Install the Azure ML CLI v2 extension
az extension add -n ml -y
# Create a resource group
az group create \
--name rg-mlops-prod \
--location eastus2
# Create the workspace
az ml workspace create \
--name mlw-production \
--resource-group rg-mlops-prod \
--location eastus2 \
--display-name "Production ML Workspace"
# Create a compute cluster for training
az ml compute create \
--name gpu-cluster \
--resource-group rg-mlops-prod \
--workspace-name mlw-production \
--type AmlCompute \
--size Standard_NC6s_v3 \
--min-instances 0 \
--max-instances 4 \
--idle-time-before-scale-down 120
SDK v2 setup
from azure.ai.ml import MLClient
from azure.ai.ml.entities import Workspace, AmlCompute
from azure.identity import DefaultAzureCredential
# Connect to the workspace
ml_client = MLClient(
credential=DefaultAzureCredential(),
subscription_id="your-subscription-id",
resource_group_name="rg-mlops-prod",
workspace_name="mlw-production",
)
# Create a compute cluster programmatically
gpu_cluster = AmlCompute(
name="gpu-cluster",
size="Standard_NC6s_v3",
min_instances=0,
max_instances=4,
idle_time_before_scale_down=120,
)
ml_client.compute.begin_create_or_update(gpu_cluster)
min_instances to 0 and configure idle_time_before_scale_down. GPU compute is expensive — a 4-node NC6s_v3 cluster left running 24/7 costs over $4,000/month. Auto-scaling to zero when idle can cut training compute costs by 80% or more.
Datasets and data assets
Azure ML treats data as a first-class, versioned resource. Every dataset registered in the workspace is immutable and traceable, so you can always reproduce which data a given model was trained on.
Registering data assets
from azure.ai.ml.entities import Data
from azure.ai.ml.constants import AssetTypes
# Register a file dataset (e.g., CSV in blob storage)
training_data = Data(
name="customer-churn-train",
description="Training data for churn prediction model",
path="azureml://datastores/workspaceblobstore/paths/data/churn_train.csv",
type=AssetTypes.URI_FILE,
version="1",
tags={"source": "crm-export", "date": "2025-07-01"},
)
ml_client.data.create_or_update(training_data)
# Register a folder dataset (e.g., images)
image_data = Data(
name="product-images-v2",
description="Product catalog images for classification",
path="azureml://datastores/workspaceblobstore/paths/images/products/",
type=AssetTypes.URI_FOLDER,
version="2",
)
ml_client.data.create_or_update(image_data)
# Register an MLTable for structured tabular data
tabular_data = Data(
name="sales-forecast-features",
path="./data/sales_features/", # folder with MLTable file
type=AssetTypes.MLTABLE,
version="3",
)
ml_client.data.create_or_update(tabular_data)
Training with component-based pipelines
Azure ML pipelines decompose training workflows into reusable components. Each component is a self-contained step (data prep, training, evaluation) with defined inputs, outputs, and a runtime environment. This makes pipelines modular, testable, and easy to maintain.
Defining a training component
from azure.ai.ml import command, Input, Output
from azure.ai.ml.entities import Environment
# Define a curated environment or custom one
env = Environment(
name="sklearn-training-env",
image="mcr.microsoft.com/azureml/openmpi4.1.0-ubuntu20.04",
conda_file="./envs/conda.yml",
)
# Training component
train_component = command(
name="train_model",
display_name="Train churn prediction model",
inputs={
"train_data": Input(type="uri_file"),
"learning_rate": Input(type="number", default=0.01),
"n_estimators": Input(type="integer", default=100),
},
outputs={
"model_output": Output(type="mlflow_model"),
"metrics_output": Output(type="uri_file"),
},
code="./src/train",
command=("python train.py "
"--train-data ${{inputs.train_data}} "
"--learning-rate ${{inputs.learning_rate}} "
"--n-estimators ${{inputs.n_estimators}} "
"--model-output ${{outputs.model_output}} "
"--metrics-output ${{outputs.metrics_output}}"),
environment=env,
compute="gpu-cluster",
)
Building the full pipeline
from azure.ai.ml.dsl import pipeline
@pipeline(
description="End-to-end churn prediction pipeline",
default_compute="gpu-cluster",
)
def churn_pipeline(raw_data, learning_rate, n_estimators):
# Step 1: Prepare data
prep_step = prep_component(
raw_data=raw_data,
test_split_ratio=0.2,
)
# Step 2: Train model
train_step = train_component(
train_data=prep_step.outputs.train_data,
learning_rate=learning_rate,
n_estimators=n_estimators,
)
# Step 3: Evaluate model
eval_step = eval_component(
model=train_step.outputs.model_output,
test_data=prep_step.outputs.test_data,
)
return {
"trained_model": train_step.outputs.model_output,
"evaluation_report": eval_step.outputs.report,
}
# Instantiate and submit
pipeline_job = churn_pipeline(
raw_data=Input(path="azureml:customer-churn-train:1"),
learning_rate=0.01,
n_estimators=200,
)
submitted_job = ml_client.jobs.create_or_update(pipeline_job)
print(f"Pipeline submitted: {submitted_job.studio_url}")
YAML pipeline definition (alternative)
# pipeline.yml — declarative pipeline definition
$schema: https://azuremlschemas.azureedge.net/latest/pipelineJob.schema.json
type: pipeline
display_name: churn-prediction-pipeline
experiment_name: churn-experiment
settings:
default_compute: azureml:gpu-cluster
inputs:
raw_data:
path: azureml:customer-churn-train:1
type: uri_file
jobs:
prep_data:
type: command
component: ./components/prep/component.yml
inputs:
raw_data: ${{parent.inputs.raw_data}}
train_model:
type: command
component: ./components/train/component.yml
inputs:
train_data: ${{parent.jobs.prep_data.outputs.train_data}}
learning_rate: 0.01
evaluate:
type: command
component: ./components/eval/component.yml
inputs:
model: ${{parent.jobs.train_model.outputs.model_output}}
test_data: ${{parent.jobs.prep_data.outputs.test_data}}
AutoML: automated model selection
When you do not have a strong prior on which algorithm to use, AutoML evaluates dozens of models and hyperparameter combinations for you. It handles featurization, model selection, and hyperparameter tuning — then returns the best model with full explainability.
from azure.ai.ml import automl, Input
# Classification task with AutoML
classification_job = automl.classification(
compute="gpu-cluster",
experiment_name="churn-automl",
training_data=Input(
path="azureml:customer-churn-train:1",
type="mltable",
),
target_column_name="churned",
primary_metric="AUC_weighted",
# Guardrails
enable_model_explainability=True,
enable_early_termination=True,
# Time and iteration limits
limits=automl.ClassificationLimits(
timeout_minutes=120,
max_trials=50,
max_concurrent_trials=4,
enable_early_termination=True,
),
# Allowed algorithms (optional filter)
allowed_training_algorithms=[
"LightGBM", "XGBoost", "RandomForest",
"LogisticRegression", "GradientBoosting",
],
# Cross-validation
n_cross_validations=5,
)
# Submit the AutoML job
returned_job = ml_client.jobs.create_or_update(classification_job)
print(f"AutoML job: {returned_job.studio_url}")
# After completion — retrieve the best model
best_run = ml_client.jobs.get(returned_job.name)
print(f"Best algorithm: {best_run.properties['best_model_algorithm']}")
print(f"Best AUC: {best_run.properties['best_primary_metric_score']}")
AutoML supports classification, regression, time-series forecasting, computer vision (image classification, object detection), and NLP (text classification, NER) tasks. For vision and NLP tasks, it fine-tunes pre-trained deep learning models automatically.
enable_model_explainability flag generates feature importance scores that help you understand what the model learned — critical for building trust with stakeholders.
Model registry: versioning and promotion
The model registry is a central repository for all trained models. Every model gets a name, version, and metadata. You can tag models with stage labels to track their lifecycle from development through staging to production.
from azure.ai.ml.entities import Model
from azure.ai.ml.constants import AssetTypes
# Register a model from a training run
model = Model(
name="churn-prediction",
version="3",
path=f"azureml://jobs/{training_job.name}/outputs/model_output",
type=AssetTypes.MLFLOW_MODEL,
description="LightGBM churn classifier — AUC 0.94",
tags={
"algorithm": "LightGBM",
"auc": "0.94",
"dataset_version": "1",
"stage": "staging",
},
properties={
"training_job": training_job.name,
"approved_by": "",
},
)
ml_client.models.create_or_update(model)
# List all versions of a model
versions = ml_client.models.list(name="churn-prediction")
for v in versions:
print(f"v{v.version} — {v.tags.get('stage', 'dev')} — {v.description}")
# Promote to production (update tags)
prod_model = ml_client.models.get(name="churn-prediction", version="3")
prod_model.tags["stage"] = "production"
prod_model.properties["approved_by"] = "ml-lead@company.com"
ml_client.models.create_or_update(prod_model)
MLFLOW_MODEL type and you get automatic dependency tracking, signature validation, and framework-agnostic serving. This means a model trained with scikit-learn, PyTorch, or TensorFlow can be deployed the same way.
Managed endpoints: online and batch
Azure ML provides two types of managed endpoints that handle infrastructure, scaling, and networking for you.
Online endpoints (real-time inference)
from azure.ai.ml.entities import (
ManagedOnlineEndpoint,
ManagedOnlineDeployment,
CodeConfiguration,
)
# Create the endpoint
endpoint = ManagedOnlineEndpoint(
name="churn-prediction-endpoint",
description="Real-time churn prediction API",
auth_mode="key",
tags={"environment": "production"},
)
ml_client.online_endpoints.begin_create_or_update(endpoint).result()
# Deploy the model (blue deployment)
blue_deployment = ManagedOnlineDeployment(
name="blue",
endpoint_name="churn-prediction-endpoint",
model="azureml:churn-prediction:3",
instance_type="Standard_DS3_v2",
instance_count=2,
request_settings={
"request_timeout_ms": 3000,
"max_concurrent_requests_per_instance": 10,
},
)
ml_client.online_deployments.begin_create_or_update(blue_deployment).result()
# Route 100% traffic to the blue deployment
endpoint.traffic = {"blue": 100}
ml_client.online_endpoints.begin_create_or_update(endpoint).result()
# Test the endpoint
result = ml_client.online_endpoints.invoke(
endpoint_name="churn-prediction-endpoint",
request_file="./test-request.json",
)
print(result)
Blue-green deployment (zero-downtime updates)
# Deploy new model version as "green"
green_deployment = ManagedOnlineDeployment(
name="green",
endpoint_name="churn-prediction-endpoint",
model="azureml:churn-prediction:4", # newer version
instance_type="Standard_DS3_v2",
instance_count=2,
)
ml_client.online_deployments.begin_create_or_update(green_deployment).result()
# Canary: send 10% traffic to green
endpoint.traffic = {"blue": 90, "green": 10}
ml_client.online_endpoints.begin_create_or_update(endpoint).result()
# After validation — shift all traffic to green
endpoint.traffic = {"blue": 0, "green": 100}
ml_client.online_endpoints.begin_create_or_update(endpoint).result()
# Clean up old deployment
ml_client.online_deployments.begin_delete(
name="blue",
endpoint_name="churn-prediction-endpoint",
)
Batch endpoints (large-scale scoring)
from azure.ai.ml.entities import BatchEndpoint, BatchDeployment
# Create a batch endpoint for scoring millions of records
batch_endpoint = BatchEndpoint(
name="churn-batch-scoring",
description="Score customer churn risk in bulk",
)
ml_client.batch_endpoints.begin_create_or_update(batch_endpoint).result()
batch_deployment = BatchDeployment(
name="lgbm-v3",
endpoint_name="churn-batch-scoring",
model="azureml:churn-prediction:3",
compute="gpu-cluster",
mini_batch_size=100,
max_concurrency_per_instance=2,
output_action="append_row",
output_file_name="predictions.csv",
)
ml_client.batch_deployments.begin_create_or_update(batch_deployment).result()
# Invoke the batch endpoint
job = ml_client.batch_endpoints.invoke(
endpoint_name="churn-batch-scoring",
input=Input(
path="azureml://datastores/workspaceblobstore/paths/scoring/july_customers.csv",
type="uri_file",
),
)
auth_mode="aad_token" instead of "key". Key-based auth works for development but is a security risk in production. Azure AD token auth integrates with RBAC, conditional access policies, and audit logs.
Responsible AI dashboard
The Responsible AI (RAI) dashboard provides a unified view of model fairness, interpretability, error analysis, and causal reasoning. It is not optional — it is a requirement for any model that affects people’s outcomes (credit scoring, hiring, healthcare, etc.).
from azure.ai.ml import Input
from azure.ai.ml.entities import (
ResponsibleAiInsights,
ErrorAnalysisConfig,
ExplanationConfig,
FairnessConfig,
CausalConfig,
)
# Configure the RAI dashboard components
rai_config = ResponsibleAiInsights(
model="azureml:churn-prediction:3",
train_data=Input(path="azureml:customer-churn-train:1", type="mltable"),
test_data=Input(path="azureml:customer-churn-test:1", type="mltable"),
target_column="churned",
components=[
# Error analysis: find where the model fails
ErrorAnalysisConfig(
max_depth=4,
num_leaves=31,
),
# Explanations: feature importance per prediction
ExplanationConfig(),
# Fairness: check for bias across groups
FairnessConfig(
sensitive_features=["age_group", "gender", "region"],
fairness_metric="demographic_parity",
),
# Causal: what-if analysis
CausalConfig(
treatment_features=["discount_offered", "support_calls"],
),
],
)
# Submit the RAI dashboard job
rai_job = ml_client.jobs.create_or_update(rai_config)
print(f"RAI dashboard: {rai_job.studio_url}")
The dashboard answers four critical questions:
- Error analysis — Where does the model make the most mistakes? Which subgroups have the highest error rates?
- Explanations — Which features drive each individual prediction? Is the model relying on the right signals?
- Fairness — Does the model perform equally across demographic groups? Are there disparate impact patterns?
- Causal inference — What would happen if we changed a feature? Does offering a discount actually reduce churn?
CI/CD for ML with GitHub Actions
MLOps automation connects your Git repository to the Azure ML platform. When code is merged to main, the pipeline should automatically retrain, evaluate, and — if metrics pass — deploy the new model.
GitHub Actions workflow
# .github/workflows/mlops-pipeline.yml
name: MLOps Training Pipeline
on:
push:
branches: [main]
paths:
- "src/train/**"
- "src/evaluate/**"
- "data/**"
- "pipeline.yml"
jobs:
train-and-evaluate:
runs-on: ubuntu-latest
permissions:
id-token: write
contents: read
steps:
- uses: actions/checkout@v4
- name: Azure Login (OIDC)
uses: azure/login@v2
with:
client-id: ${{ secrets.AZURE_CLIENT_ID }}
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
- name: Install Azure ML CLI
run: az extension add -n ml -y
- name: Submit training pipeline
run: |
az ml job create \
--file pipeline.yml \
--resource-group rg-mlops-prod \
--workspace-name mlw-production \
--stream
- name: Validate metrics
run: |
python scripts/check_metrics.py \
--min-auc 0.90 \
--min-f1 0.85
deploy:
needs: train-and-evaluate
runs-on: ubuntu-latest
if: success()
steps:
- uses: actions/checkout@v4
- name: Azure Login (OIDC)
uses: azure/login@v2
with:
client-id: ${{ secrets.AZURE_CLIENT_ID }}
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
- name: Install Azure ML CLI
run: az extension add -n ml -y
- name: Deploy to staging (green)
run: |
az ml online-deployment create \
--file deploy/green-deployment.yml \
--resource-group rg-mlops-prod \
--workspace-name mlw-production
- name: Smoke test
run: |
python scripts/smoke_test.py \
--endpoint churn-prediction-endpoint \
--deployment green
- name: Shift traffic
run: |
az ml online-endpoint update \
--name churn-prediction-endpoint \
--traffic "blue=0 green=100" \
--resource-group rg-mlops-prod \
--workspace-name mlw-production
Platform capabilities at a glance
Component Pipelines
Modular, reusable steps with defined inputs/outputs. Python SDK or YAML definitions.
AutoML
Automated featurization, model selection, and hyperparameter tuning across ML tasks.
Model Registry
Versioned model store with staging labels, lineage tracking, and MLflow support.
Managed Endpoints
Real-time and batch inference with blue-green deployments and autoscaling.
Responsible AI
Fairness, interpretability, error analysis, and causal inference in one dashboard.
Data Drift Monitoring
Detect statistical changes in input data that signal model degradation.
Environments
Docker + Conda environments versioned alongside code for perfect reproducibility.
Enterprise Security
VNet injection, private endpoints, managed identity, RBAC, and CMK encryption.
Azure ML vs. the competition
| Capability | Azure ML | Databricks MLflow | AWS SageMaker | GCP Vertex AI |
|---|---|---|---|---|
| Pipeline orchestration | Component pipelines (SDK v2 + YAML) | Workflows + Delta Live Tables | SageMaker Pipelines | Vertex Pipelines (KFP) |
| AutoML | Built-in (classification, regression, vision, NLP, forecasting) | AutoML via Databricks AutoML | SageMaker Autopilot | Vertex AutoML |
| Model registry | Native + MLflow integration | Unity Catalog / MLflow Registry | SageMaker Model Registry | Vertex Model Registry |
| Managed serving | Online + Batch endpoints | Model Serving (Serverless) | Real-time + Batch Transform | Online + Batch Prediction |
| Responsible AI | Integrated dashboard (fairness, explanations, error analysis, causal) | MLflow + third-party libs | Clarify (bias + explainability) | Vertex Explainable AI |
| CI/CD integration | GitHub Actions, Azure DevOps, CLI v2 | Databricks Asset Bundles | SageMaker Projects + CodePipeline | Cloud Build + Vertex |
| Notebook experience | Integrated notebooks + VS Code | Collaborative notebooks (strong) | SageMaker Studio notebooks | Colab Enterprise |
| Differentiator | Deep Azure ecosystem + Responsible AI | Spark-native + lakehouse | Broadest service catalog | BigQuery + TensorFlow native |
Choose Azure ML when your organization is invested in the Microsoft ecosystem, needs built-in Responsible AI tooling, or requires tight integration with Azure DevOps and GitHub. Choose Databricks when your workflows are Spark-heavy and you want a unified data + ML platform. Each platform has matured significantly — the deciding factor is usually which ecosystem your team already lives in.
Production MLOps checklist
- Version everything — data assets, environments, pipeline definitions, and models should be in source control with immutable versions.
- Automate the pipeline — manual training runs do not scale. Use CI/CD triggers so that code changes automatically kick off training and evaluation.
- Gate deployments on metrics — set minimum thresholds for AUC, F1, precision, or recall. A model that fails the gate never reaches production.
- Run the Responsible AI dashboard — before any model that affects real people goes live, verify fairness across sensitive groups and review error patterns.
- Monitor for data drift — production data evolves. Configure drift monitors that alert you when input distributions shift beyond a threshold, triggering retraining.
- Use blue-green deployments — never replace a production model in-place. Deploy the new version alongside the old, validate, then shift traffic.
- Scale compute to zero — training clusters and batch compute should auto-scale to zero when idle. GPU hours left running can consume budget rapidly.
- Secure the workspace — use private endpoints, managed identity (not keys), and RBAC. Store secrets in Key Vault, never in code or environment variables.
Next steps
- Create a workspace using the CLI v2 and register your first data asset.
- Build a two-step pipeline — data preparation followed by training — and submit it to a compute cluster.
- Run AutoML on a tabular dataset to establish a baseline, then compare against your custom model.
- Deploy to a managed endpoint and test the REST API with sample data.
- Generate a Responsible AI dashboard and review the fairness analysis before promoting the model.
- Read the docs: learn.microsoft.com/azure/machine-learning
Leave a Reply