Course outline

15.7

Step Functions and Cognito

Orchestrated workflows, user pools vs identity pools, and row-level security for app users.

24 min Reading + animations

Tip: tap or hover any dotted-underlined term to see what it means.

By the end of this lesson you will be able to

  • Explain why a multi-step process (an order, a loan, a data job) belongs in an AWS Step Functions state machine instead of Lambda functions calling each other.
  • Read and write a small Amazon States Language definition with Task, Choice, Wait, Parallel, Map, Pass, Succeed and Fail states, plus Retry and Catch rules.
  • Pause a workflow for a human with the callback pattern (.waitForTaskToken) and know the three service integration patterns.
  • Choose between Standard and Express workflows from duration, execution guarantee, history and price, and calculate the monthly bill of each.
  • Describe a Cognito user pool (sign-up, sign-in, MFA, federation, managed login, JWTs, feature plans) and plug it into API Gateway and an ALB.
  • Use a Cognito identity pool to give app users temporary AWS credentials, with guest access and row-level security in DynamoDB (dynamodb:LeadingKeys).
  • Tell a user pool from an identity pool, and Cognito from IAM, from the exam keywords.

#Why orchestrate: the trouble with functions calling functions

Lessons 15.1 to 15.6 gave you the parts: Lambda functions, DynamoDB tables, an API in front. Real business processes are rarely one function. They are a sequence of steps with decisions, waits and failures.

You could make the Payment function call the Inventory function directly, and Inventory call Shipping. It works on day one and hurts every day after:

  • Every function must know the next one. Change the order of steps and you redeploy three functions.
  • Error handling lives in your code. Each function needs its own retry loop, backoff and “undo what I did” logic.
  • You pay for waiting. A function that synchronously waits for another is billed for the whole wait, and it can never wait longer than Lambda’s 15-minute limit (lesson 15.2). Waiting two days for a manager is impossible.
  • Nobody can see where an order is. The state is scattered across logs of several functions.

Step Functions moves all of that into one place: a state machine that calls each step, passes the data along, retries, catches errors, waits, and records the history of every execution (one run of the workflow, for example one order).

#AWS Step Functions: state machines

#Amazon States Language and Workflow Studio

A state machine is written in the Amazon States Language (ASL), a JSON document that lists the states and how they connect. You rarely type it by hand: Workflow Studio, the drag-and-drop designer in the console (also available in Visual Studio Code through the AWS Toolkit), draws the graph and writes the ASL for you. In the console you then watch every execution as the same graph, each state green, red or in progress, with its input and output.

The order workflow, shortened:

{
  "Comment": "Order workflow for the Dhaka shop",
  "StartAt": "Charge payment",
  "States": {
    "Charge payment": {
      "Type": "Task",
      "Resource": "arn:aws:states:::lambda:invoke",
      "Arguments": { "FunctionName": "payment-example", "Payload": "{% $states.input %}" },
      "Output": "{% $states.result.Payload %}",
      "Next": "Paid?"
    },
    "Paid?": {
      "Type": "Choice",
      "Choices": [{ "Condition": "{% $states.input.payment = 'paid' %}", "Next": "Reserve stock" }],
      "Default": "Payment declined"
    },
    "Reserve stock": {
      "Type": "Task",
      "Resource": "arn:aws:states:::lambda:invoke",
      "Arguments": { "FunctionName": "inventory-example", "Payload": "{% $states.input %}" },
      "Retry": [{
        "ErrorEquals": ["Lambda.TooManyRequestsException", "States.TaskFailed"],
        "IntervalSeconds": 2, "BackoffRate": 2, "MaxAttempts": 3
      }],
      "Catch": [{ "ErrorEquals": ["States.ALL"], "Next": "Refund payment" }],
      "Next": "Ship and notify"
    },
    "Ship and notify":  { "Type": "Parallel", "Branches": ["…ship…", "…SNS publish…"], "Next": "Done" },
    "Refund payment":   { "Type": "Task", "Resource": "arn:aws:states:::lambda:invoke", "Arguments": { "FunctionName": "refund-example" }, "Next": "Order failed" },
    "Payment declined": { "Type": "Fail", "Error": "PaymentDeclined", "Cause": "Card declined" },
    "Order failed":     { "Type": "Fail", "Error": "OrderFailed" },
    "Done":             { "Type": "Succeed" }
  },
  "QueryLanguage": "JSONata"
}

