Build an Event-Driven Order Pipeline
Wire DynamoDB Streams to Lambda to process orders in real time.
Introduction
30 Second Summary
Think about what happens when you place an order online. Behind the scenes, dozens of things fire off automatically: inventory updates, payment processing, shipping notifications. None of these systems call each other directly. They react to events.
In this project, you will build your own event-driven order processing system on AWS. You will wire together a database, a serverless function, and a streaming pipeline so that inserting an order automatically triggers processing logic with zero manual intervention.
What You'll Build
A serverless pipeline where inserting a new order into a DynamoDB table automatically triggers a Lambda function that processes the order in real time.
By the end of this project, you'll have:
- A DynamoDB table with streams that captures every data change and fires events automatically.
- A Python Lambda function that processes order inserts, updates, and deletes in real time.
- An event source mapping that connects the stream to your function, proving how decoupled cloud systems communicate through events.
- Secret Mission: Add an SNS email notification that alerts you whenever a high-value order is placed.
Are there any prerequisites?
You need an AWS account and basic familiarity with the command line. If you have worked with services like EC2 or S3 before, you are ready for this project.
Verify Your Tools and Configure AWS Access
Before you can wire cloud services together, your local machine needs a way to talk to AWS. The AWS CLI is a command-line tool that lets you create, configure, and manage AWS resources directly from your terminal.
In this step, you will verify the CLI is installed, hand it your credentials so it can act on your behalf, and create a project folder for your Lambda code.
In this step, get ready to:
- Verify the AWS CLI is installed and working.
- Configure your AWS credentials for CLI access.
- Create and open your project folder.
Verify your AWS CLI installation
The AWS CLI is how you will create every resource in this project. Let's confirm it is installed.
- Press the Windows key to open your search bar.
- Type PowerShell and press Enter to open it.
- Check your AWS CLI version by running this command:
aws --version
✔️ I see a version number
You should see output like aws-cli/2.x.x followed by Python version and OS info. Any 2.x version works for this project.
Skip ahead to the next substep.
ⓧ Command not found
The CLI is not installed yet. Let's fix that.
- Download the AWS CLI installer from this link:
- https://awscli.amazonaws.com/AWSCLIV2.msi.
- Run the downloaded AWSCLIV2.msi file and follow the installer prompts (accept all defaults).
- Close PowerShell completely, then reopen it.
- Run aws --version again to confirm the install succeeded.
Still seeing 'command not found'?
Make sure you fully closed and reopened PowerShell after the installer finished. The PATH update only takes effect in new terminal sessions.
If that does not help, try restarting your computer and running the command again.
help me install the AWS CLI on Windows
Configure your AWS credentials
The CLI needs credentials to prove it is allowed to create resources in your AWS account. You will provide an Access Key ID and Secret Access Key that link the CLI to your account.
Where do I find my access keys?
If you do not have access keys yet, create them in the AWS Console. Go to IAM > Users > select your user > Security credentials tab > Create access key.
If you are using the root account, go to your account name in the top right > Security credentials > scroll to Access keys > Create access key. Copy both values immediately because the Secret Access Key is only shown once.
- Start the credential configuration wizard by running this command:
aws configure
The CLI will prompt you for four values, one at a time. Enter the following:
- For AWS Access Key ID, paste your Access Key ID.
- For AWS Secret Access Key, paste your Secret Access Key.
- For Default region name, type us-east-1.
- For Default output format, type json.
- Verify your credentials are working by running this command:
aws sts get-caller-identity
You should see a JSON response with your Account ID, UserId, and ARN. This confirms the CLI can authenticate with AWS.
Seeing 'Unable to locate credentials'?
Run aws configure again and double-check that you pasted the Access Key ID and Secret Access Key without extra spaces or line breaks.
Make sure you are running the command in the same PowerShell window where you ran aws configure.
help me fix AWS CLI credential errors
Create your project folder
You need a dedicated folder to hold the Lambda function code you will write in a later step. Let's create it now and open it in your editor.
- Create the project folder and move into it by running these commands:
mkdir ~/OrderProcessor
cd ~/OrderProcessor
What does this do?
The ~ symbol resolves to your user home directory (e.g. C:\Users\YourName). The mkdir command creates the folder, and cd moves you into it.
- Confirm the folder was created by running:
pwd
You should see a path ending in OrderProcessor.
- Open this folder in VS Code by running:
code .
Don't have VS Code?
Any text editor works for this project. If you prefer Notepad++ or another editor, just open the OrderProcessor folder in that editor instead.
'code' is not recognized?
If code . does not work, VS Code's PATH entry may be missing. Open VS Code manually, then press Ctrl+Shift+P, type Shell Command: Install 'code' command in PATH, and press Enter. Restart PowerShell and try again.
help me open my project folder in VS Code from PowerShell
Your machine is now connected to AWS and ready to create cloud resources. Next up, you will create the DynamoDB table that stores orders and generates the stream events that power the entire pipeline.
Create the DynamoDB Orders Table with Streams
Your tools are ready and your project folder is set up. Now it's time to create the first piece of your event-driven system.
Every event-driven pipeline starts with a data source that emits events. In this project, that source is a DynamoDB table with DynamoDB Streams enabled. Streams capture every change to the table (inserts, updates, deletes) and make that change data available for other services to react to.
In this step, get ready to:
- Create a DynamoDB table called Orders with streams enabled.
- Verify the table is active and streaming.
- Save the Stream ARN for later use.
Create the Orders table
DynamoDB is a fully managed NoSQL database on AWS. You are going to create a table that stores order records, with each order identified by a unique OrderId.
- In your PowerShell terminal, make sure you are inside the OrderProcessor folder.
- Create the DynamoDB table by running this command:
aws dynamodb create-table --table-name Orders --attribute-definitions AttributeName=OrderId,AttributeType=S --key-schema AttributeName=OrderId,KeyType=HASH --billing-mode PAY_PER_REQUEST --stream-specification StreamEnabled=true,StreamViewType=NEW_AND_OLD_IMAGES
What does this command do?
- --table-name Orders names your table Orders.
- --attribute-definitions and --key-schema set up OrderId as the partition key (the unique identifier for each item).
- --billing-mode PAY_PER_REQUEST means you only pay for actual reads and writes. No capacity planning needed.
- --stream-specification enables DynamoDB Streams with NEW_AND_OLD_IMAGES. This captures both the before and after state of every record that changes.
Why NEW_AND_OLD_IMAGES?
DynamoDB Streams supports four view types: KEYS_ONLY, NEW_IMAGE, OLD_IMAGE, and NEW_AND_OLD_IMAGES.
By choosing the richest option, your Lambda function can compare old and new values on every update. This is called change data capture. It lets you detect exactly what changed without making another database call.
You should see a JSON response with details about your new table, including TableStatus: CREATING.
Seeing an error?
- If you see ResourceInUseException, a table called Orders already exists. Delete it first with aws dynamodb delete-table --table-name Orders and then try again.
- If you see Unable to locate credentials, your AWS CLI is not configured. Go back to Step 1 and run aws configure.
- help me debug a DynamoDB create-table error
Verify the table is active
DynamoDB tables take a few seconds to become fully available. You need to confirm your table is ready before proceeding.
- Check the table status by running this command:
aws dynamodb describe-table --table-name Orders
What should I see?
You should see a large JSON response. Look for two key pieces:
- "TableStatus": "ACTIVE" confirms the table is ready to use.
- "StreamEnabled": true confirms streams are capturing changes.
Still seeing CREATING?
Wait 5-10 seconds and run the describe-table command again. DynamoDB tables usually become active within a few seconds.
my DynamoDB table is stuck in CREATING status
Save the Stream ARN
The Stream ARN is a unique address that identifies your table's stream. You will need this value in Step 5 to connect the stream to your Lambda function.
- In the JSON output from the previous command, find the LatestStreamArn field. It looks something like this:
"LatestStreamArn": "arn:aws:dynamodb:us-east-1:123456789012:table/Orders/stream/2026-05-09T00:00:00.000"
- Copy the full ARN value (everything inside the quotes after LatestStreamArn).
- Paste it somewhere safe, like a new file in VS Code or a sticky note. You will use it in Step 5.
Quick way to grab the ARN
You can also run this filtered command to get just the stream ARN:
aws dynamodb describe-table --table-name Orders --query "Table.LatestStreamArn" --output text
This outputs only the ARN with no surrounding JSON, making it easy to copy.
Your data source is live. The Orders table is actively streaming every change. Next up, you will create the IAM role that gives your Lambda function permission to read from this stream.
Create the IAM Execution Role for Lambda
Your DynamoDB table is live and streaming every change it receives. Now you need a Lambda function to process those stream events. But Lambda can't just reach into your AWS resources without permission.
In AWS, every service that acts on your behalf needs an IAM role that defines exactly what it's allowed to do. In this step, you'll create an execution role that grants your Lambda function two specific permissions: reading records from DynamoDB Streams and writing logs to CloudWatch.
In this step, get ready to:
- Create a trust policy that allows Lambda to assume the role.
- Create the IAM role using the AWS CLI.
- Attach a managed policy that grants DynamoDB stream and CloudWatch access.
Create the trust policy file
A trust policy tells AWS which service is allowed to "assume" (use) a role. Your trust policy will say: "Only the Lambda service can use this role."
- In VS Code, create a new file called trust-policy.json inside your OrderProcessor folder.
- Paste the following JSON into the file:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "lambda.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
}
What does this policy do?
This trust policy has one statement: it allows the Lambda service (lambda.amazonaws.com) to perform sts:AssumeRole. This means only Lambda functions can use this role.
Without a trust policy, no service can assume the role. It's the lock on the door that decides who gets the keys.
- Save trust-policy.json.
- Confirm the file exists by running:
ls
You should see trust-policy.json listed in the output.
Don't see the file?
Make sure you saved the file in VS Code (Ctrl+S). Check that your terminal is in the OrderProcessor folder by running pwd. If you're somewhere else, run cd ~/OrderProcessor to navigate back.
Help me find my trust-policy.json file
Create the IAM role
Now that the trust policy file exists, you can create the IAM role itself. The role is like a badge that Lambda will wear when it needs to access your AWS resources.
- Create the IAM role by running this command:
aws iam create-role --role-name OrderProcessorRole --assume-role-policy-document file://trust-policy.json
What does this command do?
This creates an IAM role called OrderProcessorRole and attaches the trust policy you just wrote. The file:// prefix tells the AWS CLI to read the policy from a local file instead of expecting it inline.
You should see a JSON response containing the role details, including an Arn field. It looks something like arn:aws:iam::123456789012:role/OrderProcessorRole.
- Copy the Arn value from the output and save it somewhere (a sticky note, a text file, or your clipboard). You'll need it in Step 4 when you deploy the Lambda function.
Seeing an EntityAlreadyExists error?
This means a role called OrderProcessorRole already exists in your account (perhaps from a previous attempt). Delete it first by running:
aws iam delete-role --role-name OrderProcessorRole
Then re-run the create-role command above.
Help me fix my IAM role creation error
Attach the managed policy
The role exists, but right now it has zero permissions. You need to attach a policy that tells AWS what this role is allowed to do.
AWS provides a pre-built managed policy called AWSLambdaDynamoDBExecutionRole that grants exactly what your function needs: permission to read from DynamoDB Streams and write logs to CloudWatch.
- Attach the policy to your role by running this command:
aws iam attach-role-policy --role-name OrderProcessorRole --policy-arn arn:aws:iam::aws:policy/service-role/AWSLambdaDynamoDBExecutionRole
What does this command do?
This attaches the AWS-managed AWSLambdaDynamoDBExecutionRole policy to your role. The policy grants two sets of permissions:
- DynamoDB Streams access: read records, get shard iterators, and describe streams.
- CloudWatch Logs access: create log groups, create log streams, and write log events.
This command produces no output on success. Silence means it worked.
- Verify the policy is attached by running:
aws iam list-attached-role-policies --role-name OrderProcessorRole
You should see a JSON response listing AWSLambdaDynamoDBExecutionRole under AttachedPolicies.
Seeing a NoSuchEntity error?
This means the role name doesn't match exactly. Check for typos in OrderProcessorRole. The name is case-sensitive.
Help me troubleshoot my IAM policy attachment
Why use a managed policy instead of writing your own?
AWS managed policies are maintained by AWS and automatically updated when services change. Writing a custom policy gives you finer control, but for common patterns like "Lambda reads from DynamoDB Streams," the managed policy is simpler and less error-prone.
Your IAM role is ready and has exactly the permissions Lambda needs. Next up, you'll write the actual Python function that processes order events from the stream.
Write and Deploy the Lambda Function
Your DynamoDB table is streaming changes, and your IAM role is ready to grant permissions. Now you need the actual code that responds to those stream events.
In this step, you'll write a Python Lambda function that processes order events from the stream. It will handle three types of changes: new orders being placed, existing orders being updated, and orders being deleted. Then you'll package and deploy it to AWS.
In this step, get ready to:
- Write a Python handler that processes INSERT, MODIFY, and REMOVE stream events.
- Package the function code into a zip file for deployment.
- Deploy the Lambda function to AWS with the correct runtime and IAM role.
Write the handler code
The Lambda handler is a Python function that AWS invokes automatically whenever new records appear on the DynamoDB stream. It receives an event object containing the stream records and decides what to do based on the type of change.
- In VS Code, create a new file called lambda_function.py inside your OrderProcessor folder.
- Paste the following code into lambda_function.py:
import json
def handler(event, context):
print("Received DynamoDB Stream event:")
print(json.dumps(event, indent=2))
for record in event["Records"]:
event_name = record["eventName"]
if event_name == "INSERT":
new_order = record["dynamodb"]["NewImage"]
order_id = new_order["OrderId"]["S"]
customer_id = new_order["CustomerId"]["S"]
amount = float(new_order["Amount"]["N"])
status = new_order["Status"]["S"]
print(f"New order received!")
print(f" Order ID: {order_id}")
print(f" Customer: {customer_id}")
print(f" Amount: ${amount:.2f}")
print(f" Status: {status}")
if amount > 100:
print(f" HIGH-VALUE ORDER DETECTED!")
elif event_name == "MODIFY":
new_image = record["dynamodb"]["NewImage"]
old_image = record["dynamodb"]["OldImage"]
order_id = new_image["OrderId"]["S"]
old_status = old_image["Status"]["S"]
new_status = new_image["Status"]["S"]
print(f"Order updated: {order_id}")
print(f" Status changed: {old_status} -> {new_status}")
elif event_name == "REMOVE":
old_image = record["dynamodb"]["OldImage"]
order_id = old_image["OrderId"]["S"]
print(f"Order deleted: {order_id}")
return {
"statusCode": 200,
"body": json.dumps(f"Processed {len(event['Records'])} record(s)")
}
What does this code do?
The handler function is the entry point that AWS Lambda calls. It receives an event containing a list of stream records, and a context object with runtime info.
- INSERT events mean a new order was placed. The code extracts order details from the stream record and flags any order over $100 as high-value.
- MODIFY events mean an existing order changed. The code compares old and new status values to show what changed.
- REMOVE events mean an order was deleted. The code logs which order was removed.
- The DynamoDB stream record format uses type descriptors like ["S"] for strings and ["N"] for numbers. That is why the code accesses values like new_order["OrderId"]["S"].
- Save lambda_function.py.
File not saving?
- Make sure the file is named exactly lambda_function.py (with an underscore, not a hyphen). The Lambda handler path depends on this exact filename.
- Confirm the file is inside your OrderProcessor folder, not a subfolder.
- Help me check my lambda_function.py is set up correctly.
✔️ Awesome, I've got everything!
Great. Double check you've saved your file before moving on to packaging.
ⓧ I'd like to double check the full code
Here is the complete lambda_function.py file for comparison:
import json
def handler(event, context):
print("Received DynamoDB Stream event:")
print(json.dumps(event, indent=2))
for record in event["Records"]:
event_name = record["eventName"]
if event_name == "INSERT":
new_order = record["dynamodb"]["NewImage"]
order_id = new_order["OrderId"]["S"]
customer_id = new_order["CustomerId"]["S"]
amount = float(new_order["Amount"]["N"])
status = new_order["Status"]["S"]
print(f"New order received!")
print(f" Order ID: {order_id}")
print(f" Customer: {customer_id}")
print(f" Amount: ${amount:.2f}")
print(f" Status: {status}")
if amount > 100:
print(f" HIGH-VALUE ORDER DETECTED!")
elif event_name == "MODIFY":
new_image = record["dynamodb"]["NewImage"]
old_image = record["dynamodb"]["OldImage"]
order_id = new_image["OrderId"]["S"]
old_status = old_image["Status"]["S"]
new_status = new_image["Status"]["S"]
print(f"Order updated: {order_id}")
print(f" Status changed: {old_status} -> {new_status}")
elif event_name == "REMOVE":
old_image = record["dynamodb"]["OldImage"]
order_id = old_image["OrderId"]["S"]
print(f"Order deleted: {order_id}")
return {
"statusCode": 200,
"body": json.dumps(f"Processed {len(event['Records'])} record(s)")
}
Package the function into a zip file
AWS Lambda expects your code as a zip archive. Since this function only uses the built-in json module, you only need to zip the single Python file.
- In PowerShell, make sure you are in your OrderProcessor folder. Create the zip file by running this command:
Compress-Archive -Path lambda_function.py -DestinationPath function.zip
- Confirm the zip file was created by running:
ls function.zip
You should see function.zip listed with a file size greater than 0.
Compress-Archive not found or zip is empty?
- Make sure you are using PowerShell, not Command Prompt (cmd.exe). Compress-Archive is a native PowerShell cmdlet.
- If function.zip already exists from a previous attempt, delete it first with Remove-Item function.zip and run the command again.
- Verify that lambda_function.py exists in your current directory by running ls lambda_function.py.
- Help me fix my Compress-Archive command.
Deploy the function to AWS Lambda
Now you will upload the zip file to AWS and create the Lambda function. The deploy command tells AWS which runtime to use, which file contains the handler, and which IAM role grants permissions.
- Deploy the Lambda function by running this command:
aws lambda create-function --function-name OrderProcessor --runtime python3.12 --handler lambda_function.handler --zip-file fileb://function.zip --role [[ROLE_ARN="your OrderProcessorRole ARN from Step 3"]]
What do these flags mean?
- --function-name OrderProcessor names your function in AWS.
- --runtime python3.12 tells Lambda which language environment to use.
- --handler lambda_function.handler tells Lambda the file (lambda_function) and function (handler) to invoke.
- --zip-file fileb://function.zip uploads your packaged code. The fileb:// prefix tells the CLI to read binary file content.
- --role assigns the OrderProcessorRole IAM role you created in the previous step, granting stream read and log write permissions.
You should see a JSON response containing your function's configuration. Look for "FunctionName": "OrderProcessor" and "State": "Active" (or "Pending" which becomes Active within seconds).
Seeing an InvalidParameterValueException?
- If you see "The role defined for the function cannot be assumed by Lambda," wait 10 seconds and try again. IAM roles can take a moment to propagate after creation.
- If you see "Role ARN is invalid," double check you pasted the full ARN. It should start with arn:aws:iam:: and end with :role/OrderProcessorRole.
- If you see "Function already exists," you may have run the command before. Run aws lambda delete-function --function-name OrderProcessor and try again.
- Help me debug my Lambda create-function command.
Your Lambda function is deployed and ready to process events. Next, you will wire the DynamoDB stream directly to your function so it triggers automatically whenever an order changes.
Connect the DynamoDB Stream to Lambda
You have a DynamoDB table producing stream events and a Lambda function ready to process them. Right now, these two services have no idea the other exists.
In this step, you will create an event source mapping. This is the connector that tells AWS to poll the DynamoDB stream and invoke your Lambda function whenever new records appear.
In this step, get ready to:
- Create an event source mapping between the DynamoDB stream and your Lambda function.
- Verify the mapping is active and enabled.
- Understand how AWS glues event-driven services together without any custom polling code.
Create the event source mapping
An event source mapping is an AWS resource that connects a stream or queue to a Lambda function. AWS manages the polling, batching, and invocation for you.
- Create the event source mapping by running this command in PowerShell:
aws lambda create-event-source-mapping --function-name OrderProcessor --event-source-arn [[STREAM_ARN="your LatestStreamArn from Step 2"]] --starting-position LATEST --batch-size 10
What does this command do?
--function-name OrderProcessor tells AWS which Lambda function to invoke when events arrive.
--event-source-arn points to the DynamoDB stream you want Lambda to read from. This is the LatestStreamArn you saved in Step 2.
--starting-position LATEST means Lambda only processes new events from this point forward, ignoring any older records already in the stream.
--batch-size 10 means AWS will send up to 10 stream records per Lambda invocation.
- You should see a JSON response containing fields like UUID, FunctionArn, and State set to Creating or Enabled.
Seeing an error about the event source ARN?
Make sure you replaced the placeholder with the actual LatestStreamArn you saved from Step 2. It should look like arn:aws:dynamodb:us-east-1:123456789012:table/Orders/stream/....
If you lost the Stream ARN, retrieve it again by running aws dynamodb describe-table --table-name Orders and looking for the LatestStreamArn field.
Help me fix my event source mapping error
Verify the mapping is enabled
The event source mapping takes a few seconds to become fully active. Let's confirm it's working.
- Check the mapping status by running this command:
aws lambda list-event-source-mappings --function-name OrderProcessor
- In the JSON output, look for the State field. It should say Enabled.
State still shows Creating?
Wait 10-15 seconds and run the command again. The mapping needs a moment to activate. If it stays in Creating for more than a minute, check that your IAM role has the AWSLambdaDynamoDBExecutionRole policy attached.
Help me troubleshoot my event source mapping state
Understand what just happened
What is an event source mapping?
The event source mapping is the "glue" in event-driven architecture. It tells AWS to continuously poll the DynamoDB stream on your behalf. Whenever new records appear, Lambda automatically invokes your function with the stream data.
You did not write any polling code. You did not set up a cron job. You did not provision a server to watch for changes. AWS handles all of that infrastructure. Your function only runs when there is actual work to do.
Why LATEST instead of TRIM_HORIZON?
LATEST means your function only processes events that happen after you created the mapping. The alternative, TRIM_HORIZON, would replay all available records in the stream from the beginning.
Since you have not inserted any orders yet, both options would produce the same result. LATEST is the safer default for production systems where you do not want to reprocess historical data.
Your pipeline is now fully wired. The DynamoDB stream feeds events into Lambda without a single line of custom polling code. Next up, you will insert real orders and watch the entire system fire in real time.
Test the Event-Driven System
In the previous step, you connected the DynamoDB stream to your Lambda function using an event source mapping. The pipeline is fully wired.
Now it is time to see the whole system in action. You will insert, update, and delete orders in DynamoDB and watch your Lambda function process each event in real time through CloudWatch logs.
In this step, get ready to:
- Insert a test order into DynamoDB and trigger the stream.
- Check CloudWatch logs to confirm Lambda processed the event.
- Test updates and deletes to observe all three event types.
Insert a test order
Your event-driven pipeline works like a chain reaction. When you insert an item into the Orders table, DynamoDB writes a stream record, which triggers the event source mapping, which invokes your Lambda function.
- Insert a test order into the Orders table by running this command:
aws dynamodb put-item --table-name Orders --item '{"OrderId": {"S": "ORD-001"}, "CustomerId": {"S": "CUST-123"}, "Amount": {"N": "149.99"}, "Status": {"S": "NEW"}}'
What does this command do?
This inserts a single order with four attributes into your Orders table.
- OrderId is ORD-001 (the partition key).
- Amount is 149.99, which is above $100 so Lambda will flag it as high-value.
- DynamoDB stores numbers as strings internally, which is why 149.99 is wrapped in quotes.
The command produces no output on success. Behind the scenes, DynamoDB has already written a stream record and Lambda is processing it.
Give Lambda a moment
The event source mapping polls the stream on a short interval. Wait about 10-15 seconds before checking the logs to give Lambda time to receive and process the event.
Check CloudWatch logs
Every print() statement in your Lambda function writes to a CloudWatch log group named /aws/lambda/OrderProcessor. You can tail these logs from the command line.
- View your Lambda function's recent log output by running this command:
aws logs tail /aws/lambda/OrderProcessor --since 5m
You should see output that includes lines like:
New order received!
Order ID: ORD-001
Customer: CUST-123
Amount: $149.99
Status: NEW
HIGH-VALUE ORDER DETECTED!
What do these logs tell you?
Your Lambda function received the INSERT event from the DynamoDB stream. It parsed the order attributes and printed them.
Because the amount is $149.99 (over $100), the function flagged it as a high-value order. This is the exact logic from the if amount > 100 check in your code.
Not seeing any logs?
- Wait 15-30 seconds and run the command again. The event source mapping may need a moment to poll and deliver the event.
- Make sure your event source mapping state is Enabled by running aws lambda list-event-source-mappings --function-name OrderProcessor.
- If you see ResourceNotFoundException, the log group has not been created yet. Lambda creates it on the first invocation. Wait a bit longer and retry.
Help me troubleshoot why my Lambda function logs are not appearing in CloudWatch.
Test updates and deletes
Your Lambda function handles three event types: INSERT, MODIFY, and REMOVE. You have already tested INSERT. Now you will trigger the other two.
- Update the order's status to SHIPPED by running this command:
aws dynamodb update-item --table-name Orders --key '{"OrderId": {"S": "ORD-001"}}' --update-expression "SET #s = :new_status" --expression-attribute-names '{"#s": "Status"}' --expression-attribute-values '{":new_status": {"S": "SHIPPED"}}'
What does this command do?
This updates only the Status attribute of order ORD-001 from NEW to SHIPPED.
The #s placeholder is needed because Status is a reserved word in DynamoDB. Expression attribute names let you use reserved words safely.
- Wait about 15 seconds for Lambda to process the MODIFY event.
- Delete the order by running this command:
aws dynamodb delete-item --table-name Orders --key '{"OrderId": {"S": "ORD-001"}}'
- Wait another 15 seconds, then check the logs for all three events by running:
aws logs tail /aws/lambda/OrderProcessor --since 5m
You should now see all three event types processed in your logs. Look for these lines:
Order updated: ORD-001
Status changed: NEW -> SHIPPED
Order deleted: ORD-001
What just happened?
You triggered all three DynamoDB stream event types and watched Lambda process each one automatically.
- INSERT: A new order was created and Lambda logged the details plus the high-value flag.
- MODIFY: The status changed from NEW to SHIPPED and Lambda compared old vs new values (thanks to NEW_AND_OLD_IMAGES stream view type).
- REMOVE: The order was deleted and Lambda logged which order was removed.
No polling code, no cron jobs, no servers. AWS handled all the plumbing between services.
Only seeing some events?
- If you ran the commands too quickly, the events might arrive in a single batch. Check if all three event types appear in one log entry.
- If MODIFY or REMOVE events are missing, increase the --since value to 10m and try again.
- Make sure you ran the commands in order (put-item, then update-item, then delete-item). You cannot update or delete an item that does not exist.
Help me figure out why some of my DynamoDB stream events are not showing up in CloudWatch logs.
Secret mission
Add SNS Email Notifications for High-Value Orders
Your order processing system detects high-value orders, but nobody finds out until they check the logs. In this secret mission, you'll extend the pipeline with SNS notifications so you get an email the moment a big order lands.
Clean Up Your Resources
Clean Up Your Resources
Decide whether to keep your resources running, pause them to come back later, or delete them entirely. All resources in this project fall within the AWS Free Tier for light usage, so there are no ongoing costs unless you exceed Free Tier limits.
Resources you created in this project:
- DynamoDB table: Orders (Free Tier: 25 GB storage, 25 RCU/WCU).
- Lambda function: OrderProcessor (Free Tier: 1M requests/month).
- IAM role: OrderProcessorRole.
- Event source mapping (no cost).
- CloudWatch log group: /aws/lambda/OrderProcessor.
- If you completed the Secret Mission: SNS topic HighValueOrders (Free Tier: 1,000 email notifications/month).
Keep everything running
No action needed. Choose this if you are still actively experimenting with event-driven architecture or want to add more features to your order processor.
- All resources remain within the AWS Free Tier for light usage.
- The event source mapping will continue to poll your DynamoDB stream automatically.
- You will only be charged if you exceed Free Tier limits (1M Lambda invocations/month or 25 GB DynamoDB storage).
Pause - I'll come back to this later
Disable the event source mapping so Lambda stops processing stream events. Your table, function, and role stay in place for when you return.
- Find your event source mapping UUID by running:
aws lambda list-event-source-mappings --function-name OrderProcessor
- Copy the UUID value from the output, then disable the mapping by running:
aws lambda update-event-source-mapping --uuid [[EVENT_SOURCE_UUID="your event source mapping UUID"]] --no-enabled
- When you are ready to resume, run the same command but replace --no-enabled with --enabled.
Delete - I don't want to use this again
Remove all project resources permanently. Run each command in order.
- Delete the Lambda function:
aws lambda delete-function --function-name OrderProcessor
- Delete the DynamoDB table:
aws dynamodb delete-table --table-name Orders
- Detach the managed policy from the IAM role:
aws iam detach-role-policy --role-name OrderProcessorRole --policy-arn arn:aws:iam::aws:policy/service-role/AWSLambdaDynamoDBExecutionRole
- Delete the IAM role:
aws iam delete-role --role-name OrderProcessorRole
- If you completed the Secret Mission, delete the SNS topic:
aws sns delete-topic --topic-arn [[SNS_TOPIC_ARN="your SNS topic ARN from the Secret Mission"]]
- Optionally, delete the CloudWatch log group:
aws logs delete-log-group --log-group-name /aws/lambda/OrderProcessor
That's a Wrap!
That's a Wrap!
Nice work! You just built a fully event-driven order processing system on AWS from scratch.
No frameworks, no magic. Just you wiring cloud services together like an architect.
You've learned how to:
- Build a DynamoDB table with streams that captures every data change in real time and automatically triggers downstream processing.
- Deploy a Python Lambda function that processes INSERT, MODIFY, and REMOVE events from a DynamoDB stream.
- Connect services through an event source mapping, the glue of event-driven architecture that lets AWS poll streams and invoke your function automatically.
- Secret Mission: Added SNS email notifications to extend the pipeline into a multi-service fan-out pattern for high-value orders.
Ready to quiz yourself?