Cloud Operations & Ticketing System
Build an automated incident pipeline with Lambda, CloudWatch, SNS, and DynamoDB.
Introduction
30 Second Summary
When a service you rely on goes down, someone has to notice the failure, document it, and coordinate the fix. In most engineering teams, this entire chain is automated so that tickets appear in the tracking system before anyone opens their laptop.
In this project, you will build an end-to-end automated incident ticketing pipeline on AWS. You will deploy a function, intentionally break it, then wire up monitoring, alerts, and a ticket creator that logs every failure as a structured, queryable record.
What You'll Build
When your order processing service fails, a new incident ticket automatically appears in your database within minutes, complete with a unique ticket ID, alarm name, timestamp, and OPEN status.
By the end of this project, you'll have:
- A working order processor deployed as an AWS Lambda function that you can invoke, intentionally break, and watch fail in real time.
- Automated failure detection powered by CloudWatch Alarms and SNS that catches errors and delivers alert emails to your inbox within minutes.
- A ticket-creator function that converts every alert into a structured incident record in DynamoDB, complete with ticket ID, timestamp, and OPEN status.
- Secret Mission: Add ticket resolution tracking so you can update tickets from OPEN to RESOLVED and query only unresolved incidents.
Are there any prerequisites?
You will need an AWS account, Cursor installed on your computer, and some experience with Python. Step 1 covers installing and configuring the AWS CLI.
Before We Start
Before we dive in, let's lock in what you're about to build and why it matters.
Set Up the AWS CLI
To build your automated incident pipeline, you need a way to talk to AWS services directly from your terminal. The AWS CLI is a command-line tool that lets you create, invoke, and inspect AWS resources without clicking through the console for every action.
In this step, you will install the AWS CLI and connect it to your AWS account. Once configured, every aws command you run will authenticate against your account automatically.
In this step, get ready to:
- Install the AWS CLI on your machine.
- Configure the CLI with your AWS credentials.
- Verify that the CLI can reach your AWS account.
Install the AWS CLI
The AWS CLI is the bridge between your terminal and every AWS service you will use in this project. Let's check if it is already installed.
- Open your terminal by pressing Cmd+Space (macOS) or the Windows key (Windows), typing Terminal (macOS) or PowerShell (Windows), and pressing Enter.
- Check whether the AWS CLI is installed by running this command:
aws --version
✔️ I see a version number
The AWS CLI is already installed. You are good to move on to the next substep.
ⓧ Command not found
The AWS CLI is not installed yet. Follow the instructions for your operating system below.
macOS
- Install the AWS CLI by running this command in your terminal:
curl -fsSL https://awscli.amazonaws.com/v2/install.sh | bash
What does this command do?
This downloads the official AWS CLI install script from Amazon and runs it. It installs the CLI for the current user and adds the aws command to your path.
- Close and reopen your terminal so the new path takes effect.
- Verify the installation by running:
aws --version
You should see version output starting with aws-cli/2. followed by a version number.
Windows
- Install the AWS CLI by running this command in PowerShell:
irm 'https://awscli.amazonaws.com/v2/install.ps1' | iex
What does this command do?
This downloads and runs the official AWS CLI install script for Windows. It installs the CLI for the current user without needing administrator privileges.
- Close and reopen PowerShell so the new path takes effect.
- Verify the installation by running:
aws --version
You should see version output starting with aws-cli/2. followed by a version number.
Still seeing command not found?
Make sure you closed and reopened your terminal after installing. If the issue persists, the installer may not have added aws to your system path. Check the official AWS CLI troubleshooting guide for your operating system.
Need more help? Why is the aws command not found after I installed the AWS CLI?
Create an access key for the CLI
The AWS CLI needs credentials to prove who you are when it talks to AWS. You will create an access key in the IAM console and then feed it to the CLI.
- Open the IAM console in your browser at https://console.aws.amazon.com/iam/.
- In the left navigation pane, click Users.
- Click your IAM user name to open the user details page.
- Select the Security credentials tab.
- In the Access keys section, click Create access key.
- On the Access key best practices & alternatives page, select Command Line Interface (CLI).
- Check the confirmation box at the bottom, then click Next.
- Skip the optional description tag and click Create access key.
- Click Download .csv file to save your Access Key ID and Secret Access Key to your computer.
- Click Done.
Save your credentials now
This is the only time you can view or download your Secret Access Key. If you lose it, you will need to delete the key and create a new one. Keep the downloaded .csv file in a safe place.
Configure the CLI with your credentials
Now that you have your access key, you can connect the AWS CLI to your account. The aws configure command will prompt you for four values.
- Start the configuration wizard by running this command in your terminal:
aws configure
What will this command ask for?
The CLI prompts you for four values, one at a time. Here is what to enter for each:
- AWS Access Key ID: paste the Access Key ID from the .csv file you just downloaded.
- AWS Secret Access Key: paste the Secret Access Key from the same .csv file.
- Default region name: type us-east-1.
- Default output format: type json.
- Enter each value when prompted, pressing Enter after each one.
What is a default region?
The default region tells the CLI which AWS data center to send your commands to. You are using us-east-1 (Northern Virginia), which is the most commonly used region and has the broadest service availability.
Before you run the next command, what do you think will happen if your credentials are correct?
- Verify that the CLI can reach your AWS account by running this command:
aws sts get-caller-identity
You should see a JSON response containing your AWS Account ID, a User ARN, and a User ID. This confirms the CLI is authenticated and connected to your account.
Seeing an error instead of your account details?
- Double-check that you pasted the correct Access Key ID and Secret Access Key from the .csv file. A single extra space or missing character will cause an authentication failure.
- Make sure you entered us-east-1 for the region and json for the output format. You can re-run aws configure to correct any values.
- If you see a permissions error, make sure your IAM user has sufficient permissions attached.
Need more help? Why is aws sts get-caller-identity returning an error for me?
Your terminal is now connected to your AWS account. Next up, you will deploy your first Lambda function and see what happens when it breaks.
Deploy and Break Your First Lambda Function
Your AWS CLI is configured and talking to your account. Now you need a service to monitor.
Before you can build automated monitoring, you need something that actually breaks. In this step, you will deploy a Lambda function that processes orders, watch it succeed, then intentionally introduce a bug. The goal is to feel how invisible failures are when you have no monitoring in place.
In this step, get ready to:
- Deploy a Lambda function that processes orders and confirm it works.
- Break the function with an intentional bug and observe the error.
- Manually search CloudWatch Logs to find the failure.
Create the order-processor Lambda function
Every Lambda function needs a name, a runtime (the language version it runs on), and some code. You will create a function called order-processor that takes an order ID and returns a success message.
- Open the AWS Management Console in your browser and sign in to your AWS account.
- In the search bar at the top, type Lambda and select Lambda from the results.
- Click Create function.
- Select Author from scratch.
- For Function name, enter order-processor.
- For Runtime, select Python 3.13.
- Leave all other settings at their defaults and click Create function.
Lambda creates your function and opens the code editor. You should see a file called lambda_function.py with some default code.
- Select the Code tab if it is not already selected.
- In the code editor, select all the default code in lambda_function.py and replace it with the following order processing code:
import json
def lambda_handler(event, context):
# Extract the order ID from the incoming event
order_id = event.get("order_id")
# Validate that an order ID was provided
if not order_id:
raise ValueError("Missing required field: order_id")
# Build a success response with the order details
result = {
"status": "processed",
"order_id": order_id,
"message": f"Order {order_id} processed successfully",
}
return {"statusCode": 200, "body": json.dumps(result)}
What does this code do?
This function simulates an order processing service. It reads an order_id from the incoming event, checks that it exists, then returns a success response with a 200 status code.
The lambda_handler function is the entry point that AWS Lambda calls every time your function is invoked. The event parameter contains whatever data the caller sends in.
- In the DEPLOY section of the code editor, click Deploy to save and deploy your code.
You should see a confirmation banner that says your function was updated.
Now test it. You need to create a test event that sends a sample order ID to your function.
- In the TEST EVENTS section of the code editor, click Create test event.
- For Event name, enter testOrder.
- Replace the default event JSON with the following:
{
"order_id": "ORD-001"
}
- Click Save.
- Click the run icon next to your testOrder test event to invoke your function.
You should see a successful response with a statusCode of 200 and a message saying Order ORD-001 processed successfully.
Not seeing a 200 response?
- Make sure you clicked Deploy before running the test. Unsaved code will not execute.
- Check that your test event JSON matches the format above exactly. The key must be order_id with an underscore.
- Verify that you selected Python 3.13 as the runtime when you created the function.
Still stuck? Help me debug my order-processor Lambda function that is not returning a 200 response.
Break the function with an intentional bug
Your function works. In production, services break all the time. A bad deployment, a logic error, an unexpected input. You are going to simulate that by adding a bug that causes the function to crash for any order ID starting with "ORD".
- In the code editor, find this line in your lambda_function.py:
if not order_id:
raise ValueError("Missing required field: order_id")
- Directly below those two lines, add the following bug:
# BUG: reject any order starting with 'ORD'
if order_id.startswith("ORD"):
raise Exception(f"Processing failed for order: {order_id}")
What does this bug do?
This code raises an Exception whenever the order_id starts with "ORD". Since your test event uses ORD-001, the function will now crash every time you invoke it. This simulates a real-world bug that slips into production.
- Click Deploy to save your changes.
Before you invoke it, take a moment to predict: what do you think will happen when you run the same testOrder event now?
- Click the run icon next to your testOrder event to invoke the broken function.
The function returns an error. You should see an Exception with the message Processing failed for order: ORD-001.
✔️ Awesome, I've got everything!
Great. Double check you see the error response in your test results before moving on.
ⓧ I'd like to double check the full code
Your lambda_function.py should now look exactly like this:
import json
def lambda_handler(event, context):
order_id = event.get("order_id")
if not order_id:
raise ValueError("Missing required field: order_id")
if order_id.startswith("ORD"):
raise Exception(f"Processing failed for order: {order_id}")
result = {
"status": "processed",
"order_id": order_id,
"message": f"Order {order_id} processed successfully",
}
return {"statusCode": 200, "body": json.dumps(result)}
Hunt for the error in CloudWatch Logs
Your function is broken and throwing errors. But imagine you did not just watch it fail. Imagine this function runs in the background across dozens of services. How would you even know something went wrong?
Right now, the only way to find out is to manually dig through CloudWatch Logs. Try it.
- In the Lambda console, click the Monitor tab for your order-processor function.
- Click View CloudWatch logs to open the CloudWatch console.
This opens the log group for your function: /aws/lambda/order-processor. You should see one or more log streams listed.
- Click the most recent log stream (the one at the top of the list).
- Scroll through the log events and look for the error message. You should find a line containing Processing failed for order: ORD-001.
You found it. But think about what it took to get here.
Why is this painful?
To find one error, you had to open the Lambda console, navigate to the Monitor tab, click through to CloudWatch, pick the right log group, guess which log stream to open, and scroll through entries to find the failure.
Now imagine doing this across dozens of functions at 3 AM. You would need to check each function individually, hoping you find the right log stream. Failures are completely invisible until someone goes looking for them.
You have a broken function and the only way to catch it is to manually hunt through logs. In the next step, you will set up CloudWatch Alarms and SNS to detect failures automatically and send you an alert the moment something goes wrong.
Set Up Automated Failure Detection with CloudWatch and SNS
In the last step, you had to manually dig through CloudWatch Logs to find a single error. You opened the log group, scrolled through log streams, and searched for the failure. For one function, that took a few minutes.
Now imagine doing that across dozens of functions at 3 AM. You need AWS to watch for errors automatically and tell you the moment something breaks. That is exactly what CloudWatch Alarms and SNS do.
In this step, get ready to:
- Create an SNS topic and subscribe your email to receive alerts.
- Create a CloudWatch Alarm that watches your Lambda function for errors.
- Trigger the alarm and receive an alert email.
Create an SNS topic and subscribe your email
SNS stands for Simple Notification Service. It is a pub/sub messaging service that broadcasts messages to multiple subscribers at once. You will create a topic (a named channel) and subscribe your email to it so AWS can send you alerts.
- In the AWS console search bar at the top, type SNS and select Simple Notification Service.
- In the left navigation pane, choose Topics.
- Click Create topic.
- For Type, choose Standard.
- For Name, enter incident-alerts.
- Scroll to the bottom and click Create topic.
You should see the topic detail page for incident-alerts with an ARN displayed at the top.
What is a topic?
Think of an SNS topic as a named broadcast channel. Any AWS service can publish a message to the topic, and every subscriber (your email, a Lambda function, an SQS queue) receives that message. You are building the channel first, then plugging in subscribers.
Now add your email as a subscriber so you receive messages published to this topic.
- On the incident-alerts topic detail page, click Create subscription.
- For Protocol, choose Email.
- For Endpoint, enter your email address.
- Click Create subscription.
The subscription status shows PendingConfirmation. AWS just sent a confirmation email to the address you entered.
- Open your email inbox and find the email from AWS Notifications.
- Click Confirm subscription in the email.
- Return to the SNS console. Refresh the page and confirm the subscription Status now shows Confirmed.
Don't see the confirmation email?
- Check your spam or junk folder. The email comes from no-reply@sns.amazonaws.com.
- Make sure you entered your email address correctly. If not, delete the subscription and create a new one with the correct address.
- The confirmation link expires after 2 days. If it has expired, delete the subscription and create it again.
Still stuck? Why isn't my SNS email subscription confirming?
Create a CloudWatch Alarm for Lambda errors
Lambda automatically sends metrics to CloudWatch every time your function runs. One of those metrics is Errors, which counts invocations that ended in failure. You will create an alarm that watches this metric and fires when it detects one or more errors.
- In the AWS console search bar, type CloudWatch and select CloudWatch.
- In the left navigation pane, choose Alarms, then All alarms.
- Click Create alarm.
- Click Select metric.
You need to find the Errors metric for your order-processor function.
- In the search box, type order-processor and press Enter.
- Select Lambda, then By Function Name.
- Find the row where MetricName is Errors and FunctionName is order-processor. Check the box next to it.
- Click the Graphed metrics tab.
- Under Statistic, select Sum.
- Under Period, select 1 minute.
- Click Select metric.
What do Sum and 1 minute mean?
Sum tells CloudWatch to add up every error that happened during the evaluation window. A period of 1 minute means CloudWatch checks for errors every 60 seconds. Together, they mean: "If the total number of errors in any 1-minute window is above my threshold, fire the alarm."
Now set the threshold and notification.
- On the Specify metric and conditions page, scroll to Conditions.
- For the threshold type, keep Static selected.
- Set it to Greater/Equal (>=).
- For the threshold value, enter 1.
- Click Next.
- Under Notification, keep In alarm selected as the alarm state trigger.
- For Select an SNS topic, choose Select an existing SNS topic.
- Select the incident-alerts topic you created earlier.
- Click Next.
- For Alarm name, enter order-processor-errors.
- Click Next.
- On the Preview and create page, review your settings and click Create alarm.
You should see your new order-processor-errors alarm listed with a state of Insufficient data. This is normal. The alarm has not evaluated any data yet.
Why does the alarm show Insufficient data?
CloudWatch Alarms have three states: OK (metric is below the threshold), ALARM (metric breached the threshold), and INSUFFICIENT_DATA (not enough data to evaluate yet). Since the alarm was just created, it has not seen a full evaluation period.
Don't see the Errors metric for order-processor?
- CloudWatch only shows metrics that have been reported. If your function has never been invoked (or the invocations were too long ago), the Errors metric may not appear. Go back to the Lambda console, invoke order-processor with the test event, then return to CloudWatch and search again.
- Make sure you are in the same region (us-east-1) where you created the Lambda function.
Still stuck? Why can't I find the Lambda Errors metric in CloudWatch?
Trigger the alarm and check your email
Everything is wired up. Time to test the full detection pipeline. You will invoke the broken order-processor function again to generate an error, wait for CloudWatch to detect it, and see the alert land in your inbox.
- In the AWS console search bar, type Lambda and select Lambda.
- Click on the order-processor function.
- Click Test to invoke it with the same test event from earlier.
The function fails as expected with the error about ORD-001.
Before you check your email, think about this: what do you expect the alarm notification to look like? Will it be a clean, structured ticket you can act on, or something else?
- Wait 2-5 minutes for the CloudWatch Alarm to evaluate the 1-minute period and transition to ALARM state.
- Check your email inbox for a notification from AWS.
Why the wait?
CloudWatch evaluates metrics on a 1-minute period. After the function errors, CloudWatch needs to aggregate the data point, compare it to your threshold, and then transition the alarm state. The SNS notification is sent once the alarm reaches the ALARM state. This typically takes 1-5 minutes.
Open the alarm notification email. Read through it carefully.
The email contains useful data: the alarm name, the state change, the trigger reason, and the metric details. But it is a wall of raw JSON. There is no structured record. There is no ticket ID. There is no way to track whether this incident has been resolved.
What is the gap?
You have automated detection. CloudWatch watches for errors and SNS delivers the alert in seconds. That is a huge improvement over digging through logs manually.
But email alerts alone are not incident management. If three alarms fire tonight, you have three unstructured emails. There is no way to query, sort, or mark them as resolved. Real operations teams need structured tickets stored in a database.
You have automated failure detection. CloudWatch watches your function around the clock and SNS pings your inbox the moment something breaks. In the next step, you will close the gap by building a Lambda function that converts these raw alerts into structured incident tickets stored in DynamoDB.
Build an Automated Ticket Creator with Lambda and DynamoDB
In the previous step, you wired up CloudWatch and SNS to detect failures and send email alerts the moment your function breaks. That is a huge improvement over manually digging through logs.
But those email alerts are walls of raw JSON. There is no ticket ID, no structured record, and no way to track whether someone has resolved the incident. In this step, you will create a DynamoDB table to store incident tickets and a second Lambda function that automatically converts each alarm notification into a queryable incident record.
In this step, get ready to:
- Create a DynamoDB table to store structured incident tickets.
- Deploy a ticket-creator Lambda function with permissions to write to DynamoDB.
- Connect the full pipeline and verify that a failure automatically produces a ticket.
Create the IncidentTickets DynamoDB table
Your ticket-creator function needs somewhere to store the incidents it creates. DynamoDB is a serverless key-value database that scales automatically and stays within the AWS Always Free tier for this project.
- In the AWS console search bar, type DynamoDB and select it from the results.
- In the left navigation pane, click Tables.
- Click Create table.
- For Table name, enter IncidentTickets.
- For Partition key, enter ticket_id and leave the type set to String.
- For Table settings, keep the default selection of Default settings.
- Click Create table.
Wait a few seconds for the table status to change from Creating to Active. You should see IncidentTickets listed with an Active status in the Tables list.
What is a partition key?
The partition key is the primary way DynamoDB organizes and retrieves items. Every item in the table must have a unique partition key value.
You chose ticket_id because each incident ticket gets its own unique ID like INC-A3F9B201. This means you can look up any ticket directly by its ID.
Table stuck in Creating status?
DynamoDB tables usually become active within a few seconds. If yours stays in Creating for more than a minute, try refreshing the page.
help me debug my DynamoDB table creation
Create the ticket-creator Lambda function and attach permissions
This function will receive alarm notifications from SNS and write structured tickets to your DynamoDB table. Before it can write to DynamoDB, its execution role needs explicit permission.
- In the AWS console search bar, type Lambda and select it from the results.
- Click Create function.
- Select Author from scratch.
- For Function name, enter ticket-creator.
- For Runtime, select Python 3.13.
- Leave all other settings at their defaults and click Create function.
You should see a success banner confirming your function was created.
Now you need to give this function permission to write to your DynamoDB table. You will add an inline IAM policy to the function's execution role.
- On the ticket-creator function page, click the Configuration tab.
- Click Permissions in the left sidebar.
- Under Execution role, click the role name link to open it in the IAM console.
- In the Permissions policies pane, click Add permissions and select Create inline policy.
- In the Policy editor, select JSON.
- Replace the existing JSON with the following policy:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "dynamodb:PutItem",
"Resource": "arn:aws:dynamodb:*:*:table/IncidentTickets"
}
]
}
What does this policy do?
This IAM policy grants exactly one permission: the ability to call dynamodb:PutItem on the IncidentTickets table. The function can write new items but cannot read, update, or delete anything.
The *:* in the resource ARN matches any region and account, which keeps the policy portable for a learning project.
- Click Next.
- For Policy name, enter DynamoDB-PutItem-IncidentTickets.
- Click Create policy.
You should see the new policy listed under Permissions policies for the role.
Now switch back to the Lambda console to add the function code. You can click the browser back button or search for Lambda in the AWS console search bar and open the ticket-creator function.
- In the code editor, select all the existing code and replace it with the following imports and setup. This connects your function to the IncidentTickets table:
import json
import uuid
from datetime import datetime, timezone
import boto3
# Connect to the IncidentTickets DynamoDB table
dynamodb = boto3.resource("dynamodb")
table = dynamodb.Table("IncidentTickets")
What does this setup code do?
The boto3 library is the AWS SDK for Python. It comes pre-installed in the Lambda runtime, so you do not need to add any dependencies.
The dynamodb.Table("IncidentTickets") line creates a reference to your table. The uuid and datetime imports will generate unique ticket IDs and timestamps.
- Directly below the setup code, add the handler function. This parses the SNS alarm notification:
def lambda_handler(event, context):
# Process each SNS record in the event
for record in event["Records"]:
# Parse the alarm notification from the SNS message
sns_message = json.loads(record["Sns"]["Message"])
# Extract alarm details
alarm_name = sns_message.get("AlarmName", "Unknown")
new_state = sns_message.get("NewStateValue", "Unknown")
reason = sns_message.get("NewStateReason", "No reason provided")
timestamp = sns_message.get("StateChangeTime", datetime.now(timezone.utc).isoformat())
region = sns_message.get("Region", "Unknown")
# Extract the metric that triggered the alarm
trigger = sns_message.get("Trigger", {})
metric_name = trigger.get("MetricName", "Unknown")
namespace = trigger.get("Namespace", "Unknown")
What does this parsing code do?
When SNS triggers a Lambda function, it sends an event containing one or more records. Each record has a Sns.Message field with the CloudWatch alarm notification as a JSON string.
The code parses that JSON and pulls out key fields: the alarm name, the new state (ALARM or OK), the reason it fired, when it happened, and which metric triggered it. The .get() method provides safe defaults if any field is missing.
- Still inside the for loop, add the ticket creation and return logic below the parsing code:
# Generate a unique ticket ID
ticket_id = f"INC-{uuid.uuid4().hex[:8].upper()}"
# Build the structured incident ticket
ticket = {
"ticket_id": ticket_id,
"alarm_name": alarm_name,
"state": new_state,
"reason": reason,
"timestamp": timestamp,
"region": region,
"metric_name": metric_name,
"namespace": namespace,
"status": "OPEN",
"created_at": datetime.now(timezone.utc).isoformat(),
}
# Write the ticket to DynamoDB
table.put_item(Item=ticket)
print(f"Created incident ticket: {ticket_id}")
return {"statusCode": 200, "body": json.dumps({"message": "Tickets created successfully"})}
What does this ticket creation code do?
Each ticket gets a unique ID like INC-A3F9B201 generated from a random UUID. The ticket dictionary bundles all the alarm details into a structured record with a status of OPEN.
The table.put_item() call writes the ticket to DynamoDB. The print() statement logs the ticket ID to CloudWatch Logs so you can verify the function ran.
- Click Deploy to save and deploy your function.
You should see a Changes deployed banner confirming the update.
✔️ Awesome, I've got everything!
Great. Make sure you have saved and deployed your function before moving on.
ⓧ I'd like to double check the full code
Scroll up through the three code chunks you pasted. Confirm they appear in this order, with no extra lines between them:
- Chunk 1: the imports (json, uuid, datetime, boto3) and the dynamodb / table setup.
- Chunk 2: the def lambda_handler(event, context): function with the SNS parsing logic.
- Chunk 3: the ticket creation, table.put_item() call, print statement, and return statement.
Make sure the indentation is correct. Chunks 2 and 3 should both be inside def lambda_handler, and the ticket creation code should be inside the for loop (indented with 8 spaces).
Seeing a deployment error?
Check that the indentation is consistent. Python is sensitive to whitespace. The for loop should be indented 4 spaces inside the function, and all the code inside the loop should be indented 8 spaces.
Also verify there is no leftover default code from when the function was created. The editor should contain only the three chunks you pasted.
help me fix my ticket-creator Lambda code
Connect the pipeline and verify end to end
The last piece is connecting your ticket-creator function to the incident-alerts SNS topic so it runs automatically when an alarm fires.
- On the ticket-creator function page, scroll to the Function overview section at the top.
- Click Add trigger.
- In the trigger configuration dropdown, select SNS.
- In the SNS topic field, select incident-alerts.
- Click Add.
You should see SNS: incident-alerts appear as a trigger in the function overview diagram.
What does adding this trigger do?
When the incident-alerts SNS topic receives a message from CloudWatch, it now does two things: sends you an email AND invokes your ticket-creator Lambda function. This is the pub/sub pattern in action. One event, multiple subscribers.
Now it is time to test the full pipeline end to end. You will invoke the broken order-processor function one more time and watch the entire chain fire.
- In the AWS console search bar, type Lambda and open the order-processor function.
- Click the Test tab.
- Select the existing test event (it should still contain {"order_id": "ORD-001"}) and click Test.
The function fails as expected. Now the CloudWatch Alarm needs to evaluate the error, transition to ALARM state, and publish to SNS.
How long will this take?
The CloudWatch Alarm evaluates on a 1-minute period. After you invoke the function, it may take 1 to 5 minutes for the alarm to detect the error, change to ALARM state, and trigger the SNS notification.
You should receive another alarm email while you wait. Once you see the email, your ticket should be ready in DynamoDB.
Before you check, think about what you expect to find in the IncidentTickets table. What fields should the ticket contain? What should the status value be?
- After you receive the alarm email (or after waiting a few minutes), open the DynamoDB console by searching for DynamoDB in the AWS console search bar.
- Click Tables in the left sidebar and select IncidentTickets.
- Click Explore table items.
- Click Run to scan the table.
You should see a new item in the table with a ticket ID like INC-A3F9B201, the alarm name order-processor-errors, the metric name, a timestamp, and a status of OPEN. That is your first automatically generated incident ticket.
No items in the table?
If the alarm was already in ALARM state from the previous step, it will not fire again until it returns to OK first. Wait 2 to 3 minutes without invoking the function, then check the CloudWatch Alarm console to confirm it has returned to OK. Once it shows OK, invoke the broken order-processor again and wait for the alarm to transition back to ALARM.
Also check the ticket-creator function's CloudWatch Logs (log group /aws/lambda/ticket-creator) for any errors. A common issue is a missing IAM permission. Verify the inline policy is attached to the correct execution role.
help me debug why my DynamoDB table has no incident tickets
What just happened?
You just built a fully automated incident pipeline. Your broken order-processor threw an error. CloudWatch detected the error within a minute. The alarm fired and published to SNS. SNS delivered an email to you AND invoked your ticket-creator function. That function parsed the alarm data and wrote a structured ticket to DynamoDB.
No human clicked anything. The entire chain ran automatically. This is the same event-driven architecture pattern that enterprise teams use to build monitoring and incident management systems at scale.
Secret mission
Add Ticket Resolution Tracking
Your pipeline catches failures and creates tickets, but every ticket stays OPEN forever. Real incident management tracks the full lifecycle from OPEN through RESOLVED. In this mission, you will add severity classification to your ticket-creator Lambda, update ticket statuses using the AWS CLI, and query only unresolved incidents.
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 Always Free tier, so there are no ongoing costs.
Resources you used:
- 2 Lambda functions: order-processor and ticket-creator.
- 1 DynamoDB table: IncidentTickets.
- 1 SNS topic: incident-alerts (with email subscription).
- 1 CloudWatch Alarm: order-processor-errors.
Keep everything running
No action needed. All resources stay within the AWS Always Free tier, so there are no ongoing costs. The CloudWatch Alarm continues to monitor the order-processor function.
Pause - I'll come back to this later
Delete the CloudWatch Alarm to stop active monitoring. Your Lambda functions and DynamoDB table remain but incur no cost when idle.
- Open the CloudWatch console.
- In the left navigation pane, select Alarms, then All alarms.
- Select the checkbox next to order-processor-errors, then choose Actions, Delete.
- Choose Delete to confirm.
Delete - I don't want to use this again
Remove all project resources to leave your AWS account clean.
- Open the CloudWatch console.
- In the left navigation pane, select Alarms.
- Select the checkbox next to order-processor-errors, choose Actions, then Delete. Choose Delete to confirm.
- Open the Amazon SNS console.
- In the left navigation pane, select Topics.
- Select the incident-alerts topic and choose Delete.
- In the Delete topic dialog box, enter delete me and choose Delete. This also removes the email subscription.
- Open the Lambda console and select Functions in the left navigation pane.
- Select the order-processor function, choose Actions, then Delete.
- Type confirm in the text input field and choose Delete.
- Repeat the previous two steps for the ticket-creator function.
- Open the DynamoDB console.
- In the left navigation pane, select Tables.
- Select the IncidentTickets table and choose Delete.
- Enter confirm in the confirmation dialog and choose Delete.
Nice Work!
Nice Work!
Nice work! You just built a fully automated incident ticketing pipeline on AWS. Every failure your service produces is now automatically detected, broadcast, and recorded as a structured, queryable ticket.
You've learned how to:
- Deployed a Lambda function, intentionally broke it, and discovered how invisible failures are without monitoring by digging through CloudWatch Logs manually.
- Built automated failure detection using a CloudWatch Alarm and SNS email notifications that alert you the moment something goes wrong.
- Created an event-driven ticket-creator Lambda that converts alarm notifications into structured incident records in DynamoDB, completing the full monitoring-to-ticket pipeline.
- Secret Mission: Added ticket resolution tracking with severity classification, status updates from OPEN to RESOLVED, and filtered queries to manage incident lifecycles.
Ready to quiz yourself?