#The eight state types

StateWhat it doesEveryday example
TaskDoes one unit of work: invokes a Lambda function, calls an AWS service, runs an ECS task, calls an HTTPS API, or waits for an activity workerCharge the card
ChoiceBranches on the data, like an if / switch; always give it a DefaultPaid? Declined?
WaitPauses for a number of seconds or until a timestamp, at no compute costSend the review request 7 days after delivery
ParallelRuns several branches at the same time and continues when all finish; one failing branch fails the stateShip and notify at once
MapRuns the same steps for each item of an array (inline) or of a big data set in S3 (distributed)Reserve each item of the order
PassPasses its input to its output, optionally adding or reshaping data; useful for defaults and testingAdd "currency": "BDT"
SucceedEnds the execution (or a branch) successfullyOrder complete
FailEnds the execution as failed, with an error name and cause you choosePayment declined

The slide’s feature list in one line: sequence, parallel, conditions (Choice), timeouts, error handling, all as configuration.

One order, one state machine: Step Functions runs the steps

A Dhaka e-commerce shop processes each order in three steps: charge the payment, reserve the stock, ship. Each step is a small Lambda function. Step Functions decides what runs next, retries what fails and remembers where every order is.

Scenario

Start → Task (payment) → Choice → Task (stock) → Parallel (ship + notify) → Succeed.

Lambda tasks
Checkout API
Step Functionsorder-example
Customer alerts
Payment
Inventory
Shipping
Refund
start-execution

Execution order-5501

state

Start

orderId

5501

amount

2,450 taka

status

running

Step 1 of 6
1

The checkout API starts one execution of the state machine per order and hands it the order as JSON input. From here the API is free: it does not wait, and it does not need to know the steps.

#Retry and Catch

Any Task, Parallel or Map state can carry two error-handling blocks.

  • Retry: a list of rules, each with ErrorEquals (which errors), IntervalSeconds (first wait, default 1), BackoffRate (multiplier, default 2.0), MaxAttempts (default 3; 0 means never retry), and optionally MaxDelaySeconds and JitterStrategy: FULL to spread retries out. With interval 2 and backoff 2, the waits are 2, 4 and 8 seconds.
  • Catch: once retries are exhausted (or for errors with no retry rule), a matching catcher sends the execution to a recovery state, with the error placed in the data. Without a matching catcher the whole execution fails.

Error names you should recognise: States.ALL (matches every error), States.TaskFailed, States.Timeout (a state ran longer than its TimeoutSeconds, or the execution reached its maximum), States.HeartbeatTimeout, States.Permissions, States.NoChoiceMatched, and service errors such as Lambda.TooManyRequestsException or Lambda.ServiceException, plus any custom error your function throws.

The order animation’s “Catch and refund” scenario is the saga pattern: when a later step fails, run compensating actions that undo the earlier ones (refund the payment, release the stock). Step Functions is the usual way to build sagas on AWS, because the Catch rules make the undo path visible and reliable.

A failed Standard execution can be redriven within 14 days: after you fix the cause, it restarts from the state that failed instead of from the beginning, so the customer is not charged again.

#Waiting for a human: the callback pattern

The slide mentions a human approval feature, and the notes give the exam trick: if a process can take hours or days, for example waiting for manual approval, the answer is Step Functions with a Standard workflow.

Every Task that calls a service uses one of three service integration patterns:

PatternARN suffixStep Functions…Example
Request Response(none)Calls the service, gets the HTTP response, moves on immediatelyPublish to SNS, put an item in DynamoDB
Run a job.syncStarts the job and waits until it finishesRun an ECS task, a Glue job, a Batch job, a child workflow, and continue when it ends
Wait for callback.waitForTaskTokenSends a task token to someone and pauses until that token comes backEmail a manager, wait for an external system or a person

With the callback pattern, Step Functions puts a unique task token in the message it sends (an SNS email, an SQS message, a Lambda payload). Whoever finishes the work calls SendTaskSuccess (with the output) or SendTaskFailure (with an error) and passes the token back. Long-running workers can also call SendTaskHeartbeat; if they stop, HeartbeatSeconds fails the task. Always set TimeoutSeconds on a callback Task, or it may wait for the one-year maximum.

Waiting for a human: the task token callback

