Based on the MLA-C01 exam guideReport an error
properties are referenced as the next step's input, the dependency automatically becomes a DAG edge. Without a reference, steps run in parallel.If last week was about "how to deploy a model," this week is about "how to automate that deployment and make it repeatable" — in other words, MLOps.
| Feature | Description |
|---|---|
| Reproducibility | The workflow definition is kept as code and JSON, so the same procedure runs repeatedly |
| Lineage tracking | Automatically records which data, code, and parameters produced which model |
| Studio integration | View the DAG visually and track the status of each step per execution |
| Python SDK based | Defined with the SageMaker Python SDK, with no separate DSL |
| Registry integration | The RegisterModel step registers a version straight into the Model Registry |
What Pipelines creates is a DAG. A DAG is a graph that is "directed (execution order) and acyclic (no infinite loops)," the standard model of workflow orchestration that expresses data dependencies as nodes and edges. Airflow and Step Functions use the same DAG model. In the exam it splits like this: "automate training → evaluation → deployment inside SageMaker" means Pipelines, while "broadly orchestrate many AWS services" means Step Functions.
A widely used shape is preprocessing → training → evaluation → condition → registration.
ParameterString(InputData) ParameterFloat(AccuracyThreshold)
│ │
▼ │
ProcessingStep "PreprocessData" (preprocessing) │
│ properties → S3Output.S3Uri │
▼ │
TrainingStep "TrainModel" (training) │
│ ModelArtifacts.S3ModelArtifacts │
▼ │
ProcessingStep "EvaluateModel" (evaluation) │
│ PropertyFile → evaluation.json │
└──────────────┬─────────────────────────────┘
▼
ConditionStep "CheckAccuracy"
│
if_steps ─────┴───── else_steps
│ │
▼ ▼
RegisterModel FailStep
→ Model Registry (or left empty)if_steps branch is skipped — a poor model never reaches the Registry.Pipelines provides step types predefined for each kind of work. The main step types are as follows:
| Step type | Role | Typical use |
|---|---|---|
| ProcessingStep | Runs a SageMaker Processing job | Preprocessing, feature engineering, model evaluation |
| TrainingStep | Estimator-based model training | The main training. Output is model.tar.gz |
| TuningStep | Runs a hyperparameter tuning job | Searching for the best combination among many candidates |
| ModelStep / CreateModelStep | Creates a model object from the training artifact | The step right before deployment or batch transform |
| RegisterModel | Registers a version in the Model Registry | Starting point of the approval workflow |
| TransformStep | Runs batch transform | Large-scale offline inference |
| ConditionStep | Branches based on a condition | Accuracy threshold quality gate |
| LambdaStep | Invokes a Lambda function | Short custom logic (notifications, tagging, external APIs) |
| CallbackStep | Hands off to an external system via SQS and waits | When you must wait for a response from a system outside the pipeline |
| FailStep | Explicitly marks the pipeline as failed | When you do not want an unmet condition to end as "success" |
ProcessingStep (preprocessing) → TrainingStep (training) → ProcessingStep (evaluation) → ConditionStep (threshold) → RegisterModel (register if it passes).⚠️ Trap: "Do not register if accuracy misses the bar" is satisfied just by leaving
ConditionStep'selse_stepsempty. But if the requirement goes further — "if it misses, the pipeline must be marked as failed so that an alert fires" — you needelse_steps=[FailStep(...)]. If nothing happens when the condition is false, the execution status simply ends as Succeeded and nobody notices.
A parameter is a variable that lets you inject a value at execution time rather than fixing it at definition time. If you pull values such as instance type and count, the training data path, and the accuracy threshold out into parameters, the same definition can run in multiple environments.
| Parameter type | Holds | Example |
|---|---|---|
ParameterString | A string | S3 path, instance type, approval status |
ParameterInteger | An integer | Instance count, number of epochs |
ParameterFloat | A floating-point number | Accuracy or AUC threshold |
ParameterBoolean | True/false | A toggle such as whether to use caching |
from sagemaker.workflow.parameters import (
ParameterString, ParameterInteger, ParameterFloat,
)
# Declare parameters whose values can be injected at execution time
input_data = ParameterString(
name="InputData",
default_value="s3://my-bucket/dataset/train.csv",
)
instance_count = ParameterInteger(name="TrainingInstanceCount", default_value=1)
accuracy_threshold = ParameterFloat(name="AccuracyThreshold", default_value=0.85)At execution time you can use the defaults as they are or override them.
# Run with the defaults
pipeline.start()
# Run with overridden parameters — same definition, different inputs
pipeline.start(parameters={
"InputData": "s3://my-bucket/dataset/train_v2.csv",
"AccuracyThreshold": 0.90,
})You can do the same thing with boto3. This is the form used when running a pipeline from CI/CD.
import boto3
sm = boto3.client("sagemaker")
sm.start_pipeline_execution(
PipelineName="MLA-DemoPipeline",
PipelineParameters=[
{"Name": "InputData", "Value": "s3://my-bucket/dataset/train_v2.csv"},
{"Name": "AccuracyThreshold", "Value": "0.90"},
],
)⚠️ Trap: You must pass the parameter object as is for execution-time injection to work. If you pull out the value, as in
instance_count.default_value, the number from definition time gets baked in, and no amount of overriding at execution is reflected. Concatenating a parameter into a string is risky for the same reason.
Each step is created as a SageMaker Python SDK object. When one step's properties are referenced as the next step's input, SageMaker infers the dependency and creates a DAG edge.
from sagemaker.workflow.steps import ProcessingStep, TrainingStep
from sagemaker.workflow.pipeline import Pipeline
# 1) Preprocessing step
step_process = ProcessingStep(
name="PreprocessData",
processor=sklearn_processor,
inputs=[ProcessingInput(source=input_data, destination="/opt/ml/processing/input")],
outputs=[ProcessingOutput(output_name="train", source="/opt/ml/processing/train")],
code="preprocess.py",
)
# 2) Training step — references the preprocessing output S3 path as input (= dependency created automatically)
step_train = TrainingStep(
name="TrainModel",
estimator=xgb_estimator,
inputs={
"train": TrainingInput(
s3_data=step_process.properties.ProcessingOutputConfig.Outputs[
"train"
].S3Output.S3Uri,
content_type="text/csv",
)
},
)Because step_train references the output of step_process, SageMaker works out the "preprocessing → training" order automatically.
| How to create a dependency | When to use it |
|---|---|
Output reference (step_a.properties....) | The default. When data actually flows between steps |
depends_on=[step_a] | When no data is exchanged but you want to force an order |
| (Do nothing) | The steps are independent → they run in parallel |
properties.ModelArtifacts.S3ModelArtifacts; for processing, properties.ProcessingOutputConfig.Outputs["name"].S3Output.S3Uri.A condition step branches the pipeline based on evaluation results. The representative pattern is a quality gate: "register the model if its accuracy exceeds the threshold, otherwise do not."
from sagemaker.workflow.conditions import ConditionGreaterThanOrEqualTo
from sagemaker.workflow.condition_step import ConditionStep
from sagemaker.workflow.functions import JsonGet
# Read accuracy from the evaluation.json that the evaluation step outputs and compare it to the threshold
cond_accuracy = ConditionGreaterThanOrEqualTo(
left=JsonGet(
step_name=step_eval.name,
property_file=evaluation_report,
json_path="metrics.accuracy.value",
),
right=accuracy_threshold, # ParameterFloat
)
step_condition = ConditionStep(
name="CheckAccuracy",
conditions=[cond_accuracy],
if_steps=[step_register], # If the condition is met: register the model
else_steps=[], # If not met: do nothing
)conditions, if_steps runs only when all are true (a compound gate such as accuracy ≥ 0.85 and latency ≤ 100ms).JsonGet reads is a JSON file that the evaluation step has declared as a PropertyFile. If it is not declared, it cannot be read.ConditionGreaterThanOrEqualTo, ConditionLessThanOrEqualTo, ConditionEquals, ConditionIn, ConditionNot, and so on.from sagemaker.workflow.properties import PropertyFile
# Declare which file the evaluation step exposes as a "readable property file"
evaluation_report = PropertyFile(
name="EvaluationReport",
output_name="evaluation", # Must match the output_name of the ProcessingOutput
path="evaluation.json", # The file name the container wrote at that path
)The quality gate built with a condition step implements the core MLOps idea of "replacing manual human judgment with automated validation." If a model does not clear the baseline, the deployment flow stops automatically, so a degraded model is blocked from entering production at the code level. When the exam says "automate so that a model is registered/deployed only when accuracy is above a certain level," the answer is ConditionStep.
Gather the steps into a Pipeline object, register it with upsert, and then run it.
pipeline = Pipeline(
name="MLA-DemoPipeline",
parameters=[input_data, instance_count, accuracy_threshold],
steps=[step_process, step_train, step_eval, step_condition],
sagemaker_session=pipeline_session,
)
# Create or update the pipeline definition in SageMaker
pipeline.upsert(role_arn=role)
# Start execution
execution = pipeline.start()
execution.wait() # Wait until it completesexecution.list_steps().PipelineSession rather than a regular Session. That way estimator.fit() does not run training immediately but only produces the "step arguments."| Symptom | Common cause | Action |
|---|---|---|
| Steps that should run in order start at the same time | There is no output reference between the steps, so SageMaker treats them as independent | Reference the properties output or specify depends_on |
| The condition is always false, so registration never happens | PropertyFile not declared, output_name mismatch, or a typo in json_path | Compare the evaluation report structure and json_path against the actual JSON |
| The condition is not met but the execution ends as "Succeeded" | else_steps is empty, so it is not marked as failed | Fail explicitly with else_steps=[FailStep(...)] |
| Overriding a parameter does not change the value | default_value was pulled out and hardcoded at definition time | Pass the parameter object to the step as is |
| You changed the code but it runs with the old definition | start was called without upsert | Run pipeline.upsert() again after changing the definition |
The training step fails with AccessDenied / PassRole denied | The pipeline execution role lacks iam:PassRole or S3 permissions | Grant the execution role PassRole on the target role and bucket permissions |
| Every execution reruns from preprocessing, making it slow and expensive | Step caching is not used | Enable CacheConfig on steps that do not change |
| The DAG does not appear in Studio | upsert failed, or a different Region/domain | Check the creation Region and the execution role permissions |
Debugging a pipeline starts with separating "is it a definition problem or an execution problem?" If the DAG shape looks odd, it is a definition problem (references, depends_on); if the shape is right but only a specific step dies, it is a problem with the work that step runs (permissions, data, container). The former shows up right away if you extract the JSON with pipeline.definition().
Tomorrow we cover the Model Registry, which manages the models this pipeline registers as versions and connects them to deployment through approval.
JsonGet extracts values through it.Click a choice to reveal the answer and explanation.
Question 1
A data science team wants to automate, reproducibly within SageMaker, preprocessing → training → evaluation → (if accuracy meets the threshold) model registration. What is the most suitable service?
Question 2
In a SageMaker pipeline, you want to inject different values for the number of training instances, the training data path, and the accuracy threshold at execution time without modifying code each time. What should you use?
Question 3
You want to configure a pipeline so that the model is not registered if the model evaluation accuracy is below 0.85, and is registered in Model Registry if it is at or above that. Which step do you use?
Question 4
In SageMaker Pipelines, what happens when the output S3 path of the preprocessing step is referenced as the input of the training step?
Question 5
Which of the following is NOT considered an advantage of SageMaker Pipelines?