Based on the MLS-C01 exam guideReport an error
PendingManualApproval, and only once approved does it become Approved, which lets deployment automation continue.Running a model once in a notebook and deploying it by hand from the console gives you no reproducibility, no traceability, and no collaboration.
| Problem | What happens in manual ML | MLOps solution | AWS components |
|---|---|---|---|
| Reproducibility | "How did we build that model last month?" — nobody knows | Fix the workflow as code so the same code + the same data → the same result | SageMaker Pipelines |
| Governance | There is no record of which data was used for training or who approved it | Accumulate versions, metrics, approval status, and lineage in one place | Model Registry, Lineage |
| Automation | Retraining and deployment depend on people, so they are delayed and error-prone | Commits and events trigger training and deployment | Projects, CodePipeline, EventBridge |
💡 Concept: The three problems are not independent. Without reproducibility, governance records are meaningless, and without governance gates, automation just deploys bad models faster. Automation is safe only when the gates are already in place.
Pipelines connects Steps into a directed acyclic graph (DAG). A dependency between Steps is created automatically when one Step's output (properties) is referenced as another Step's input.
| Step | What it does | Typical use |
|---|---|---|
ProcessingStep | Runs a Processing Job | Preprocessing, feature engineering, model evaluation |
TrainingStep | Runs a Training Job | Model training |
TuningStep | Runs a Hyperparameter Tuning Job | Searching for the best hyperparameters |
TransformStep | Runs a Batch Transform | Large-scale offline inference |
CreateModelStep | Creates a Model object from the training artifacts | Preparing for deployment or batch transform |
RegisterModel (step collection) | Registers a model package in Model Registry | Version management and entry into the approval gate |
ConditionStep | Conditional branching (if_steps/else_steps) | Quality gate |
FailStep | Explicitly fails the pipeline | Marks the run as stopped when criteria are not met |
LambdaStep | Invokes a Lambda function | Short custom logic, notifications, external APIs |
CallbackStep | Hands off to an external system via SQS and waits for a response | Human review, integration with in-house systems |
QualityCheckStep / ClarifyCheckStep | Checks quality and bias baselines | Preventing drift and fairness regressions |
EMRStep | Runs a Spark job on EMR | Large-scale distributed preprocessing |
[ParameterString input_data]
│
▼
┌───────────────┐ ┌──────────────┐ ┌────────────────┐
│ ProcessingStep│──▶ │ TrainingStep │──▶ │ ProcessingStep │
│ "Preprocess" │ │ "Train" │ │ "Evaluate" │
└───────────────┘ └──────────────┘ └────────┬───────┘
train/val/test model.tar.gz evaluation.json
│
▼
┌──────────────────────┐
│ ConditionStep │
│ AUC >= 0.80 ? │
└───┬──────────────┬───┘
if_steps│ │else_steps
▼ ▼
┌─────────────┐ ┌───────────┐
│RegisterModel│ │ FailStep │
└──────┬──────┘ └───────────┘
▼
[Model Registry] PendingManualApproval
│ approve (Approved)
▼
[EventBridge] ─▶ [ModelDeploy CodePipeline] ─▶ endpointfrom sagemaker.workflow.pipeline import Pipeline
from sagemaker.workflow.steps import ProcessingStep, TrainingStep
from sagemaker.workflow.condition_step import ConditionStep
from sagemaker.workflow.conditions import ConditionGreaterThanOrEqualTo
from sagemaker.workflow.functions import JsonGet
from sagemaker.workflow.parameters import ParameterString, ParameterFloat
input_data = ParameterString(name="InputData", default_value="s3://bucket/raw/")
auc_threshold = ParameterFloat(name="AucThreshold", default_value=0.80)
step_process = ProcessingStep(name="Preprocess", processor=sklearn_proc, ...)
step_train = TrainingStep(name="Train", estimator=xgb, ...)
step_eval = ProcessingStep(name="Evaluate", processor=eval_proc,
property_files=[eval_report], ...)
# Register only when the evaluation metric is at or above the threshold
cond = ConditionGreaterThanOrEqualTo(
left=JsonGet(step_name=step_eval.name, property_file=eval_report,
json_path="metrics.auc.value"),
right=auc_threshold,
)
step_cond = ConditionStep(name="AUCThreshold", conditions=[cond],
if_steps=[step_register], else_steps=[step_fail])
pipeline = Pipeline(
name="churn-pipeline",
parameters=[input_data, auc_threshold],
steps=[step_process, step_train, step_eval, step_cond],
)
pipeline.upsert(role_arn=role)
pipeline.start()| Condition class | What it checks |
|---|---|
ConditionGreaterThanOrEqualTo / ConditionLessThanOrEqualTo | Whether the metric is at or above / at or below the threshold |
ConditionEquals / ConditionIn | Whether the value matches / is in the allowed list |
ConditionNot / ConditionOr | Negating a condition / combining several conditions |
ParameterString, ParameterInteger, and ParameterFloat inject input paths, thresholds, and instance types at execution time. You can re-run without changing code.CacheConfig(enable_caching=True, expire_after="30d") to a Step skips re-running a Step with identical input, saving time and cost.PropertyFile, read a specific path with JsonGet, and feed it into the condition.ConditionStep is the heart of the MLOps quality gate. As in "if the evaluation AUC is below 0.80, do not register," it keeps a bad model from flowing into production automatically. On the exam, a stem such as "automatically block deployment of a model that does not meet the criteria" maps to the combination of ConditionStep + the Model Registry approval gate.
A trained model is registered in a Model Package Group as a version (Model Package).
from sagemaker.workflow.step_collections import RegisterModel
step_register = RegisterModel(
name="RegisterModel",
estimator=xgb,
model_data=step_train.properties.ModelArtifacts.S3ModelArtifacts,
content_types=["text/csv"], response_types=["text/csv"],
inference_instances=["ml.m5.large"],
transform_instances=["ml.m5.large"],
model_package_group_name="churn-models",
approval_status="PendingManualApproval", # approval gate
model_metrics=model_metrics, # attach evaluation metrics to the version
)churn-models (Model Package Group)
├── v1 Approved (in production)
├── v2 Rejected (did not meet evaluation criteria)
└── v3 PendingManualApproval (awaiting review)| Transition | Trigger | Result |
|---|---|---|
(new registration) → PendingManualApproval | Default setting of RegisterModel | Not a deployment target. Awaiting review |
PendingManualApproval → Approved | Approval by the owner or an automatic rule | A Model Package State Change event fires → triggers the deployment pipeline |
PendingManualApproval → Rejected | Metrics not met or a risk judgment | Not deployed; only a record remains |
Approved → Rejected | A problem is found in production | Blocks new deployments. Roll back to the previous Approved version |
Rejected → Approved | Re-review or issue resolved | Becomes deployable again |
UpdateModelPackage API (or the Studio UI). Because the status change itself becomes an event, approval is the deployment trigger.approval_status="Approved", but that removes human review, so it is not recommended in regulated environments.[Dataset S3] ─▶ [Processing Job] ─▶ [Training Job] ─▶ [Model Package v3]
Artifact Action Action Artifact
└──────── Tracked end to end through Associations (links) ────────┘
Rollback: v3 → Rejected , v1 → Approved ─▶ ModelDeploy redeploys v1Model Registry is the center of lineage, tying together "which model artifact came from which training job, metrics, and data." The approval status (Approved/Rejected/Pending) is the switch with which a person or automation enforces governance, and a rollback is done not as a new deployment but as a redeployment of the previous Approved version.
SageMaker Projects is an MLOps template that automatically provisions two repositories and the pipelines.
┌──── ModelBuild repo (pipelines/ , tests/) ────┐
│ git push ─▶ CodePipeline ─▶ CodeBuild │
│ └─▶ SageMaker Pipeline run │
│ └─▶ Model Registry entry │
└───────────────────────────────────────────────┘
│ approval (Approved) event
▼
┌──── ModelDeploy repo (CloudFormation templates) ──┐
│ EventBridge ─▶ CodePipeline │
│ ├─▶ Deploy Staging endpoint │
│ ├─▶ Manual approval stage │
│ └─▶ Deploy Production endpoint │
└───────────────────────────────────────────────────┘| Service | Role | What it does in this flow |
|---|---|---|
| CodeCommit / Git | Source repository | Version control for the pipeline definition and deployment templates |
| CodeBuild | Runs builds and tests | Runs the pipeline definition, unit and data validation tests |
| CodePipeline | Orchestration | Connects source → build → deploy stages and inserts manual approval stages |
| CloudFormation | IaC deployment | Creates and updates endpoints and endpoint configurations as code |
| EventBridge | Event routing | Receives the model approval event and starts the ModelDeploy pipeline |
| Model Registry | Governance | Controls whether to deploy through the approval status |
# buildspec.yml excerpt (CodeBuild)
phases:
build:
commands:
- python pipelines/run_pipeline.py --module-name pipelines.churn.pipeline
- python -m pytest tests/ # code and data validation tests⚠️ Pitfall: Attaching CI/CD does not create a gate. You need three layers of gates in place for automation to be safe: ConditionStep (model quality), Model Registry approval (a person or policy), and Staging validation.
You do not send a new model to all traffic at once. SageMaker endpoints support gradual deployment.
| Strategy | Traffic flow | Rollback speed | User impact | When to use |
|---|---|---|---|---|
| Blue/Green (All-at-once) | Prepare a new fleet, then switch 100% at once | Fast (the old fleet is kept for a baking period) | All users if there is a problem | The default safe deployment. When the change is already validated |
| Canary | Only a small share (e.g., 10%) goes to the new fleet → switch after validation | Very fast (only a few are reverted) | A few users only | When minimizing risk is the top priority |
| Linear | Increase by a fixed percentage over several steps | Fast (stop the step and revert) | Grows gradually per step | When you move on after checking metrics at each step |
| Rolling | Replace instances sequentially in batches | Medium (revert the progress made) | Users on the batch that was replaced | Replacing a large fleet cost-efficiently |
| Shadow | Copies production traffic to the new model and discards its responses | Immediate (not live use) | None | When you only want pre-validation with real traffic |
| A/B (Production Variant) | Distributes traffic by weight across variants on one endpoint | Immediate by adjusting weights | Different models for different groups | When you want to compare the performance of two models in practice |
| Stem clue | Answer to choose | Why |
|---|---|---|
| "If the evaluation metric does not meet the criteria, do not register and stop" | ConditionStep (+FailStep) | A quality gate that compares the metric to a threshold and branches |
| "Track which model was trained on which data, with fast rollback" | Model Registry (+Lineage) | Manages versions, approval status, and lineage |
| "What is the default approval status of a newly registered model?" | PendingManualApproval | Not a deployment target until reviewed |
| "When approved, the deployment pipeline must run automatically" | EventBridge → CodePipeline | Routes the model package state change event |
| "Set up build and deploy repos and CI/CD from a template at once" | SageMaker Projects | Automatic ModelBuild/ModelDeploy provisioning + IaC deployment |
| "Validate with a small amount of traffic first, then expand" | Canary | Gradual switchover that minimizes exposure |
| "Compare the performance of two models with real traffic" | A/B (Production Variant) weighted distribution | Serve and compare simultaneously on one endpoint |
| "Pre-validate with real traffic without affecting users" | Shadow deployment | Copies traffic and does not return the responses |
| "It is wasteful to re-run the same preprocessing Step every time" | Step caching with CacheConfig | Skips re-running a Step with identical input |
| "Reuse by changing input paths and thresholds on each run" | Pipeline Parameters | Injected at execution time without editing the definition |
| "Manual deployment from a notebook is enough" | Wrong answer choice | Breaks reproducibility and governance |
PropertyFile and JsonGet?else_steps non-empty and does it explicitly signal failure?In the next post, we cover ML security, which underpins all of these pipelines: IAM execution roles, VPC isolation, and KMS encryption.
Click a choice to reveal the answer and explanation.
Question 1
In a SageMaker Pipeline, you want to stop the pipeline without registering the model in the Model Registry if the trained model's validation AUC is below 0.75. Which component should you use?
Question 2
A problem occurred with the production model, and you want to quickly roll back to a previous version and track "which model is currently trained on which data." Which SageMaker feature is MOST appropriate?
Question 3
You want to release a new model to production by first sending only a small amount of real traffic to confirm there are no problems, and then gradually increasing the share. Which deployment strategy is MOST appropriate?
Question 4
What is the MOST accurate description of the core value of the MLOps templates provided by SageMaker Projects?
Question 5
What is the typical default approval status of a newly registered model version in the Model Registry, and what does it mean?