A microfinance lender approves small loans automatically, but a loan officer must approve any loan where the credit score is borderline. The workflow pauses for days without running anything, then continues the moment the officer clicks.

Scenario

Loan app
Step FunctionsStandardrunning
Notifyemail
Loan officer
Approve link+ Lambda
start: 5 lakh taka loan
Step 1 of 8
1

A shop owner in Narayanganj applies for a loan of 5 lakh taka. The app starts a Standard workflow execution with the application as input.

#What Step Functions can call

The slide says Step Functions integrates with EC2, ECS, on-premises servers, API Gateway, SQS and more. Today there are three layers:

  • Optimized integrations with special support (.sync, callbacks, simpler parameters): Lambda, ECS and Fargate, AWS Batch, DynamoDB, SNS, SQS, Glue, SageMaker, EventBridge, API Gateway, Athena, EMR, Bedrock, and other Step Functions workflows (nested workflows).
  • AWS SDK integrations: call almost any API of over 220 AWS services directly from a Task (arn:aws:states:::aws-sdk:s3:copyObject), with no Lambda function in between. Older material says “200+ services”; both mean “nearly everything”.
  • HTTP Task: call any public HTTPS API (a payment gateway, Salesforce) with credentials kept in an EventBridge connection.

For code that runs outside AWS’s managed services, such as a program on an EC2 instance or an on-premises server, use an activity: the worker polls GetActivityTask, does the job and returns the result with the task token. Activities are Standard-only.

Executions start from the console, the SDK or CLI (StartExecution), API Gateway, an EventBridge rule or schedule (lesson 19.5), a Lambda function, or another state machine. Slide use cases: order fulfilment, data processing, web applications, any workflow.

Calling services directly instead of through “glue” Lambda functions is cheaper and has fewer moving parts: a dynamodb:putItem Task has no cold start, no code to patch and no Lambda bill.

#Standard vs Express workflows

When you create a state machine you choose its type, and you cannot change it later. The notes give the headline: Standard runs for up to 1 year (long-running processes), Express for up to 5 minutes (high-volume, fast work). The rest matters just as much:

StandardExpress
Maximum duration1 year5 minutes
Execution guaranteeExactly-once: each state runs once unless you add RetryAsynchronous: at-least-once; synchronous: at-most-once
SuitsLong, durable, auditable, non-idempotent steps (payments, starting an EMR cluster)Short, high-volume, idempotent event processing (IoT ingestion, streaming transforms, mobile and API back ends)
PricePer state transition: 0.025 USD per 1,000 (4,000 free a month, forever)Per execution + duration × memory: 1.00 USD per million, plus 0.06 USD per GB-hour for the first 1,000 GB-hours (lower tiers above)
Execution historyKept by Step Functions for 90 days, visual debugging in the consoleNot kept: turn on CloudWatch Logs
Callback (.waitForTaskToken) and .syncYesNo
Distributed Map, activities, redriveYesNo
Start rate (default)About 300 per second in the largest Regions (token bucket, raisable)About 6,000 per second; no limit on state transitions
How it is startedAsynchronously (StartExecution)Asynchronous (StartExecution) or synchronous (StartSyncExecution, returns the result, for example behind API Gateway)

Prices are US East (N. Virginia) list prices, checked October 2026. The Lambda functions and other services a workflow calls are billed separately.

A worked example: an API back end runs 1,000,000 executions a month, each with 10 state transitions, lasting 2 seconds at 64 MB.

  • Standard: 10,000,000 transitions − 4,000 free = 9,996,000 × 0.000025 = about 249.90 USD.
  • Express: 1,000,000 × 1.00 / 1,000,000 = 1.00 USD for requests; duration 1,000,000 × 2 s × 0.0625 GB = 125,000 GB-seconds = 34.7 GB-hours × 0.06 = 2.08 USD; total about 3.08 USD.

The same workflow is 80 times cheaper on Express, if it is short and idempotent. Flip it around: a loan approval that runs 3,000 times a month with 12 transitions each is 36,000 transitions, 0.80 USD on Standard, and Express cannot do it at all (it waits for days and needs a callback).

Standard or Express: what does one workflow cost?

Pick how often the workflow runs, how many states each run passes through, how long a run takes and how much memory it uses. Say whether it waits for a person or a long job, and whether a step must never run twice. Then calculate a month both ways.

Executions a month

State transitions per execution

Average duration

Memory (billed in 64 MB steps)

