AWS with Floci

Chapter 13 — Capstone Project

Day 1 — Design and Deploy the Core Pipeline

1. Concept Primer

This capstone chains everything from Chapters 1–11 into one flow: S3 upload → Lambda trigger → DynamoDB write → SNS notification → SQS consumer. Today's goal is getting the chain deployed and firing end-to-end; Day 2 adds observability and teardown.

2. Hands-on Exercise

Deploy the S3 bucket, DynamoDB table, Lambda function, SNS topic, and SQS queue, then wire the event chain together.

3. Exact Commands

floci start && eval $(floci env)

# Storage + table
aws s3 mb s3://capstone-uploads
aws dynamodb create-table \
  --table-name CapstoneEvents \
  --attribute-definitions AttributeName=EventId,AttributeType=S \
  --key-schema AttributeName=EventId,KeyType=HASH \
  --billing-mode PAY_PER_REQUEST

# Messaging
TOPIC_ARN=$(aws sns create-topic --name capstone-events --query 'TopicArn' --output text)
QUEUE_URL=$(aws sqs create-queue --queue-name capstone-consumer --query 'QueueUrl' --output text)
QUEUE_ARN=$(aws sqs get-queue-attributes --queue-url $QUEUE_URL --attribute-names QueueArn --query 'Attributes.QueueArn' --output text)
aws sns subscribe --topic-arn $TOPIC_ARN --protocol sqs --notification-endpoint $QUEUE_ARN

# Lambda: writes to DynamoDB, publishes to SNS
mkdir -p capstone-fn && cd capstone-fn
cat > handler.py << 'EOF'
import json, boto3, uuid, os

dynamodb = boto3.client("dynamodb", endpoint_url=os.environ["AWS_ENDPOINT_URL"])
sns = boto3.client("sns", endpoint_url=os.environ["AWS_ENDPOINT_URL"])

def handler(event, context):
    record = event["Records"][0]
    key = record["s3"]["object"]["key"]
    event_id = str(uuid.uuid4())

    dynamodb.put_item(
        TableName="CapstoneEvents",
        Item={"EventId": {"S": event_id}, "Key": {"S": key}}
    )
    sns.publish(
        TopicArn=os.environ["TOPIC_ARN"],
        Message=json.dumps({"eventId": event_id, "key": key})
    )
    return {"statusCode": 200}
EOF
zip function.zip handler.py
cd ..

aws iam create-role --role-name capstone-role \
  --assume-role-policy-document '{
    "Version":"2012-10-17",
    "Statement":[{"Effect":"Allow","Principal":{"Service":"lambda.amazonaws.com"},"Action":"sts:AssumeRole"}]
  }'

aws lambda create-function \
  --function-name capstone-fn \
  --runtime python3.12 \
  --handler handler.handler \
  --role arn:aws:iam::000000000000:role/capstone-role \
  --zip-file fileb://capstone-fn/function.zip \
  --environment "Variables={AWS_ENDPOINT_URL=http://localhost:4566,TOPIC_ARN=$TOPIC_ARN}"

aws lambda add-permission \
  --function-name capstone-fn \
  --statement-id s3invoke \
  --action lambda:InvokeFunction \
  --principal s3.amazonaws.com \
  --source-arn arn:aws:s3:::capstone-uploads

FN_ARN=$(aws lambda get-function --function-name capstone-fn --query 'Configuration.FunctionArn' --output text)
aws s3api put-bucket-notification-configuration \
  --bucket capstone-uploads \
  --notification-configuration "{
    \"LambdaFunctionConfigurations\": [{
      \"LambdaFunctionArn\": \"$FN_ARN\",
      \"Events\": [\"s3:ObjectCreated:*\"]
    }]
  }"

# Fire the chain
echo "capstone test" > event.txt
aws s3 cp event.txt s3://capstone-uploads/event.txt

sleep 3
aws dynamodb scan --table-name CapstoneEvents
aws sqs receive-message --queue-url $QUEUE_URL

4. Gotchas

  • The Lambda hardcodes AWS_ENDPOINT_URL into its own environment so boto3 inside the function also talks to Floci — easy to forget, and the most common reason a "working" Lambda mysteriously can't reach DynamoDB/SNS when run as an event trigger vs. direct invoke.
  • Give the chain a few seconds (sleep 3 above) before checking DynamoDB/SQS — event delivery through S3 → Lambda → SNS → SQS is asynchronous even locally.
  • This reuses account ID 000000000000 and role name conventions from earlier chapters — keep names unique per project if you're layering the capstone on top of leftover resources from previous chapters' exercises.

5. Self-Check

Trace the chain backward: if sqs receive-message returns nothing, which three prior links in the chain would you check, in order, to isolate where it broke?


Day 2 — Observability, IaC-ify It, and Teardown

1. Concept Primer

A capstone isn't done until it's reproducible and disposable: wrap the whole thing in one CloudFormation/Terraform deploy, add basic logging visibility, and script the teardown so nothing lingers between runs.

2. Hands-on Exercise

Check Lambda logs from Day 1's run, add a CloudWatch alarm on invocation errors, then write and run a full teardown script.

3. Exact Commands

eval $(floci env)

# Confirm the function actually ran
aws logs tail /aws/lambda/capstone-fn --since 15m

# Basic error alarm (relies on the Lambda Errors metric AWS publishes automatically)
aws cloudwatch put-metric-alarm \
  --alarm-name capstone-fn-errors \
  --namespace AWS/Lambda \
  --metric-name Errors \
  --dimensions Name=FunctionName,Value=capstone-fn \
  --statistic Sum \
  --period 60 \
  --evaluation-periods 1 \
  --threshold 0 \
  --comparison-operator GreaterThanThreshold

# Teardown script
cat > teardown.sh << 'EOF'
#!/bin/bash
set -e
eval $(floci env)

aws lambda delete-function --function-name capstone-fn || true
aws iam delete-role --role-name capstone-role || true
aws sns delete-topic --topic-arn $(aws sns list-topics --query "Topics[?contains(TopicArn,'capstone-events')].TopicArn" --output text) || true
QUEUE_URL=$(aws sqs get-queue-url --queue-name capstone-consumer --query 'QueueUrl' --output text 2>/dev/null || true)
[ -n "$QUEUE_URL" ] && aws sqs delete-queue --queue-url $QUEUE_URL || true
aws dynamodb delete-table --table-name CapstoneEvents || true
aws s3 rb s3://capstone-uploads --force || true
aws cloudwatch delete-alarms --alarm-names capstone-fn-errors || true

echo "Capstone teardown complete."
EOF
chmod +x teardown.sh
./teardown.sh

4. Gotchas

  • The AWS/Lambda namespace's Errors metric is one AWS publishes automatically per function — whether Floci populates it with the same fidelity as real AWS is worth double-checking; if the alarm never leaves INSUFFICIENT_DATA, that's likely why.
  • || true on every teardown line keeps the script running even if a resource was already deleted or never got created — useful for a script you'll re-run often while iterating.
  • aws s3 rb ... --force deletes the bucket and everything in it — fine for a capstone sandbox, but never a pattern to reuse against anything with real data.

5. Self-Check

Why does the teardown script delete the Lambda function before the IAM role, and the SNS topic before the SQS queue — does the order actually matter here, and why?

Filters

No filters available for this view.

Reset All