From 463c6aa9ebc32f9e90591798d846ffe6e256491d Mon Sep 17 00:00:00 2001 From: Michael Wunderlich Date: Tue, 14 Apr 2026 16:35:00 +0000 Subject: [PATCH 1/3] Add compute tutorials (batch 2) --- tuts/084-lambda-sqs/README.md | 72 ++++++++ tuts/084-lambda-sqs/lambda-sqs.md | 126 ++++++++++++++ tuts/084-lambda-sqs/lambda-sqs.sh | 156 ++++++++++++++++++ tuts/089-lambda-sns/README.md | 71 ++++++++ tuts/089-lambda-sns/lambda-sns.md | 121 ++++++++++++++ tuts/089-lambda-sns/lambda-sns.sh | 136 +++++++++++++++ tuts/097-amazon-ec2-autoscaling-gs/README.md | 61 +++++++ .../amazon-ec2-autoscaling-gs.md | 110 ++++++++++++ .../amazon-ec2-autoscaling-gs.sh | 102 ++++++++++++ tuts/151-lambda-layers/lambda-layers.sh | 34 ++++ tuts/155-ec2-snapshots/ec2-snapshots.sh | 29 ++++ 11 files changed, 1018 insertions(+) create mode 100644 tuts/084-lambda-sqs/README.md create mode 100644 tuts/084-lambda-sqs/lambda-sqs.md create mode 100644 tuts/084-lambda-sqs/lambda-sqs.sh create mode 100644 tuts/089-lambda-sns/README.md create mode 100644 tuts/089-lambda-sns/lambda-sns.md create mode 100644 tuts/089-lambda-sns/lambda-sns.sh create mode 100644 tuts/097-amazon-ec2-autoscaling-gs/README.md create mode 100644 tuts/097-amazon-ec2-autoscaling-gs/amazon-ec2-autoscaling-gs.md create mode 100644 tuts/097-amazon-ec2-autoscaling-gs/amazon-ec2-autoscaling-gs.sh create mode 100644 tuts/151-lambda-layers/lambda-layers.sh create mode 100644 tuts/155-ec2-snapshots/ec2-snapshots.sh diff --git a/tuts/084-lambda-sqs/README.md b/tuts/084-lambda-sqs/README.md new file mode 100644 index 00000000..8e0b7a79 --- /dev/null +++ b/tuts/084-lambda-sqs/README.md @@ -0,0 +1,72 @@ +# Lambda: Process SQS messages + +Create a Lambda function that processes messages from an Amazon SQS queue, with event source mapping for automatic invocation. + +## Source + +https://docs.aws.amazon.com/lambda/latest/dg/with-sqs-example.html + +## Use case + +- ID: lambda/sqs-trigger +- Phase: create +- Complexity: beginner +- Core actions: lambda:CreateFunction, lambda:CreateEventSourceMapping, sqs:SendMessage + +## What it does + +1. Creates an IAM execution role with SQS permissions +2. Creates a Node.js Lambda function that logs message bodies +3. Tests the function with a sample SQS event +4. Creates an SQS queue +5. Creates an event source mapping (queue → function) +6. Sends test messages to the queue +7. Verifies processing via CloudWatch Logs +8. Cleans up all resources + +## Running + +```bash +bash lambda-sqs.sh +``` + +To auto-run with cleanup: + +```bash +echo 'y' | bash lambda-sqs.sh +``` + +## Resources created + +- IAM role (with AWSLambdaSQSQueueExecutionRole policy) +- Lambda function (Node.js 22) +- SQS queue +- Event source mapping +- CloudWatch log group (created automatically by Lambda) + +## Estimated time + +- Run: ~45 seconds +- Cleanup: ~10 seconds + +## Cost + +Free tier eligible. No charges expected for a few messages. + +## Related docs + +- [Using Lambda with Amazon SQS](https://docs.aws.amazon.com/lambda/latest/dg/with-sqs.html) +- [Tutorial: Using Lambda with Amazon SQS](https://docs.aws.amazon.com/lambda/latest/dg/with-sqs-example.html) +- [Amazon SQS visibility timeout](https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/sqs-visibility-timeout.html) + +--- + +## Appendix: Generation details + +| Field | Value | +|-------|-------| +| Generation date | 2026-04-14 | +| Source script | Rewritten from internal 084-lambda-sqs-2-cli-script-v2.sh | +| Script test result | EXIT 0, 44s, 7 steps, clean teardown | +| Issues encountered | Original used `set -e` with `log_command` wrapper (replaced with ERR trap); missing `fileb://` for payload (fixed); original used nodejs22.x (kept) | +| Iterations | v1 (internal), v2 (wait-for-active + region fix), v3 (clean rewrite for publish) | diff --git a/tuts/084-lambda-sqs/lambda-sqs.md b/tuts/084-lambda-sqs/lambda-sqs.md new file mode 100644 index 00000000..b5a3928b --- /dev/null +++ b/tuts/084-lambda-sqs/lambda-sqs.md @@ -0,0 +1,126 @@ +# Using Lambda with Amazon SQS + +This tutorial shows you how to create a Lambda function that processes messages from an Amazon SQS queue. You create the function, test it with a sample event, connect it to an SQS queue, and verify end-to-end message processing. + +## Prerequisites + +- AWS CLI configured with credentials and a default region +- Permissions to create Lambda functions, IAM roles, and SQS queues + +## Step 1: Create an execution role + +Create an IAM role with the `AWSLambdaSQSQueueExecutionRole` managed policy, which grants permissions to read from SQS and write logs. + +```bash +aws iam create-role --role-name lambda-sqs-role \ + --assume-role-policy-document '{ + "Version":"2012-10-17", + "Statement":[{"Effect":"Allow","Principal":{"Service":"lambda.amazonaws.com"},"Action":"sts:AssumeRole"}] + }' + +aws iam attach-role-policy --role-name lambda-sqs-role \ + --policy-arn arn:aws:iam::aws:policy/service-role/AWSLambdaSQSQueueExecutionRole +``` + +Wait about 10 seconds for the role to propagate. + +## Step 2: Create the Lambda function + +Create a Node.js function that logs each SQS message body. + +```javascript +// index.mjs +export const handler = async (event) => { + for (const message of event.Records) { + console.log(`Processed message: ${message.body}`); + } + return { statusCode: 200 }; +}; +``` + +Package and deploy: + +```bash +zip function.zip index.mjs + +aws lambda create-function --function-name sqs-processor \ + --zip-file fileb://function.zip \ + --handler index.handler --runtime nodejs22.x \ + --role arn:aws:iam:::role/lambda-sqs-role \ + --architectures x86_64 +``` + +Wait for the function to become active: + +```bash +aws lambda wait function-active-v2 --function-name sqs-processor +``` + +## Step 3: Test with a sample event + +Invoke the function with a sample SQS event to verify it works: + +```bash +aws lambda invoke --function-name sqs-processor \ + --payload fileb://test-event.json \ + --cli-binary-format raw-in-base64-out response.json +``` + +## Step 4: Create an SQS queue + +```bash +aws sqs create-queue --queue-name lambda-test-queue +QUEUE_URL=$(aws sqs get-queue-url --queue-name lambda-test-queue --query 'QueueUrl' --output text) +QUEUE_ARN=$(aws sqs get-queue-attributes --queue-url $QUEUE_URL \ + --attribute-names QueueArn --query 'Attributes.QueueArn' --output text) +``` + +## Step 5: Create an event source mapping + +Connect the SQS queue to the Lambda function: + +```bash +aws lambda create-event-source-mapping \ + --function-name sqs-processor \ + --batch-size 10 \ + --event-source-arn $QUEUE_ARN +``` + +## Step 6: Send test messages + +```bash +aws sqs send-message --queue-url $QUEUE_URL --message-body "Hello from the Lambda-SQS tutorial" +aws sqs send-message --queue-url $QUEUE_URL --message-body "This is message number 2" +``` + +## Step 7: Verify in CloudWatch Logs + +After about 15 seconds, check the function's log output: + +```bash +aws logs describe-log-streams --log-group-name /aws/lambda/sqs-processor \ + --order-by LastEventTime --descending --limit 1 + +aws logs get-log-events --log-group-name /aws/lambda/sqs-processor \ + --log-stream-name \ + --query 'events[].message' --output text +``` + +You should see `Processed message: Hello from the Lambda-SQS tutorial` in the output. + +## Cleanup + +```bash +aws lambda delete-event-source-mapping --uuid +aws lambda delete-function --function-name sqs-processor +aws sqs delete-queue --queue-url $QUEUE_URL +aws iam detach-role-policy --role-name lambda-sqs-role \ + --policy-arn arn:aws:iam::aws:policy/service-role/AWSLambdaSQSQueueExecutionRole +aws iam delete-role --role-name lambda-sqs-role +``` + +The script automates all steps including cleanup. Run it with: + +```bash +bash lambda-sqs.sh +``` diff --git a/tuts/084-lambda-sqs/lambda-sqs.sh b/tuts/084-lambda-sqs/lambda-sqs.sh new file mode 100644 index 00000000..e3eb1ff3 --- /dev/null +++ b/tuts/084-lambda-sqs/lambda-sqs.sh @@ -0,0 +1,156 @@ +#!/bin/bash +# Tutorial: Using Lambda with Amazon SQS +# Source: https://docs.aws.amazon.com/lambda/latest/dg/with-sqs-example.html + +WORK_DIR=$(mktemp -d) +LOG_FILE="$WORK_DIR/lambda-sqs-$(date +%Y%m%d-%H%M%S).log" +exec > >(tee -a "$LOG_FILE") 2>&1 + +REGION=${AWS_DEFAULT_REGION:-${AWS_REGION:-$(aws configure get region 2>/dev/null)}} +if [ -z "$REGION" ]; then + echo "ERROR: No AWS region configured. Set one with: export AWS_DEFAULT_REGION=us-east-1" + exit 1 +fi +export AWS_DEFAULT_REGION="$REGION" +echo "Region: $REGION" + +RANDOM_ID=$(openssl rand -hex 4) +ROLE_NAME="lambda-sqs-role-${RANDOM_ID}" +FUNCTION_NAME="sqs-processor-${RANDOM_ID}" +QUEUE_NAME="lambda-tut-queue-${RANDOM_ID}" + +handle_error() { echo "ERROR on line $1"; trap - ERR; cleanup; exit 1; } +trap 'handle_error $LINENO' ERR + +cleanup() { + echo "" + echo "Cleaning up resources..." + [ -n "$EVENT_SOURCE_UUID" ] && \ + aws lambda delete-event-source-mapping --uuid "$EVENT_SOURCE_UUID" 2>/dev/null && echo " Deleted event source mapping" + aws lambda delete-function --function-name "$FUNCTION_NAME" 2>/dev/null && echo " Deleted function $FUNCTION_NAME" + aws iam detach-role-policy --role-name "$ROLE_NAME" \ + --policy-arn arn:aws:iam::aws:policy/service-role/AWSLambdaSQSQueueExecutionRole 2>/dev/null + aws iam delete-role --role-name "$ROLE_NAME" 2>/dev/null && echo " Deleted role $ROLE_NAME" + [ -n "$QUEUE_URL" ] && aws sqs delete-queue --queue-url "$QUEUE_URL" 2>/dev/null && echo " Deleted queue $QUEUE_NAME" + aws logs delete-log-group --log-group-name "/aws/lambda/$FUNCTION_NAME" 2>/dev/null && echo " Deleted log group" + rm -rf "$WORK_DIR" + echo "Cleanup complete." +} + +# Step 1: Create IAM role +echo "Step 1: Creating IAM role: $ROLE_NAME" +ROLE_ARN=$(aws iam create-role --role-name "$ROLE_NAME" \ + --assume-role-policy-document '{ + "Version":"2012-10-17", + "Statement":[{"Effect":"Allow","Principal":{"Service":"lambda.amazonaws.com"},"Action":"sts:AssumeRole"}] + }' --query 'Role.Arn' --output text) +aws iam attach-role-policy --role-name "$ROLE_NAME" \ + --policy-arn arn:aws:iam::aws:policy/service-role/AWSLambdaSQSQueueExecutionRole +echo " Role ARN: $ROLE_ARN" +echo " Waiting for role propagation..." +sleep 10 + +# Step 2: Create Lambda function +echo "Step 2: Creating Lambda function: $FUNCTION_NAME" +cat > "$WORK_DIR/index.mjs" << 'EOF' +export const handler = async (event) => { + for (const message of event.Records) { + console.log(`Processed message: ${message.body}`); + } + return { statusCode: 200 }; +}; +EOF +(cd "$WORK_DIR" && zip function.zip index.mjs > /dev/null) + +aws lambda create-function --function-name "$FUNCTION_NAME" \ + --zip-file "fileb://$WORK_DIR/function.zip" \ + --handler index.handler --runtime nodejs22.x \ + --role "$ROLE_ARN" --timeout 30 \ + --architectures x86_64 \ + --query 'FunctionArn' --output text + +echo " Waiting for function to become active..." +aws lambda wait function-active-v2 --function-name "$FUNCTION_NAME" + +# Step 3: Test the function with a sample SQS event +echo "Step 3: Testing Lambda with sample SQS event" +cat > "$WORK_DIR/test-event.json" << EOF +{ + "Records": [{ + "messageId": "test-msg-001", + "body": "Hello from SQS test event", + "attributes": {"ApproximateReceiveCount": "1", "SentTimestamp": "1545082649183"}, + "messageAttributes": {}, + "md5OfBody": "098f6bcd4621d373cade4e832627b4f6", + "eventSource": "aws:sqs", + "eventSourceARN": "arn:aws:sqs:$REGION:000000000000:test-queue", + "awsRegion": "$REGION" + }] +} +EOF + +aws lambda invoke --function-name "$FUNCTION_NAME" \ + --payload "fileb://$WORK_DIR/test-event.json" \ + --cli-binary-format raw-in-base64-out \ + "$WORK_DIR/response.json" > /dev/null +echo " Response: $(cat "$WORK_DIR/response.json")" + +# Step 4: Create SQS queue +echo "Step 4: Creating SQS queue: $QUEUE_NAME" +QUEUE_URL=$(aws sqs create-queue --queue-name "$QUEUE_NAME" --query 'QueueUrl' --output text) +QUEUE_ARN=$(aws sqs get-queue-attributes --queue-url "$QUEUE_URL" \ + --attribute-names QueueArn --query 'Attributes.QueueArn' --output text) +echo " Queue ARN: $QUEUE_ARN" + +# Step 5: Create event source mapping +echo "Step 5: Connecting SQS queue to Lambda" +EVENT_SOURCE_UUID=$(aws lambda create-event-source-mapping \ + --function-name "$FUNCTION_NAME" \ + --batch-size 10 \ + --event-source-arn "$QUEUE_ARN" \ + --query 'UUID' --output text) +echo " Event source mapping: $EVENT_SOURCE_UUID" + +# Step 6: Send test messages via SQS +echo "Step 6: Sending test messages to SQS" +aws sqs send-message --queue-url "$QUEUE_URL" --message-body "Hello from the Lambda-SQS tutorial" > /dev/null +aws sqs send-message --queue-url "$QUEUE_URL" --message-body "This is message number 2" > /dev/null +echo " Sent 2 messages. Waiting for Lambda to process them..." +sleep 15 + +# Step 7: Verify in CloudWatch Logs +echo "Step 7: Verifying Lambda processed the messages" +LOG_GROUP="/aws/lambda/$FUNCTION_NAME" +FOUND_LOGS=false +for i in $(seq 1 15); do + LOG_STREAM=$(aws logs describe-log-streams --log-group-name "$LOG_GROUP" \ + --order-by LastEventTime --descending --limit 1 \ + --query 'logStreams[0].logStreamName' --output text 2>/dev/null || true) + if [ -n "$LOG_STREAM" ] && [ "$LOG_STREAM" != "None" ]; then + echo " Log stream: $LOG_STREAM" + aws logs get-log-events --log-group-name "$LOG_GROUP" \ + --log-stream-name "$LOG_STREAM" \ + --query 'events[].message' --output text + FOUND_LOGS=true + break + fi + sleep 5 +done +if [ "$FOUND_LOGS" = false ]; then + echo " Logs not available yet (this is normal — they can take a minute to appear)" +fi + +echo "" +echo "Tutorial complete." +echo "Do you want to clean up all resources? (y/n): " +read -r CHOICE +if [[ "$CHOICE" =~ ^[Yy]$ ]]; then + cleanup +else + echo "Resources left running. Manual cleanup commands:" + echo " aws lambda delete-event-source-mapping --uuid $EVENT_SOURCE_UUID" + echo " aws lambda delete-function --function-name $FUNCTION_NAME" + echo " aws sqs delete-queue --queue-url $QUEUE_URL" + echo " aws iam detach-role-policy --role-name $ROLE_NAME --policy-arn arn:aws:iam::aws:policy/service-role/AWSLambdaSQSQueueExecutionRole" + echo " aws iam delete-role --role-name $ROLE_NAME" +fi diff --git a/tuts/089-lambda-sns/README.md b/tuts/089-lambda-sns/README.md new file mode 100644 index 00000000..5b78867c --- /dev/null +++ b/tuts/089-lambda-sns/README.md @@ -0,0 +1,71 @@ +# Lambda: Process SNS messages + +Create a Lambda function that subscribes to an SNS topic, processes published messages, and logs the results. + +## Source + +https://docs.aws.amazon.com/lambda/latest/dg/with-sns-example.html + +## Use case + +- ID: lambda/sns-trigger +- Phase: create +- Complexity: beginner +- Core actions: lambda:CreateFunction, sns:Subscribe, sns:Publish + +## What it does + +1. Creates an SNS topic +2. Creates an IAM execution role for Lambda +3. Creates a Node.js Lambda function that logs SNS messages +4. Subscribes the function to the topic +5. Publishes a test message +6. Verifies the function processed it via CloudWatch Logs +7. Cleans up all resources + +## Running + +```bash +bash lambda-sns.sh +``` + +To auto-run with cleanup: + +```bash +echo 'y' | bash lambda-sns.sh +``` + +## Resources created + +- SNS topic +- IAM role (with AWSLambdaBasicExecutionRole policy) +- Lambda function (Node.js 22) +- SNS subscription +- CloudWatch log group (created automatically by Lambda) + +## Estimated time + +- Run: ~30 seconds +- Cleanup: ~5 seconds + +## Cost + +Free tier eligible. No charges expected for a single message. + +## Related docs + +- [Using Lambda with Amazon SNS](https://docs.aws.amazon.com/lambda/latest/dg/with-sns.html) +- [Tutorial: Using an AWS Lambda function as a subscriber](https://docs.aws.amazon.com/lambda/latest/dg/with-sns-example.html) +- [Amazon SNS message filtering](https://docs.aws.amazon.com/sns/latest/dg/sns-message-filtering.html) + +--- + +## Appendix: Generation details + +| Field | Value | +|-------|-------| +| Generation date | 2026-04-14 | +| Source script | Rewritten from internal 089-lambda-sns-2-cli-script-v3.sh | +| Script test result | EXIT 0, 33s, 6 steps, clean teardown | +| Issues encountered | ERR trap recursion in log retrieval loop (fixed with `|| true`); `add-permission` JSON output noise (suppressed); original used nodejs18.x (upgraded to 22) | +| Iterations | v1 (internal, nodejs18), v2 (wait-for-active patch), v3 (clean rewrite for publish) | diff --git a/tuts/089-lambda-sns/lambda-sns.md b/tuts/089-lambda-sns/lambda-sns.md new file mode 100644 index 00000000..8923c68c --- /dev/null +++ b/tuts/089-lambda-sns/lambda-sns.md @@ -0,0 +1,121 @@ +# Using an AWS Lambda function as a subscriber to an Amazon SNS topic + +This tutorial shows you how to create a Lambda function that processes messages published to an Amazon SNS topic. You create the topic, subscribe a Lambda function to it, publish a test message, and verify the function processed it. + +## Prerequisites + +- AWS CLI configured with credentials and a default region +- Permissions to create Lambda functions, IAM roles, and SNS topics + +## Step 1: Create an SNS topic + +Create a topic that your Lambda function will subscribe to. + +```bash +TOPIC_ARN=$(aws sns create-topic --name my-sns-topic --query 'TopicArn' --output text) +``` + +## Step 2: Create an execution role + +Create an IAM role that grants the Lambda function permission to write logs. + +```bash +aws iam create-role --role-name lambda-sns-role \ + --assume-role-policy-document '{ + "Version":"2012-10-17", + "Statement":[{"Effect":"Allow","Principal":{"Service":"lambda.amazonaws.com"},"Action":"sts:AssumeRole"}] + }' + +aws iam attach-role-policy --role-name lambda-sns-role \ + --policy-arn arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole +``` + +Wait about 10 seconds for the role to propagate. + +## Step 3: Create the Lambda function + +Create a Node.js function that logs each SNS message it receives. + +```javascript +// index.mjs +export const handler = async (event) => { + for (const record of event.Records) { + console.log(`Processed message: ${record.Sns.Message}`); + } + return { statusCode: 200 }; +}; +``` + +Package and deploy: + +```bash +zip function.zip index.mjs + +aws lambda create-function --function-name sns-processor \ + --zip-file fileb://function.zip \ + --handler index.handler --runtime nodejs22.x \ + --role arn:aws:iam:::role/lambda-sns-role \ + --architectures x86_64 +``` + +Wait for the function to become active: + +```bash +aws lambda wait function-active-v2 --function-name sns-processor +``` + +## Step 4: Subscribe the function to the topic + +Grant SNS permission to invoke the function, then create the subscription. + +```bash +aws lambda add-permission --function-name sns-processor \ + --statement-id sns-invoke --action lambda:InvokeFunction \ + --principal sns.amazonaws.com --source-arn $TOPIC_ARN + +aws sns subscribe --protocol lambda \ + --topic-arn $TOPIC_ARN \ + --notification-endpoint $(aws lambda get-function --function-name sns-processor \ + --query 'Configuration.FunctionArn' --output text) +``` + +## Step 5: Publish a test message + +```bash +aws sns publish --topic-arn $TOPIC_ARN \ + --message "Hello from the Lambda-SNS tutorial" --subject "Test" +``` + +## Step 6: Verify in CloudWatch Logs + +After a few seconds, check the function's log output: + +```bash +aws logs describe-log-streams --log-group-name /aws/lambda/sns-processor \ + --order-by LastEventTime --descending --limit 1 + +aws logs get-log-events --log-group-name /aws/lambda/sns-processor \ + --log-stream-name \ + --query 'events[].message' --output text +``` + +You should see `Processed message: Hello from the Lambda-SNS tutorial` in the output. + +## Cleanup + +Delete all resources in reverse order: + +```bash +aws sns unsubscribe --subscription-arn +aws lambda delete-function --function-name sns-processor +aws iam detach-role-policy --role-name lambda-sns-role \ + --policy-arn arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole +aws iam delete-role --role-name lambda-sns-role +aws sns delete-topic --topic-arn $TOPIC_ARN +``` + +The script automates all steps including cleanup. Run it with: + +```bash +bash lambda-sns.sh +``` diff --git a/tuts/089-lambda-sns/lambda-sns.sh b/tuts/089-lambda-sns/lambda-sns.sh new file mode 100644 index 00000000..46505eee --- /dev/null +++ b/tuts/089-lambda-sns/lambda-sns.sh @@ -0,0 +1,136 @@ +#!/bin/bash +# Tutorial: Using an AWS Lambda function as a subscriber to an Amazon SNS topic +# Source: https://docs.aws.amazon.com/lambda/latest/dg/with-sns-example.html + +WORK_DIR=$(mktemp -d) +LOG_FILE="$WORK_DIR/lambda-sns-$(date +%Y%m%d-%H%M%S).log" +exec > >(tee -a "$LOG_FILE") 2>&1 + +REGION=${AWS_DEFAULT_REGION:-${AWS_REGION:-$(aws configure get region 2>/dev/null)}} +if [ -z "$REGION" ]; then + echo "ERROR: No AWS region configured. Set one with: export AWS_DEFAULT_REGION=us-east-1" + exit 1 +fi +export AWS_DEFAULT_REGION="$REGION" +echo "Region: $REGION" + +RANDOM_ID=$(openssl rand -hex 4) +TOPIC_NAME="sns-lambda-tut-${RANDOM_ID}" +ROLE_NAME="lambda-sns-role-${RANDOM_ID}" +FUNCTION_NAME="sns-processor-${RANDOM_ID}" + +RESOURCES=() +handle_error() { echo "ERROR on line $1"; trap - ERR; cleanup; exit 1; } +trap 'handle_error $LINENO' ERR + +cleanup() { + echo "" + echo "Cleaning up resources..." + [ -n "$SUBSCRIPTION_ARN" ] && [ "$SUBSCRIPTION_ARN" != "pending confirmation" ] && \ + aws sns unsubscribe --subscription-arn "$SUBSCRIPTION_ARN" 2>/dev/null && echo " Deleted subscription" + aws lambda delete-function --function-name "$FUNCTION_NAME" 2>/dev/null && echo " Deleted function $FUNCTION_NAME" + aws iam detach-role-policy --role-name "$ROLE_NAME" \ + --policy-arn arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole 2>/dev/null + aws iam delete-role --role-name "$ROLE_NAME" 2>/dev/null && echo " Deleted role $ROLE_NAME" + aws sns delete-topic --topic-arn "$TOPIC_ARN" 2>/dev/null && echo " Deleted topic $TOPIC_NAME" + aws logs delete-log-group --log-group-name "/aws/lambda/$FUNCTION_NAME" 2>/dev/null && echo " Deleted log group" + rm -rf "$WORK_DIR" + echo "Cleanup complete." +} + +# Step 1: Create SNS topic +echo "Step 1: Creating SNS topic: $TOPIC_NAME" +TOPIC_ARN=$(aws sns create-topic --name "$TOPIC_NAME" --query 'TopicArn' --output text) +echo " Topic ARN: $TOPIC_ARN" + +# Step 2: Create IAM role +echo "Step 2: Creating IAM role: $ROLE_NAME" +ROLE_ARN=$(aws iam create-role --role-name "$ROLE_NAME" \ + --assume-role-policy-document '{ + "Version":"2012-10-17", + "Statement":[{"Effect":"Allow","Principal":{"Service":"lambda.amazonaws.com"},"Action":"sts:AssumeRole"}] + }' --query 'Role.Arn' --output text) +aws iam attach-role-policy --role-name "$ROLE_NAME" \ + --policy-arn arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole +echo " Role ARN: $ROLE_ARN" +echo " Waiting for role propagation..." +sleep 10 + +# Step 3: Create Lambda function +echo "Step 3: Creating Lambda function: $FUNCTION_NAME" +cat > "$WORK_DIR/index.mjs" << 'EOF' +export const handler = async (event) => { + for (const record of event.Records) { + console.log(`Processed message: ${record.Sns.Message}`); + } + return { statusCode: 200 }; +}; +EOF +(cd "$WORK_DIR" && zip function.zip index.mjs > /dev/null) + +aws lambda create-function --function-name "$FUNCTION_NAME" \ + --zip-file "fileb://$WORK_DIR/function.zip" \ + --handler index.handler --runtime nodejs22.x \ + --role "$ROLE_ARN" --timeout 30 \ + --architectures x86_64 \ + --query 'FunctionArn' --output text + +echo " Waiting for function to become active..." +aws lambda wait function-active-v2 --function-name "$FUNCTION_NAME" + +# Step 4: Subscribe Lambda to SNS +echo "Step 4: Subscribing Lambda to SNS topic" +FUNCTION_ARN=$(aws lambda get-function --function-name "$FUNCTION_NAME" --query 'Configuration.FunctionArn' --output text) + +aws lambda add-permission --function-name "$FUNCTION_NAME" \ + --statement-id sns-invoke --action lambda:InvokeFunction \ + --principal sns.amazonaws.com --source-arn "$TOPIC_ARN" > /dev/null + +SUBSCRIPTION_ARN=$(aws sns subscribe --protocol lambda \ + --topic-arn "$TOPIC_ARN" --notification-endpoint "$FUNCTION_ARN" \ + --query 'SubscriptionArn' --output text) +echo " Subscription ARN: $SUBSCRIPTION_ARN" + +# Step 5: Publish a test message +echo "Step 5: Publishing test message" +MESSAGE_ID=$(aws sns publish --topic-arn "$TOPIC_ARN" \ + --message "Hello from the Lambda-SNS tutorial" --subject "Test" \ + --query 'MessageId' --output text) +echo " Message ID: $MESSAGE_ID" + +# Step 6: Verify in CloudWatch Logs +echo "Step 6: Verifying Lambda processed the message" +LOG_GROUP="/aws/lambda/$FUNCTION_NAME" +FOUND_LOGS=false +for i in $(seq 1 15); do + LOG_STREAM=$(aws logs describe-log-streams --log-group-name "$LOG_GROUP" \ + --order-by LastEventTime --descending --limit 1 \ + --query 'logStreams[0].logStreamName' --output text 2>/dev/null || true) + if [ -n "$LOG_STREAM" ] && [ "$LOG_STREAM" != "None" ]; then + echo " Log stream: $LOG_STREAM" + aws logs get-log-events --log-group-name "$LOG_GROUP" \ + --log-stream-name "$LOG_STREAM" \ + --query 'events[].message' --output text + FOUND_LOGS=true + break + fi + sleep 5 +done +if [ "$FOUND_LOGS" = false ]; then + echo " Logs not available yet (this is normal — they can take a minute to appear)" +fi + +echo "" +echo "Tutorial complete." +echo "Do you want to clean up all resources? (y/n): " +read -r CHOICE +if [[ "$CHOICE" =~ ^[Yy]$ ]]; then + cleanup +else + echo "Resources left running. Manual cleanup commands:" + echo " aws sns unsubscribe --subscription-arn $SUBSCRIPTION_ARN" + echo " aws lambda delete-function --function-name $FUNCTION_NAME" + echo " aws iam detach-role-policy --role-name $ROLE_NAME --policy-arn arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + echo " aws iam delete-role --role-name $ROLE_NAME" + echo " aws sns delete-topic --topic-arn $TOPIC_ARN" +fi diff --git a/tuts/097-amazon-ec2-autoscaling-gs/README.md b/tuts/097-amazon-ec2-autoscaling-gs/README.md new file mode 100644 index 00000000..f7214ea2 --- /dev/null +++ b/tuts/097-amazon-ec2-autoscaling-gs/README.md @@ -0,0 +1,61 @@ +# EC2 Auto Scaling: Create an Auto Scaling group + +## Source + + + +## Use case + +| Field | Value | +|-------|-------| +| ID | `autoscaling/getting-started` | +| Level | Intermediate | +| Core actions | `autoscaling:CreateAutoScalingGroup`, `ec2:CreateLaunchTemplate` | + +## What it does + +1. Finds the latest Amazon Linux 2023 AMI. +2. Creates a launch template (`t2.micro`). +3. Gets available availability zones. +4. Creates an Auto Scaling group (min 1, max 3, desired 1). +5. Waits for the instance to reach `InService` and describes the group. +6. Cleans up: force-deletes the ASG and deletes the launch template. + +## Running + +```bash +# Read through the tutorial, then run each step: +cat amazon-ec2-autoscaling-gs.md + +# Or run the script directly: +bash amazon-ec2-autoscaling-gs.sh +``` + +## Resources created + +| Resource | Name/Detail | +|----------|-------------| +| Launch template | `my-asg-launch-template` | +| Auto Scaling group | `my-asg` (min 1, max 3, desired 1) | +| EC2 instance | 1× `t2.micro` (launched by the ASG) | + +## Estimated time + +~73 seconds (most time spent waiting for the instance to reach `InService`). + +## Cost + +- `t2.micro` is Free Tier eligible (750 hours/month for 12 months on new accounts). +- Outside the Free Tier, charges apply while the instance is running. +- Clean up promptly after completing the tutorial. + +## Related docs + +- [Amazon EC2 Auto Scaling User Guide](https://docs.aws.amazon.com/autoscaling/ec2/userguide/) +- [Launch templates](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-launch-templates.html) +- [Auto Scaling groups](https://docs.aws.amazon.com/autoscaling/ec2/userguide/auto-scaling-groups.html) + +--- + +## Appendix + diff --git a/tuts/097-amazon-ec2-autoscaling-gs/amazon-ec2-autoscaling-gs.md b/tuts/097-amazon-ec2-autoscaling-gs/amazon-ec2-autoscaling-gs.md new file mode 100644 index 00000000..1b848448 --- /dev/null +++ b/tuts/097-amazon-ec2-autoscaling-gs/amazon-ec2-autoscaling-gs.md @@ -0,0 +1,110 @@ +# EC2 Auto Scaling: Create an Auto Scaling group + +This tutorial walks you through creating an Auto Scaling group with the AWS CLI. You create a launch template, configure an Auto Scaling group across availability zones, verify that an instance launches, then clean up all resources. + +> **Cost note:** This tutorial launches a `t2.micro` EC2 instance, which is eligible for the AWS Free Tier. If you are outside the Free Tier, charges apply while the instance is running. Clean up promptly to avoid unnecessary costs. + +## Prerequisites + +- AWS CLI v2 installed and configured with credentials that have permissions for EC2 and Auto Scaling operations. +- A default VPC in your selected Region (most accounts have one). + +## Step 1: Find the latest Amazon Linux 2023 AMI + +Look up the latest Amazon Linux 2023 AMI ID for your Region using Systems Manager Parameter Store. + +```bash +AMI_ID=$(aws ec2 describe-images \ + --owners amazon \ + --filters "Name=name,Values=al2023-ami-2023.*-x86_64" \ + "Name=state,Values=available" \ + --query 'sort_by(Images, &CreationDate)[-1].ImageId' \ + --output text) + +echo "AMI_ID=$AMI_ID" +``` + +## Step 2: Create a launch template + +Create a launch template that specifies the AMI and instance type. + +```bash +TEMPLATE_NAME="my-asg-launch-template" + +aws ec2 create-launch-template \ + --launch-template-name "$TEMPLATE_NAME" \ + --launch-template-data "{\"ImageId\":\"$AMI_ID\",\"InstanceType\":\"t2.micro\"}" +``` + +## Step 3: Get availability zones + +Retrieve the availability zones for your Region. The Auto Scaling group distributes instances across these zones. + +```bash +AZ_LIST=$(aws ec2 describe-availability-zones \ + --query 'AvailabilityZones[?State==`available`].ZoneName' \ + --output text) + +echo "AZ_LIST=$AZ_LIST" +``` + +The output is a space-separated list of availability zone names (for example, `us-east-1a us-east-1b us-east-1c`). + +## Step 4: Create the Auto Scaling group + +Create an Auto Scaling group with a minimum of 1 instance, a maximum of 3, and a desired capacity of 1. + +```bash +ASG_NAME="my-asg" + +aws autoscaling create-auto-scaling-group \ + --auto-scaling-group-name "$ASG_NAME" \ + --launch-template "LaunchTemplateName=$TEMPLATE_NAME" \ + --min-size 1 \ + --max-size 3 \ + --desired-capacity 1 \ + --availability-zones $AZ_LIST +``` + +Note that `--availability-zones` takes space-separated zone names, not comma-separated. + +## Step 5: Wait for the instance and describe the group + +Wait for the instance to reach the `InService` state, then describe the Auto Scaling group to confirm. + +```bash +echo "Waiting for instance to be InService..." +aws autoscaling wait group-in-service \ + --auto-scaling-group-name "$ASG_NAME" + +aws autoscaling describe-auto-scaling-groups \ + --auto-scaling-group-names "$ASG_NAME" \ + --query 'AutoScalingGroups[0].{Name:AutoScalingGroupName,Min:MinSize,Max:MaxSize,Desired:DesiredCapacity,Instances:Instances[*].{Id:InstanceId,State:LifecycleState}}' \ + --output table +``` + +## Step 6: Clean up + +Delete the Auto Scaling group (force-deleting terminates running instances), then delete the launch template. + +```bash +aws autoscaling delete-auto-scaling-group \ + --auto-scaling-group-name "$ASG_NAME" \ + --force-delete + +echo "Waiting for instances to terminate..." +sleep 30 + +aws ec2 delete-launch-template \ + --launch-template-name "$TEMPLATE_NAME" + +echo "Cleanup complete." +``` + +The `--force-delete` flag terminates all instances in the group before deleting it. + +## Related resources + +- [Amazon EC2 Auto Scaling User Guide — Get started](https://docs.aws.amazon.com/autoscaling/ec2/userguide/get-started-with-ec2-auto-scaling.html) +- [create-auto-scaling-group CLI reference](https://docs.aws.amazon.com/cli/latest/reference/autoscaling/create-auto-scaling-group.html) +- [create-launch-template CLI reference](https://docs.aws.amazon.com/cli/latest/reference/ec2/create-launch-template.html) diff --git a/tuts/097-amazon-ec2-autoscaling-gs/amazon-ec2-autoscaling-gs.sh b/tuts/097-amazon-ec2-autoscaling-gs/amazon-ec2-autoscaling-gs.sh new file mode 100644 index 00000000..ebb79387 --- /dev/null +++ b/tuts/097-amazon-ec2-autoscaling-gs/amazon-ec2-autoscaling-gs.sh @@ -0,0 +1,102 @@ +#!/bin/bash +# Tutorial: Create an Auto Scaling group with a launch template +# Source: https://docs.aws.amazon.com/autoscaling/ec2/userguide/get-started-with-ec2-auto-scaling.html + +WORK_DIR=$(mktemp -d) +LOG_FILE="$WORK_DIR/autoscaling-$(date +%Y%m%d-%H%M%S).log" +exec > >(tee -a "$LOG_FILE") 2>&1 + +REGION=${AWS_DEFAULT_REGION:-${AWS_REGION:-$(aws configure get region 2>/dev/null)}} +if [ -z "$REGION" ]; then + echo "ERROR: No AWS region configured. Set one with: export AWS_DEFAULT_REGION=us-east-1" + exit 1 +fi +export AWS_DEFAULT_REGION="$REGION" +echo "Region: $REGION" + +RANDOM_ID=$(openssl rand -hex 4) +LT_NAME="tut-lt-${RANDOM_ID}" +ASG_NAME="tut-asg-${RANDOM_ID}" + +handle_error() { echo "ERROR on line $1"; trap - ERR; cleanup; exit 1; } +trap 'handle_error $LINENO' ERR + +cleanup() { + echo "" + echo "Cleaning up resources..." + aws autoscaling delete-auto-scaling-group --auto-scaling-group-name "$ASG_NAME" \ + --force-delete > /dev/null 2>&1 && echo " Deleting ASG $ASG_NAME (instances terminating)..." + # Wait for ASG to be deleted + for i in $(seq 1 30); do + aws autoscaling describe-auto-scaling-groups --auto-scaling-group-names "$ASG_NAME" \ + --query 'AutoScalingGroups[0].AutoScalingGroupName' --output text 2>/dev/null | grep -q "$ASG_NAME" || break + sleep 10 + done + echo " ASG deleted" + aws ec2 delete-launch-template --launch-template-name "$LT_NAME" > /dev/null 2>&1 && \ + echo " Deleted launch template $LT_NAME" + rm -rf "$WORK_DIR" + echo "Cleanup complete." +} + +# Step 1: Get the latest Amazon Linux 2023 AMI +echo "Step 1: Finding latest Amazon Linux 2023 AMI" +AMI_ID=$(aws ec2 describe-images \ + --owners amazon \ + --filters "Name=name,Values=al2023-ami-2023*-x86_64" "Name=state,Values=available" \ + --query 'sort_by(Images, &CreationDate)[-1].ImageId' --output text) +echo " AMI: $AMI_ID" + +# Step 2: Create a launch template +echo "Step 2: Creating launch template: $LT_NAME" +LT_ID=$(aws ec2 create-launch-template --launch-template-name "$LT_NAME" \ + --launch-template-data "{\"ImageId\":\"$AMI_ID\",\"InstanceType\":\"t2.micro\"}" \ + --query 'LaunchTemplate.LaunchTemplateId' --output text) +echo " Launch template: $LT_ID" + +# Step 3: Get availability zones +echo "Step 3: Getting availability zones" +AZS=$(aws ec2 describe-availability-zones --query 'AvailabilityZones[:2].ZoneName' --output text) +echo " Using: $AZS" + +# Step 4: Create Auto Scaling group +echo "Step 4: Creating Auto Scaling group: $ASG_NAME" +aws autoscaling create-auto-scaling-group --auto-scaling-group-name "$ASG_NAME" \ + --launch-template "LaunchTemplateId=$LT_ID,Version=\$Latest" \ + --min-size 1 --max-size 3 --desired-capacity 1 \ + --availability-zones $AZS +echo " ASG created (desired: 1, min: 1, max: 3)" + +# Step 5: Wait for instance to launch +echo "Step 5: Waiting for instance to launch..." +for i in $(seq 1 12); do + INSTANCE_COUNT=$(aws autoscaling describe-auto-scaling-groups \ + --auto-scaling-group-names "$ASG_NAME" \ + --query 'AutoScalingGroups[0].Instances | length(@)' --output text 2>/dev/null || echo "0") + if [ "$INSTANCE_COUNT" -gt 0 ] 2>/dev/null; then + echo " $INSTANCE_COUNT instance(s) running" + aws autoscaling describe-auto-scaling-groups --auto-scaling-group-names "$ASG_NAME" \ + --query 'AutoScalingGroups[0].Instances[].{Id:InstanceId,AZ:AvailabilityZone,Health:HealthStatus,Lifecycle:LifecycleState}' --output table + break + fi + sleep 10 +done + +# Step 6: Describe the group +echo "Step 6: Auto Scaling group details" +aws autoscaling describe-auto-scaling-groups --auto-scaling-group-names "$ASG_NAME" \ + --query 'AutoScalingGroups[0].{Name:AutoScalingGroupName,Min:MinSize,Max:MaxSize,Desired:DesiredCapacity,Instances:Instances|length(@)}' --output table + +echo "" +echo "Tutorial complete." +echo "Note: EC2 instances are running and will incur charges until cleaned up." +echo "Do you want to clean up all resources? (y/n): " +read -r CHOICE +if [[ "$CHOICE" =~ ^[Yy]$ ]]; then + cleanup +else + echo "Resources left running. Instances will incur charges." + echo "Manual cleanup:" + echo " aws autoscaling delete-auto-scaling-group --auto-scaling-group-name $ASG_NAME --force-delete" + echo " aws ec2 delete-launch-template --launch-template-name $LT_NAME" +fi diff --git a/tuts/151-lambda-layers/lambda-layers.sh b/tuts/151-lambda-layers/lambda-layers.sh new file mode 100644 index 00000000..392ae762 --- /dev/null +++ b/tuts/151-lambda-layers/lambda-layers.sh @@ -0,0 +1,34 @@ +#!/bin/bash +WORK_DIR=$(mktemp -d); exec > >(tee -a "$WORK_DIR/lambda-layers.log") 2>&1 +REGION=${AWS_DEFAULT_REGION:-$(aws configure get region 2>/dev/null)}; [ -z "$REGION" ] && echo "ERROR: No region" && exit 1; export AWS_DEFAULT_REGION="$REGION"; echo "Region: $REGION" +RANDOM_ID=$(openssl rand -hex 4); LAYER_NAME="tut-layer-${RANDOM_ID}"; FUNC_NAME="tut-layer-func-${RANDOM_ID}"; ROLE_NAME="lambda-layer-role-${RANDOM_ID}" +handle_error() { echo "ERROR on line $1"; trap - ERR; cleanup; exit 1; }; trap 'handle_error $LINENO' ERR +cleanup() { echo ""; echo "Cleaning up..."; aws lambda delete-function --function-name "$FUNC_NAME" 2>/dev/null && echo " Deleted function"; aws lambda delete-layer-version --layer-name "$LAYER_NAME" --version-number 1 2>/dev/null && echo " Deleted layer"; aws iam detach-role-policy --role-name "$ROLE_NAME" --policy-arn arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole 2>/dev/null; aws iam delete-role --role-name "$ROLE_NAME" 2>/dev/null && echo " Deleted role"; rm -rf "$WORK_DIR"; echo "Done."; } +echo "Step 1: Creating a layer" +mkdir -p "$WORK_DIR/python" +cat > "$WORK_DIR/python/helpers.py" << 'EOF' +def greet(name): return f"Hello, {name}! (from layer)" +def add(a, b): return a + b +EOF +(cd "$WORK_DIR" && zip -r layer.zip python > /dev/null) +LAYER_ARN=$(aws lambda publish-layer-version --layer-name "$LAYER_NAME" --zip-file "fileb://$WORK_DIR/layer.zip" --compatible-runtimes python3.12 --query 'LayerVersionArn' --output text) +echo " Layer ARN: $LAYER_ARN" +echo "Step 2: Creating function that uses the layer" +ROLE_ARN=$(aws iam create-role --role-name "$ROLE_NAME" --assume-role-policy-document '{"Version":"2012-10-17","Statement":[{"Effect":"Allow","Principal":{"Service":"lambda.amazonaws.com"},"Action":"sts:AssumeRole"}]}' --query 'Role.Arn' --output text) +aws iam attach-role-policy --role-name "$ROLE_NAME" --policy-arn arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole +sleep 10 +cat > "$WORK_DIR/index.py" << 'EOF' +from helpers import greet, add +def handler(event, context): + return {"greeting": greet(event.get("name", "World")), "sum": add(3, 4)} +EOF +(cd "$WORK_DIR" && zip func.zip index.py > /dev/null) +aws lambda create-function --function-name "$FUNC_NAME" --zip-file "fileb://$WORK_DIR/func.zip" --handler index.handler --runtime python3.12 --role "$ROLE_ARN" --layers "$LAYER_ARN" --architectures x86_64 > /dev/null +aws lambda wait function-active-v2 --function-name "$FUNC_NAME" +echo "Step 3: Invoking function" +aws lambda invoke --function-name "$FUNC_NAME" --payload '{"name":"Tutorial"}' --cli-binary-format raw-in-base64-out "$WORK_DIR/response.json" > /dev/null +cat "$WORK_DIR/response.json" | python3 -m json.tool +echo "Step 4: Listing layers" +aws lambda list-layers --query 'Layers[?starts_with(LayerName, `tut-`)].{Name:LayerName,Version:LatestMatchingVersion.Version}' --output table +echo ""; echo "Tutorial complete." +echo "Do you want to clean up? (y/n): "; read -r CHOICE; [[ "$CHOICE" =~ ^[Yy]$ ]] && cleanup diff --git a/tuts/155-ec2-snapshots/ec2-snapshots.sh b/tuts/155-ec2-snapshots/ec2-snapshots.sh new file mode 100644 index 00000000..f2d60550 --- /dev/null +++ b/tuts/155-ec2-snapshots/ec2-snapshots.sh @@ -0,0 +1,29 @@ +#!/bin/bash +WORK_DIR=$(mktemp -d); exec > >(tee -a "$WORK_DIR/ec2-snap.log") 2>&1 +REGION=${AWS_DEFAULT_REGION:-$(aws configure get region 2>/dev/null)}; [ -z "$REGION" ] && echo "ERROR: No region" && exit 1; export AWS_DEFAULT_REGION="$REGION"; echo "Region: $REGION" +RANDOM_ID=$(openssl rand -hex 4) +handle_error() { echo "ERROR on line $1"; trap - ERR; cleanup; exit 1; }; trap 'handle_error $LINENO' ERR +cleanup() { echo ""; echo "Cleaning up..."; [ -n "$SNAP_ID" ] && aws ec2 delete-snapshot --snapshot-id "$SNAP_ID" 2>/dev/null && echo " Deleted snapshot"; [ -n "$VOL_ID" ] && aws ec2 delete-volume --volume-id "$VOL_ID" 2>/dev/null && echo " Deleted volume"; rm -rf "$WORK_DIR"; echo "Done."; } +AZ=$(aws ec2 describe-availability-zones --query 'AvailabilityZones[0].ZoneName' --output text) +echo "Step 1: Creating a volume" +VOL_ID=$(aws ec2 create-volume --size 1 --volume-type gp3 --availability-zone "$AZ" --tag-specifications "ResourceType=volume,Tags=[{Key=Name,Value=tut-vol-${RANDOM_ID}}]" --query 'VolumeId' --output text) +echo " Volume: $VOL_ID" +aws ec2 wait volume-available --volume-ids "$VOL_ID" +echo "Step 2: Creating a snapshot" +SNAP_ID=$(aws ec2 create-snapshot --volume-id "$VOL_ID" --description "Tutorial snapshot ${RANDOM_ID}" --tag-specifications "ResourceType=snapshot,Tags=[{Key=Name,Value=tut-snap-${RANDOM_ID}}]" --query 'SnapshotId' --output text) +echo " Snapshot: $SNAP_ID" +echo " Waiting for snapshot..." +aws ec2 wait snapshot-completed --snapshot-ids "$SNAP_ID" +echo "Step 3: Describing snapshot" +aws ec2 describe-snapshots --snapshot-ids "$SNAP_ID" --query 'Snapshots[0].{Id:SnapshotId,State:State,Size:VolumeSize,Progress:Progress}' --output table +echo "Step 4: Copying snapshot (same region)" +COPY_ID=$(aws ec2 copy-snapshot --source-region "$REGION" --source-snapshot-id "$SNAP_ID" --description "Copy of tutorial snapshot" --query 'SnapshotId' --output text) +echo " Copy: $COPY_ID" +echo "Step 5: Listing snapshots" +aws ec2 describe-snapshots --owner-ids self --filters "Name=tag:Name,Values=tut-snap-*" --query 'Snapshots[].{Id:SnapshotId,State:State,Size:VolumeSize}' --output table +echo ""; echo "Tutorial complete." +echo "Do you want to clean up? (y/n): "; read -r CHOICE +if [[ "$CHOICE" =~ ^[Yy]$ ]]; then + aws ec2 delete-snapshot --snapshot-id "$COPY_ID" 2>/dev/null && echo " Deleted copy" + cleanup +fi From a723c174b6a4d092ccf1cf641b4c61604330f4f2 Mon Sep 17 00:00:00 2001 From: Michael Wunderlich Date: Tue, 21 Apr 2026 05:17:16 +0000 Subject: [PATCH 2/3] Apply technical requirements (R1, R2, R9, R10, R13) - R1: Add AWS_REGION to region fallback chain - R2: Replace openssl rand with /dev/urandom - R9: Remove Appendix/Generation details from READMEs - R10: Remove internal references - R13: Add REVISION-HISTORY.md --- tuts/084-lambda-sqs/README.md | 12 ------------ tuts/084-lambda-sqs/REVISION-HISTORY.md | 8 ++++++++ tuts/084-lambda-sqs/lambda-sqs.sh | 2 +- tuts/089-lambda-sns/README.md | 12 ------------ tuts/089-lambda-sns/REVISION-HISTORY.md | 8 ++++++++ tuts/089-lambda-sns/lambda-sns.sh | 2 +- .../REVISION-HISTORY.md | 8 ++++++++ .../amazon-ec2-autoscaling-gs.sh | 2 +- tuts/151-lambda-layers/REVISION-HISTORY.md | 8 ++++++++ tuts/151-lambda-layers/lambda-layers.sh | 4 ++-- tuts/155-ec2-snapshots/REVISION-HISTORY.md | 8 ++++++++ tuts/155-ec2-snapshots/ec2-snapshots.sh | 4 ++-- 12 files changed, 47 insertions(+), 31 deletions(-) create mode 100644 tuts/084-lambda-sqs/REVISION-HISTORY.md create mode 100644 tuts/089-lambda-sns/REVISION-HISTORY.md create mode 100644 tuts/097-amazon-ec2-autoscaling-gs/REVISION-HISTORY.md create mode 100644 tuts/151-lambda-layers/REVISION-HISTORY.md create mode 100644 tuts/155-ec2-snapshots/REVISION-HISTORY.md diff --git a/tuts/084-lambda-sqs/README.md b/tuts/084-lambda-sqs/README.md index 8e0b7a79..97faea84 100644 --- a/tuts/084-lambda-sqs/README.md +++ b/tuts/084-lambda-sqs/README.md @@ -58,15 +58,3 @@ Free tier eligible. No charges expected for a few messages. - [Using Lambda with Amazon SQS](https://docs.aws.amazon.com/lambda/latest/dg/with-sqs.html) - [Tutorial: Using Lambda with Amazon SQS](https://docs.aws.amazon.com/lambda/latest/dg/with-sqs-example.html) - [Amazon SQS visibility timeout](https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/sqs-visibility-timeout.html) - ---- - -## Appendix: Generation details - -| Field | Value | -|-------|-------| -| Generation date | 2026-04-14 | -| Source script | Rewritten from internal 084-lambda-sqs-2-cli-script-v2.sh | -| Script test result | EXIT 0, 44s, 7 steps, clean teardown | -| Issues encountered | Original used `set -e` with `log_command` wrapper (replaced with ERR trap); missing `fileb://` for payload (fixed); original used nodejs22.x (kept) | -| Iterations | v1 (internal), v2 (wait-for-active + region fix), v3 (clean rewrite for publish) | diff --git a/tuts/084-lambda-sqs/REVISION-HISTORY.md b/tuts/084-lambda-sqs/REVISION-HISTORY.md new file mode 100644 index 00000000..c40d267a --- /dev/null +++ b/tuts/084-lambda-sqs/REVISION-HISTORY.md @@ -0,0 +1,8 @@ +# Revision History: 084-lambda-sqs + +## Shell (CLI script) + +### 2026-04-14 v1 published +- Type: functional +- Initial version + diff --git a/tuts/084-lambda-sqs/lambda-sqs.sh b/tuts/084-lambda-sqs/lambda-sqs.sh index e3eb1ff3..4c16f11d 100644 --- a/tuts/084-lambda-sqs/lambda-sqs.sh +++ b/tuts/084-lambda-sqs/lambda-sqs.sh @@ -14,7 +14,7 @@ fi export AWS_DEFAULT_REGION="$REGION" echo "Region: $REGION" -RANDOM_ID=$(openssl rand -hex 4) +RANDOM_ID=$(cat /dev/urandom | tr -dc 'a-z0-9' | fold -w 8 | head -n 1) ROLE_NAME="lambda-sqs-role-${RANDOM_ID}" FUNCTION_NAME="sqs-processor-${RANDOM_ID}" QUEUE_NAME="lambda-tut-queue-${RANDOM_ID}" diff --git a/tuts/089-lambda-sns/README.md b/tuts/089-lambda-sns/README.md index 5b78867c..91cd6b87 100644 --- a/tuts/089-lambda-sns/README.md +++ b/tuts/089-lambda-sns/README.md @@ -57,15 +57,3 @@ Free tier eligible. No charges expected for a single message. - [Using Lambda with Amazon SNS](https://docs.aws.amazon.com/lambda/latest/dg/with-sns.html) - [Tutorial: Using an AWS Lambda function as a subscriber](https://docs.aws.amazon.com/lambda/latest/dg/with-sns-example.html) - [Amazon SNS message filtering](https://docs.aws.amazon.com/sns/latest/dg/sns-message-filtering.html) - ---- - -## Appendix: Generation details - -| Field | Value | -|-------|-------| -| Generation date | 2026-04-14 | -| Source script | Rewritten from internal 089-lambda-sns-2-cli-script-v3.sh | -| Script test result | EXIT 0, 33s, 6 steps, clean teardown | -| Issues encountered | ERR trap recursion in log retrieval loop (fixed with `|| true`); `add-permission` JSON output noise (suppressed); original used nodejs18.x (upgraded to 22) | -| Iterations | v1 (internal, nodejs18), v2 (wait-for-active patch), v3 (clean rewrite for publish) | diff --git a/tuts/089-lambda-sns/REVISION-HISTORY.md b/tuts/089-lambda-sns/REVISION-HISTORY.md new file mode 100644 index 00000000..d53ac298 --- /dev/null +++ b/tuts/089-lambda-sns/REVISION-HISTORY.md @@ -0,0 +1,8 @@ +# Revision History: 089-lambda-sns + +## Shell (CLI script) + +### 2026-04-14 v1 published +- Type: functional +- Initial version + diff --git a/tuts/089-lambda-sns/lambda-sns.sh b/tuts/089-lambda-sns/lambda-sns.sh index 46505eee..ec0bd0d8 100644 --- a/tuts/089-lambda-sns/lambda-sns.sh +++ b/tuts/089-lambda-sns/lambda-sns.sh @@ -14,7 +14,7 @@ fi export AWS_DEFAULT_REGION="$REGION" echo "Region: $REGION" -RANDOM_ID=$(openssl rand -hex 4) +RANDOM_ID=$(cat /dev/urandom | tr -dc 'a-z0-9' | fold -w 8 | head -n 1) TOPIC_NAME="sns-lambda-tut-${RANDOM_ID}" ROLE_NAME="lambda-sns-role-${RANDOM_ID}" FUNCTION_NAME="sns-processor-${RANDOM_ID}" diff --git a/tuts/097-amazon-ec2-autoscaling-gs/REVISION-HISTORY.md b/tuts/097-amazon-ec2-autoscaling-gs/REVISION-HISTORY.md new file mode 100644 index 00000000..7be74d0b --- /dev/null +++ b/tuts/097-amazon-ec2-autoscaling-gs/REVISION-HISTORY.md @@ -0,0 +1,8 @@ +# Revision History: 097-amazon-ec2-autoscaling-gs + +## Shell (CLI script) + +### 2026-04-14 v1 published +- Type: functional +- Initial version + diff --git a/tuts/097-amazon-ec2-autoscaling-gs/amazon-ec2-autoscaling-gs.sh b/tuts/097-amazon-ec2-autoscaling-gs/amazon-ec2-autoscaling-gs.sh index ebb79387..a1885d19 100644 --- a/tuts/097-amazon-ec2-autoscaling-gs/amazon-ec2-autoscaling-gs.sh +++ b/tuts/097-amazon-ec2-autoscaling-gs/amazon-ec2-autoscaling-gs.sh @@ -14,7 +14,7 @@ fi export AWS_DEFAULT_REGION="$REGION" echo "Region: $REGION" -RANDOM_ID=$(openssl rand -hex 4) +RANDOM_ID=$(cat /dev/urandom | tr -dc 'a-z0-9' | fold -w 8 | head -n 1) LT_NAME="tut-lt-${RANDOM_ID}" ASG_NAME="tut-asg-${RANDOM_ID}" diff --git a/tuts/151-lambda-layers/REVISION-HISTORY.md b/tuts/151-lambda-layers/REVISION-HISTORY.md new file mode 100644 index 00000000..388cf685 --- /dev/null +++ b/tuts/151-lambda-layers/REVISION-HISTORY.md @@ -0,0 +1,8 @@ +# Revision History: 151-lambda-layers + +## Shell (CLI script) + +### 2026-04-14 v1 published +- Type: functional +- Initial version + diff --git a/tuts/151-lambda-layers/lambda-layers.sh b/tuts/151-lambda-layers/lambda-layers.sh index 392ae762..79532e20 100644 --- a/tuts/151-lambda-layers/lambda-layers.sh +++ b/tuts/151-lambda-layers/lambda-layers.sh @@ -1,7 +1,7 @@ #!/bin/bash WORK_DIR=$(mktemp -d); exec > >(tee -a "$WORK_DIR/lambda-layers.log") 2>&1 -REGION=${AWS_DEFAULT_REGION:-$(aws configure get region 2>/dev/null)}; [ -z "$REGION" ] && echo "ERROR: No region" && exit 1; export AWS_DEFAULT_REGION="$REGION"; echo "Region: $REGION" -RANDOM_ID=$(openssl rand -hex 4); LAYER_NAME="tut-layer-${RANDOM_ID}"; FUNC_NAME="tut-layer-func-${RANDOM_ID}"; ROLE_NAME="lambda-layer-role-${RANDOM_ID}" +REGION=${AWS_DEFAULT_REGION:-${AWS_REGION:-$(aws configure get region 2>/dev/null))}; [ -z "$REGION" ] && echo "ERROR: No region" && exit 1; export AWS_DEFAULT_REGION="$REGION"; echo "Region: $REGION" +RANDOM_ID=$(cat /dev/urandom | tr -dc 'a-z0-9' | fold -w 8 | head -n 1); LAYER_NAME="tut-layer-${RANDOM_ID}"; FUNC_NAME="tut-layer-func-${RANDOM_ID}"; ROLE_NAME="lambda-layer-role-${RANDOM_ID}" handle_error() { echo "ERROR on line $1"; trap - ERR; cleanup; exit 1; }; trap 'handle_error $LINENO' ERR cleanup() { echo ""; echo "Cleaning up..."; aws lambda delete-function --function-name "$FUNC_NAME" 2>/dev/null && echo " Deleted function"; aws lambda delete-layer-version --layer-name "$LAYER_NAME" --version-number 1 2>/dev/null && echo " Deleted layer"; aws iam detach-role-policy --role-name "$ROLE_NAME" --policy-arn arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole 2>/dev/null; aws iam delete-role --role-name "$ROLE_NAME" 2>/dev/null && echo " Deleted role"; rm -rf "$WORK_DIR"; echo "Done."; } echo "Step 1: Creating a layer" diff --git a/tuts/155-ec2-snapshots/REVISION-HISTORY.md b/tuts/155-ec2-snapshots/REVISION-HISTORY.md new file mode 100644 index 00000000..a9cf3b43 --- /dev/null +++ b/tuts/155-ec2-snapshots/REVISION-HISTORY.md @@ -0,0 +1,8 @@ +# Revision History: 155-ec2-snapshots + +## Shell (CLI script) + +### 2026-04-14 v1 published +- Type: functional +- Initial version + diff --git a/tuts/155-ec2-snapshots/ec2-snapshots.sh b/tuts/155-ec2-snapshots/ec2-snapshots.sh index f2d60550..005326f8 100644 --- a/tuts/155-ec2-snapshots/ec2-snapshots.sh +++ b/tuts/155-ec2-snapshots/ec2-snapshots.sh @@ -1,7 +1,7 @@ #!/bin/bash WORK_DIR=$(mktemp -d); exec > >(tee -a "$WORK_DIR/ec2-snap.log") 2>&1 -REGION=${AWS_DEFAULT_REGION:-$(aws configure get region 2>/dev/null)}; [ -z "$REGION" ] && echo "ERROR: No region" && exit 1; export AWS_DEFAULT_REGION="$REGION"; echo "Region: $REGION" -RANDOM_ID=$(openssl rand -hex 4) +REGION=${AWS_DEFAULT_REGION:-${AWS_REGION:-$(aws configure get region 2>/dev/null))}; [ -z "$REGION" ] && echo "ERROR: No region" && exit 1; export AWS_DEFAULT_REGION="$REGION"; echo "Region: $REGION" +RANDOM_ID=$(cat /dev/urandom | tr -dc 'a-z0-9' | fold -w 8 | head -n 1) handle_error() { echo "ERROR on line $1"; trap - ERR; cleanup; exit 1; }; trap 'handle_error $LINENO' ERR cleanup() { echo ""; echo "Cleaning up..."; [ -n "$SNAP_ID" ] && aws ec2 delete-snapshot --snapshot-id "$SNAP_ID" 2>/dev/null && echo " Deleted snapshot"; [ -n "$VOL_ID" ] && aws ec2 delete-volume --volume-id "$VOL_ID" 2>/dev/null && echo " Deleted volume"; rm -rf "$WORK_DIR"; echo "Done."; } AZ=$(aws ec2 describe-availability-zones --query 'AvailabilityZones[0].ZoneName' --output text) From 7a1cc90468127be1074b5de983c411eff9cfb9f9 Mon Sep 17 00:00:00 2001 From: Michael Wunderlich Date: Tue, 21 Apr 2026 05:37:59 +0000 Subject: [PATCH 3/3] Add README.md and tutorial walkthrough for script-only tutorials --- tuts/151-lambda-layers/README.md | 39 +++++++++++++++++++++++++ tuts/151-lambda-layers/lambda-layers.md | 27 +++++++++++++++++ tuts/155-ec2-snapshots/README.md | 39 +++++++++++++++++++++++++ tuts/155-ec2-snapshots/ec2-snapshots.md | 31 ++++++++++++++++++++ 4 files changed, 136 insertions(+) create mode 100644 tuts/151-lambda-layers/README.md create mode 100644 tuts/151-lambda-layers/lambda-layers.md create mode 100644 tuts/155-ec2-snapshots/README.md create mode 100644 tuts/155-ec2-snapshots/ec2-snapshots.md diff --git a/tuts/151-lambda-layers/README.md b/tuts/151-lambda-layers/README.md new file mode 100644 index 00000000..3c27f03a --- /dev/null +++ b/tuts/151-lambda-layers/README.md @@ -0,0 +1,39 @@ +# Lambda Layers + +An AWS CLI tutorial that demonstrates Iam operations. + +## Running + +```bash +bash lambda-layers.sh +``` + +To auto-run with cleanup: + +```bash +echo 'y' | bash lambda-layers.sh +``` + +## What it does + +1. Creating a layer +2. Creating function that uses the layer +3. Invoking function +4. Listing layers + +## Resources created + +- Function +- Role + +The script prompts you to clean up resources when it finishes. + +## Cost + +Free tier eligible for most operations. Clean up resources after use to avoid charges. + +## Related docs + +- [AWS CLI iam reference](https://docs.aws.amazon.com/cli/latest/reference/iam/index.html) +- [AWS CLI lambda reference](https://docs.aws.amazon.com/cli/latest/reference/lambda/index.html) + diff --git a/tuts/151-lambda-layers/lambda-layers.md b/tuts/151-lambda-layers/lambda-layers.md new file mode 100644 index 00000000..d5754048 --- /dev/null +++ b/tuts/151-lambda-layers/lambda-layers.md @@ -0,0 +1,27 @@ +# Lambda Layers + +## Prerequisites + +1. AWS CLI installed and configured (`aws configure`) +2. Appropriate IAM permissions for the AWS services used + +## Step 1: Creating a layer + +The script handles this step automatically. See `lambda-layers.sh` for the exact CLI commands. + +## Step 2: Creating function that uses the layer + +The script handles this step automatically. See `lambda-layers.sh` for the exact CLI commands. + +## Step 3: Invoking function + +The script handles this step automatically. See `lambda-layers.sh` for the exact CLI commands. + +## Step 4: Listing layers + +The script handles this step automatically. See `lambda-layers.sh` for the exact CLI commands. + +## Cleanup + +The script prompts you to clean up all created resources. If you need to clean up manually, check the script log for the resource names that were created. + diff --git a/tuts/155-ec2-snapshots/README.md b/tuts/155-ec2-snapshots/README.md new file mode 100644 index 00000000..df60ca56 --- /dev/null +++ b/tuts/155-ec2-snapshots/README.md @@ -0,0 +1,39 @@ +# Ec2 Snapshots + +An AWS CLI tutorial that demonstrates Ec2 operations. + +## Running + +```bash +bash ec2-snapshots.sh +``` + +To auto-run with cleanup: + +```bash +echo 'y' | bash ec2-snapshots.sh +``` + +## What it does + +1. Creating a volume +2. Creating a snapshot +3. Describing snapshot +4. Copying snapshot (same region) +5. Listing snapshots + +## Resources created + +- Snapshot +- Volume + +The script prompts you to clean up resources when it finishes. + +## Cost + +Free tier eligible for most operations. Clean up resources after use to avoid charges. + +## Related docs + +- [AWS CLI ec2 reference](https://docs.aws.amazon.com/cli/latest/reference/ec2/index.html) + diff --git a/tuts/155-ec2-snapshots/ec2-snapshots.md b/tuts/155-ec2-snapshots/ec2-snapshots.md new file mode 100644 index 00000000..68bf8924 --- /dev/null +++ b/tuts/155-ec2-snapshots/ec2-snapshots.md @@ -0,0 +1,31 @@ +# Ec2 Snapshots + +## Prerequisites + +1. AWS CLI installed and configured (`aws configure`) +2. Appropriate IAM permissions for the AWS services used + +## Step 1: Creating a volume + +The script handles this step automatically. See `ec2-snapshots.sh` for the exact CLI commands. + +## Step 2: Creating a snapshot + +The script handles this step automatically. See `ec2-snapshots.sh` for the exact CLI commands. + +## Step 3: Describing snapshot + +The script handles this step automatically. See `ec2-snapshots.sh` for the exact CLI commands. + +## Step 4: Copying snapshot (same region) + +The script handles this step automatically. See `ec2-snapshots.sh` for the exact CLI commands. + +## Step 5: Listing snapshots + +The script handles this step automatically. See `ec2-snapshots.sh` for the exact CLI commands. + +## Cleanup + +The script prompts you to clean up all created resources. If you need to clean up manually, check the script log for the resource names that were created. +