Waits for a person or a long job?

A step must never run twice (payments)?

Press “Calculate” to see the result.

#Map at scale: Distributed Map

The inline Map runs iterations inside one execution (up to 40 at a time, and every iteration’s events count toward that execution’s 25,000-entry history), so the whole array must fit in the 256 KiB limit for a state’s input and output. The Distributed Map (Standard only) reads items straight from S3 (a CSV, a JSON array, or every object under a prefix) and starts up to 10,000 child workflow executions in parallel, each of which can be Express. It is the answer for “process millions of S3 objects” with retries and a report of failures, without writing your own fan-out.

#Amazon Cognito: identity for your app’s users

IAM (chapter 2) is for the people and workloads that manage AWS. Your shop’s customers, your app’s riders, your university’s students are not AWS users, and there may be millions of them. Amazon Cognito gives users of your web and mobile apps an identity: sign-up, sign-in, and access control. The notes stress it has two parts, and this is where the exam’s most confusing questions come from:

User pool (CUP)Identity pool (CIP, “federated identities”)
Notes’ pictureIdentity card: proves who you areOffice access card: opens specific doors
JobAuthentication (“who are you?”): a user directory with sign-up, sign-in, password reset, MFAAuthorization to AWS (“what may you do in AWS?”): temporary AWS credentials
Gives the appJWTs (ID, access and refresh tokens)An access key, secret key and session token from STS
Used withYour API (API Gateway, ALB), your backendAWS services directly: S3, DynamoDB, IoT, Kinesis…
GuestsNo (only signed-in users)Yes, optional unauthenticated role

The two are often used together: the user signs in to the user pool, and the identity pool trusts the user pool’s token and turns it into AWS credentials.

#Cognito user pools

A user pool is a serverless user directory for your web and mobile apps. Features from the slides, verified against today’s service:

  • Sign-up and sign-in with a username or email (or phone) and a password, with a password policy you set.
  • Email and phone number verification with a code, and password reset (“forgot password”) through the verified address.
  • MFA: authenticator app (TOTP), SMS, and email one-time codes; optional or required. Passwordless sign-in with passkeys or one-time codes is also available.
  • Federation: let users sign in with Google, Facebook, Apple, Login with Amazon, or a corporate SAML 2.0 or OIDC identity provider, and still get the same tokens from your pool.
  • Managed login: Cognito hosts ready-made sign-in, sign-up and reset pages for your domain (the older version is called the hosted UI). It is also an OAuth 2.0 / OIDC authorization server.
  • Lambda triggers to customise each stage: pre sign-up (block disposable emails), post confirmation (create a profile row, send a welcome message), pre token generation (add claims), custom auth challenges, user migration from an old database.
  • Groups, with optional IAM roles for the identity pool, and user import from CSV.
  • Compromised-credential checks and adaptive authentication (the threat protection of the Plus plan): block or challenge sign-ins that use a leaked password or come from an unusual device or place.

After sign-in the pool returns three tokens:

TokenFormatSaysDefault lifetime
ID tokenJWTWho the user is: sub, email, name, groups1 hour (5 minutes to 1 day)
Access tokenJWTWhat the app may call: scopes, groups1 hour (5 minutes to 1 day)
Refresh tokenOpaqueGets new ID and access tokens without a password30 days (1 hour to 10 years)

The JWTs are signed with the pool’s private key. Anyone can verify them with the pool’s public keys (the JWKS URL), without calling Cognito, which is why API Gateway and the ALB can check them at line speed.

#Two front doors: API Gateway and the ALB

The slides show user pools integrating with exactly two AWS front doors:

  • API Gateway: the app signs in, gets a token and sends it in the Authorization header. A Cognito user pool authorizer (REST API) or a JWT authorizer (HTTP API) checks signature, expiry, issuer and audience, and passes the claims to the backend; invalid tokens get 401 without touching your code (lesson 15.6).
  • Application Load Balancer: an HTTPS listener rule with an authenticate-cognito action redirects a browser without a session to the managed login page, exchanges the code for tokens itself, sets a session cookie and forwards the request with x-amzn-oidc-* headers that carry the user’s identity (lesson 6.3). The application behind it needs no login code at all.

Cognito user pool: sign-up, sign-in and tokens

A ride-sharing app keeps its riders in a Cognito user pool. The pool stores the users and their passwords, checks them at sign-in and hands out JWT tokens that API Gateway or an ALB can check without asking Cognito again.

Scenario

Clientapp or browser
User poolriders-example
API Gateway
ALB
Backend
sign up: email + password
Step 1 of 5
1

Rafi installs the app and signs up with his email and a password. The app calls the user pool directly; there is no user table or password hashing in your own code.

A quick sum: a ride-sharing app with 60,000 monthly active riders on Essentials pays (60,000 − 10,000) × 0.015 = 750 USD a month; on Plus 60,000 × 0.020 = 1,200 USD, the price of automatic breach and risk checks.

#Cognito identity pools

An identity pool gets identities for users so they can obtain temporary AWS credentials and call AWS services directly (or through API Gateway with IAM authorization). It does not store users or passwords; it trusts other identity providers:

  • a Cognito user pool,
  • social logins (Google, Facebook, Apple, Login with Amazon),
  • any SAML or OIDC provider,
  • developer-authenticated identities (your own existing login system),
  • and, if you allow it, no provider at all: guest (unauthenticated) users.

How it works (the slide’s diagram): the app logs in and gets a token from the provider → exchanges the token at the identity pool → the pool validates the token, gives the user a stable identity ID, picks an IAM role and calls STS (AssumeRoleWithWebIdentity, the federation call from lesson 2.4) → the app receives temporary credentials (about one hour; the SDK renews them) → direct access to the private S3 bucket and the DynamoDB table.

Which role? By default, one role for authenticated users and one for guests. You can also map users to roles by rules (for example, a claim department = finance) or take the role from the user pool group in the token. Each role’s trust policy names cognito-identity.amazonaws.com and the identity pool ID, so nobody else can assume it.

#Row-level security with policy variables

One role serves every user, but each user must reach only their own data. The trick is the policy variable ${cognito-identity.amazonaws.com:sub}, which IAM replaces with the caller’s identity ID at request time (the same idea as ${aws:username} in lesson 2.1).

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "OwnPhotosOnly",
      "Effect": "Allow",
      "Action": ["s3:GetObject", "s3:PutObject"],
      "Resource": "arn:aws:s3:::student-photos-example/${cognito-identity.amazonaws.com:sub}/*"
    },
    {
      "Sid": "OwnGradesOnly",
      "Effect": "Allow",
      "Action": ["dynamodb:GetItem", "dynamodb:Query", "dynamodb:PutItem", "dynamodb:UpdateItem"],
      "Resource": "arn:aws:dynamodb:ap-southeast-1:111122223333:table/grades-example",
      "Condition": {
        "ForAllValues:StringEquals": {
          "dynamodb:LeadingKeys": ["${cognito-identity.amazonaws.com:sub}"]
        }
      }
    }
  ]
}

dynamodb:LeadingKeys is the partition key value of the items a request touches (lesson 15.4). The condition says “only items whose partition key is your own identity ID”. That is row-level security in DynamoDB (the slide title) with zero authorization code. Two details people get wrong: the table’s partition key must hold the identity pool’s identity ID (like ap-southeast-1:7c1e…), not the user pool’s sub; and a Scan cannot be limited this way, so leave it out of the policy.

Cognito identity pool: AWS credentials for app users

A university app lets each student upload a profile photo to S3 and read their own grades from DynamoDB, directly from the phone, with no API in between. The identity pool turns a login into temporary AWS credentials that only reach the student’s own data.

Scenario

User pool login → token exchanged at the identity pool → STS credentials → direct PUT to the student’s own S3 folder.

Student app
User poollogin
Identity poolcredentials
STS
Photos bucketphotos-example
Grades table
sign inID token

What the app holds

ID token

from the user pool

AWS credentials

none yet

Step 1 of 5
1

Nusrat signs in to the university’s user pool and gets an ID token. That token proves who she is, but S3 does not accept it: every S3 request must be signed with AWS credentials.

#User pool or identity pool: the exam keywords

The notes’ cheat sheet:

  • “Sign-up”, “sign-in”, “JWT tokens”, “user directory”, “social login for my app”, “hosted login page”, “authenticate users of API Gateway / ALB” → Cognito user pool.
  • “Temporary AWS credentials”, “give access to S3 / DynamoDB directly from a mobile app”, “unauthenticated / guest access”, “per-user folder in S3”, “fine-grained access to DynamoDB items” → Cognito identity pool.
  • Both, when the app signs users in and calls AWS directly.

Memory aid from the notes: User pool = authentication (who are you?); identity pool = authorization (what may you do in AWS?).

#Cognito or IAM?

The slide’s keywords for picking Cognito over IAM: “hundreds of users” (or thousands, millions), “mobile users”, “authenticate with SAML” for an application. IAM users are for a limited number of people who administer AWS; for a workforce signing in to AWS accounts, the modern answer is IAM Identity Center (lesson 20.3). Creating an IAM user per app customer, or embedding an IAM user’s access keys in a mobile app, is always wrong: the keys are long-term, and anyone who unpacks the app gets them.

#What goes wrong

SymptomCauseFix
Execution fails with States.NoChoiceMatchedA Choice state with no matching rule and no DefaultAlways add Default
Customer charged, no product, execution “failed”A later step failed and there was no CatchAdd Catch with compensating steps (saga), then redrive
Payment taken twiceRetry on a non-idempotent step, or async Express (at-least-once)Idempotency key; Standard for non-idempotent steps
Execution waits foreverCallback Task without TimeoutSeconds; the token was lostSet a timeout and a Catch on States.Timeout; heartbeats for long workers
Creating the state machine fails.waitForTaskToken, .sync or Distributed Map in an Express workflowUse Standard (or a Standard parent)
Express run killed at 5 minutesWork longer than the Express limitStandard, or split the work
“Execution history exceeded” failureA Standard execution reached 25,000 history events (a long loop)Move the loop to a child workflow or Distributed Map
States.DataLimitExceededState input or output over 256 KiBStore the data in S3 and pass the key
Express failures invisibleExpress keeps no historyEnable CloudWatch Logs (and X-Ray)
Task fails with States.Permissions / AccessDeniedThe state machine’s execution role lacks the permissionAdd the action to the role (e.g. lambda:InvokeFunction)
API returns 401 every morningAccess token expired after 1 hourUse the refresh token; SDKs do this automatically
S3 returns 403 for a signed-in userApp sent the user pool JWT to S3Identity pool, or your API / presigned URL
DynamoDB AccessDenied for the right userTable keyed by user pool sub, policy uses the identity IDKey items by the identity pool identity ID
Guests can write dataUnauthenticated role too broadGive the guest role read-only, public data
Breached passwords not blockedLite or Essentials planPlus plan threat protection, or enforce MFA

#Step Functions and Cognito in system design

  • Order and payment pipelines: Standard workflow, Catch with compensations (saga), idempotency keys, SNS notifications, redrive after fixes.
  • Human-in-the-loop: loan approval, content moderation, customer identity checks: .waitForTaskToken with a timeout and an escalation path.
  • Data processing: Distributed Map over S3, a Glue or EMR job with .sync, Athena queries as SDK integration Tasks; Express children for per-record work.
  • High-volume event processing: Kinesis or SQS → Lambda → asynchronous Express workflow, or API Gateway → synchronous Express workflow for an API that calls several services.
  • App identity: user pool + API Gateway authorizer for the API; identity pool for direct S3 uploads and DynamoDB reads; WAF on the user pool and API (chapter 21); the full mobile-app architecture, with DAX and caching, comes in lesson 15.8.

Step Functions vs SQS: SQS buffers and decouples (a producer does not care who processes the message or when); Step Functions coordinates a known sequence and tracks state. They combine well: a Task can send to SQS with a task token and wait for the consumer’s answer.

Which one fits?

Pick a scenario, read its keywords, and choose: a Standard or Express workflow, a Cognito user pool or identity pool, or plain IAM. Then read why.

1. Choose a scenario(Right first time: 0/10)

A microfinance lender scores each loan application automatically, but borderline cases must wait for a loan officer to approve them, which can take up to three days.

Keywords:manual approvaldayscallback

2. Your answer

#Try it yourself

Region ap-southeast-1, account 111122223333; output trimmed. The functions and roles must already exist.

# 1. Create the order state machine (Standard) from the ASL file above
aws stepfunctions create-state-machine --name order-example --type STANDARD \
  --definition file://order.asl.json \
  --role-arn arn:aws:iam::111122223333:role/sfn-order-example
#   { "stateMachineArn": "arn:aws:states:ap-southeast-1:111122223333:stateMachine:order-example",
#     "creationDate": "2026-10-28T10:02:11.402000+06:00" }

# 2. Start one execution per order; the name makes the start idempotent
aws stepfunctions start-execution \
  --state-machine-arn arn:aws:states:ap-southeast-1:111122223333:stateMachine:order-example \
  --name order-5501 --input '{"orderId":"5501","amount":2450,"items":["T-shirt","Panjabi"]}'
#   { "executionArn": "arn:aws:states:ap-southeast-1:111122223333:execution:order-example:order-5501",
#     "startDate": "2026-10-28T10:03:40.118000+06:00" }

aws stepfunctions describe-execution \
  --execution-arn arn:aws:states:ap-southeast-1:111122223333:execution:order-example:order-5501 \
  --query '{status:status,output:output}'
#   { "status": "SUCCEEDED", "output": "{\"orderId\":\"5501\",\"payment\":\"paid\",\"reservation\":\"R-77\"}" }

aws stepfunctions get-execution-history \
  --execution-arn arn:aws:states:ap-southeast-1:111122223333:execution:order-example:order-5501 \
  --query 'events[].type' --output text
#   ExecutionStarted TaskStateEntered TaskScheduled TaskStarted TaskSucceeded TaskStateExited
#   ChoiceStateEntered ChoiceStateExited … ParallelStateSucceeded SucceedStateEntered ExecutionSucceeded

# 3. Answer a waiting approval task (the token came in the officer's email)
aws stepfunctions send-task-success --task-token "$TASK_TOKEN" --task-output '{"approved":true}'
aws stepfunctions send-task-failure --task-token "$TASK_TOKEN" --error Rejected --cause "Trade licence expired"

# 4. After fixing the warehouse database, restart a failed order from the failed state
aws stepfunctions redrive-execution \
  --execution-arn arn:aws:states:ap-southeast-1:111122223333:execution:order-example:order-5504
#   { "redriveDate": "2026-10-28T11:20:05.771000+06:00" }

# 5. An Express workflow called synchronously (the API Gateway pattern)
aws stepfunctions start-sync-execution \
  --state-machine-arn arn:aws:states:ap-southeast-1:111122223333:stateMachine:quote-express-example \
  --input '{"restaurant":"R-12","area":"Dhanmondi"}'
#   { "status": "SUCCEEDED", "output": "{\"price\":540,\"etaMinutes\":32}",
#     "billingDetails": { "billedMemoryUsedInMB": 64, "billedDurationInMilliseconds": 300 } }

# 6. A user pool on the Essentials plan, email sign-in, optional MFA
aws cognito-idp create-user-pool --pool-name riders-example --user-pool-tier ESSENTIALS \
  --username-attributes email --auto-verified-attributes email --mfa-configuration OFF
#   { "UserPool": { "Id": "ap-southeast-1_Ab12Cd34E", "UserPoolTier": "ESSENTIALS", … } }

aws cognito-idp create-user-pool-client --user-pool-id ap-southeast-1_Ab12Cd34E \
  --client-name rider-app --no-generate-secret \
  --explicit-auth-flows ALLOW_USER_SRP_AUTH ALLOW_USER_PASSWORD_AUTH ALLOW_REFRESH_TOKEN_AUTH
#   { "UserPoolClient": { "ClientId": "4exampleclientid0000000000", … } }

aws cognito-idp sign-up --client-id 4exampleclientid0000000000 \
  --username rafi@example.com --password 'Long-Passphrase-2026!'
#   { "UserConfirmed": false, "CodeDeliveryDetails": { "Destination": "r***@e***", "DeliveryMedium": "EMAIL" },
#     "UserSub": "d1b2c3d4-5678-90ab-cdef-EXAMPLE11111" }

aws cognito-idp confirm-sign-up --client-id 4exampleclientid0000000000 \
  --username rafi@example.com --confirmation-code 482913

# Password flow only for this CLI test; apps use SRP or managed login
aws cognito-idp initiate-auth --client-id 4exampleclientid0000000000 --auth-flow USER_PASSWORD_AUTH \
  --auth-parameters USERNAME=rafi@example.com,PASSWORD='Long-Passphrase-2026!'
#   { "AuthenticationResult": { "AccessToken": "eyJraWQiOi…", "ExpiresIn": 3600, "TokenType": "Bearer",
#     "RefreshToken": "eyJjdHkiOi…", "IdToken": "eyJraWQiOi…" } }

# 7. An identity pool that trusts the user pool, with no guest access
aws cognito-identity create-identity-pool --identity-pool-name students_example \
  --no-allow-unauthenticated-identities \
  --cognito-identity-providers ProviderName=cognito-idp.ap-southeast-1.amazonaws.com/ap-southeast-1_Ab12Cd34E,ClientId=4exampleclientid0000000000
#   { "IdentityPoolId": "ap-southeast-1:0a1b2c3d-1111-2222-3333-EXAMPLE44444", … }

aws cognito-identity set-identity-pool-roles \
  --identity-pool-id ap-southeast-1:0a1b2c3d-1111-2222-3333-EXAMPLE44444 \
  --roles authenticated=arn:aws:iam::111122223333:role/student-auth-example

# 8. Exchange the ID token for an identity ID, then for AWS credentials
aws cognito-identity get-id --identity-pool-id ap-southeast-1:0a1b2c3d-1111-2222-3333-EXAMPLE44444 \
  --logins cognito-idp.ap-southeast-1.amazonaws.com/ap-southeast-1_Ab12Cd34E=$ID_TOKEN
#   { "IdentityId": "ap-southeast-1:7c1e5f2a-aaaa-bbbb-cccc-EXAMPLE55555" }

aws cognito-identity get-credentials-for-identity \
  --identity-id ap-southeast-1:7c1e5f2a-aaaa-bbbb-cccc-EXAMPLE55555 \
  --logins cognito-idp.ap-southeast-1.amazonaws.com/ap-southeast-1_Ab12Cd34E=$ID_TOKEN
#   { "IdentityId": "ap-southeast-1:7c1e5f2a-…",
#     "Credentials": { "AccessKeyId": "ASIAEXAMPLE7Q2K", "SecretKey": "…", "SessionToken": "…",
#                      "Expiration": "2026-10-28T12:15:02+06:00" } }

Put those three values in AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY and AWS_SESSION_TOKEN, then try aws s3 cp avatar.jpg s3://student-photos-example/ap-southeast-1:7c1e5f2a-aaaa-bbbb-cccc-EXAMPLE55555/avatar.jpg (allowed) and the same command with another student’s folder (AccessDenied).

#Summary

Key takeaways

  • Step Functions orchestrates steps as a state machine written in Amazon States Language (ASL), designed visually in Workflow Studio. Exam phrases: “orchestrate multiple Lambda functions”, “coordinate microservices”, “state machine”.
  • States: Task, Choice (always a Default), Wait, Parallel, Map (inline or Distributed over S3), Pass, Succeed, Fail. Retry (interval, backoff, max attempts) and Catch (to a recovery state) are configuration, not code; compensations make a saga; failed Standard executions can be redriven within 14 days.
  • Integration patterns: Request Response, Run a job (.sync), Wait for callback (.waitForTaskToken) for human approval, with SendTaskSuccess / SendTaskFailure, a timeout and heartbeats. Optimized integrations (Lambda, ECS, Batch, DynamoDB, SNS, SQS, Glue, SageMaker…), SDK integrations with over 220 services, HTTP Tasks, activities for EC2 and on-premises workers.
  • Standard: up to 1 year, exactly-once, 90-day history, callbacks and .sync, priced per state transition (0.025 USD per 1,000). Express: up to 5 minutes, at-least-once (async) or at-most-once (sync), CloudWatch Logs only, priced per execution + GB-second; far cheaper for short high-volume work. Hours or days or manual approval → Standard.
  • Cognito user pool = authentication: user directory, sign-up / sign-in, verification, password reset, MFA, social / SAML / OIDC federation, managed login, Lambda triggers, JWTs (ID and access 1 h, refresh 30 days). Integrates with API Gateway authorizers and the ALB authenticate-cognito action. Feature plans Lite / Essentials (default) / Plus (threat protection, compromised credentials).
  • Cognito identity pool = temporary AWS credentials through STS for direct S3 / DynamoDB access; sources: user pools, social, SAML, OIDC, developer identities; authenticated and guest roles; ${cognito-identity.amazonaws.com:sub} and dynamodb:LeadingKeys give per-user folders and row-level security.
  • Cognito vs IAM: “hundreds of users”, “mobile users”, “authenticate with SAML” for an app → Cognito. IAM (Identity Center) is for people who manage AWS; never ship IAM keys in an app.