Files
kodekloud-engineer/aws-47.md

14 KiB

Task 47

The Nautilus DevOps team needs to implement priority queuing using Amazon SQS and SNS. The goal is to create a system where messages with different priorities are handled accordingly. You are required to use AWS CloudFormation to deploy the necessary resources in your AWS account. The CloudFormation template should be created on the AWS client host at /root/datacenter-priority-stack.yml, the stack name must be datacenter-priority-stack and it should create the following resources:

Two SQS queues named datacenter-High-Priority-Queue and datacenter-Low-Priority-Queue. An SNS topic named datacenter-Priority-Queues-Topic. A Lambda function named datacenter-priorities-queue-function that will consume messages from the SQS queues. The Lambda function code is provided in /root/index.py on the AWS client host. An IAM role named lambda_execution_role that provides the necessary permissions for the Lambda function to interact with SQS and SNS. Once the stack is deployed, to test the same you can publish messages to the SNS topic, invoke the Lambda function and observe the order in which they are processed by the Lambda function. The high-priority message must be processed first.

topicarn=$(aws sns list-topics --query "Topics[?contains(TopicArn, 'datacenter-Priority-Queues-Topic')].TopicArn" --output text)

aws sns publish --topic-arn $topicarn --message 'High Priority message 1' --message-attributes '{"priority" : { "DataType":"String", "StringValue":"high"}}'

aws sns publish --topic-arn $topicarn --message 'High Priority message 2' --message-attributes '{"priority" : { "DataType":"String", "StringValue":"high"}}'

aws sns publish --topic-arn $topicarn --message 'Low Priority message 1' --message-attributes '{"priority" : { "DataType":"String", "StringValue":"low"}}'

aws sns publish --topic-arn $topicarn --message 'Low Priority message 2' --message-attributes '{"priority" : { "DataType":"String", "StringValue":"low"}}'

Solution

SQS/SNS Priority Queue via CloudFormation Task (datacenter) — KodeKloud-Constrained

A CloudFormation task — deploy SNS → (filtered fan-out) → two SQS queues, plus a Lambda that drains high-priority first. The priority logic is: SNS message-attribute filter policies route priority=high messages to the high queue and priority=low to the low queue; the Lambda (provided index.py) reads the high queue before the low queue on invocation.

⚠️ KodeKloud + CloudFormation caveat (HIGHLIGHT): KodeKloud's general FAQ warns that arbitrary CloudFormation/Terraform "will almost certainly not work due to IAM restrictions." This task explicitly requires CloudFormation, which means the lab has curated the IAM permissions for this specific scenario. So it works here — but it's why the IAM role below uses managed policies, not inline (inline PutRolePolicy is the restricted path). Don't add custom/inline policies to the role.

Where KodeKloud limits are specifically applied

Limit Where it lands in this task Highlighted below
Lambda timeout ≤ 10s Timeout: 10 on the function ⚠️ HIGHLIGHT
Lambda memory ≤ 256 MB MemorySize: 256 on the function ⚠️ HIGHLIGHT
IAM: managed policies only, no inline/custom Role uses ManagedPolicyArns ⚠️ HIGHLIGHT
IAM: can't exceed login user's scope AWS-managed policies (SQS/SNS/Logs Full) are within scope ⚠️ HIGHLIGHT
Lambda config set at create, not updated later All props set in the template ⚠️ HIGHLIGHT
SQS/SNS "basic operations only" Standard queues, standard topic — nothing advanced (implicit)

Step 1 — Write the template (everything except the embedded code)

The Lambda code from /root/index.py gets injected into the template (inline ZipFile) in Step 2, so here we write the template up to the ZipFile: | line, with the Lambda as the last resource and Code its last property.

cat > /root/datacenter-priority-stack.yml << 'YAML'
AWSTemplateFormatVersion: '2010-09-09'
Description: Priority queuing with SNS filter-policy fan-out to SQS + Lambda consumer

Resources:

  HighPriorityQueue:
    Type: AWS::SQS::Queue
    Properties:
      QueueName: datacenter-High-Priority-Queue

  LowPriorityQueue:
    Type: AWS::SQS::Queue
    Properties:
      QueueName: datacenter-Low-Priority-Queue

  PriorityTopic:
    Type: AWS::SNS::Topic
    Properties:
      TopicName: datacenter-Priority-Queues-Topic

  HighSubscription:
    Type: AWS::SNS::Subscription
    Properties:
      TopicArn: !Ref PriorityTopic
      Protocol: sqs
      Endpoint: !GetAtt HighPriorityQueue.Arn
      RawMessageDelivery: true
      FilterPolicy:
        priority:
          - high

  LowSubscription:
    Type: AWS::SNS::Subscription
    Properties:
      TopicArn: !Ref PriorityTopic
      Protocol: sqs
      Endpoint: !GetAtt LowPriorityQueue.Arn
      RawMessageDelivery: true
      FilterPolicy:
        priority:
          - low

  # SPLIT: one QueuePolicy per queue — SQS requires exactly one resource per statement
  HighQueuePolicy:
    Type: AWS::SQS::QueuePolicy
    Properties:
      Queues:
        - !Ref HighPriorityQueue
      PolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: Allow
            Principal: "*"
            Action: sqs:SendMessage
            Resource: !GetAtt HighPriorityQueue.Arn
            Condition:
              ArnEquals:
                aws:SourceArn: !Ref PriorityTopic

  LowQueuePolicy:
    Type: AWS::SQS::QueuePolicy
    Properties:
      Queues:
        - !Ref LowPriorityQueue
      PolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: Allow
            Principal: "*"
            Action: sqs:SendMessage
            Resource: !GetAtt LowPriorityQueue.Arn
            Condition:
              ArnEquals:
                aws:SourceArn: !Ref PriorityTopic

  LambdaExecutionRole:
    Type: AWS::IAM::Role
    Properties:
      RoleName: lambda_execution_role
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: Allow
            Principal:
              Service: lambda.amazonaws.com
            Action: sts:AssumeRole
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/AmazonSQSFullAccess
        - arn:aws:iam::aws:policy/AmazonSNSFullAccess
        - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole

  PriorityLambda:
    Type: AWS::Lambda::Function
    Properties:
      FunctionName: datacenter-priorities-queue-function
      Handler: index.lambda_handler
      Runtime: python3.13
      Role: !GetAtt LambdaExecutionRole.Arn
      Timeout: 10
      MemorySize: 256
      Environment:
        Variables:
          high_priority_queue: !Ref HighPriorityQueue
          low_priority_queue: !Ref LowPriorityQueue
      Code:
        ZipFile: |
YAML

⚠️ HIGHLIGHTED limit applications in that template:

  • Timeout: 10 — KodeKloud auto-resets Lambda timeouts > 10s to 3s and suspends the session for > 30s. 10 is the ceiling. This is a hard limit you must set here.
  • MemorySize: 256 — the lab cap; > 256 gets reset to 128. Set it explicitly.
  • ManagedPolicyArns instead of inline Policies — the lab restricts custom/inline policy creation (your PutRolePolicy denial earlier). Attaching AWS-managed policies is permitted because they don't exceed your login user's scope. AmazonSQSFullAccess + AmazonSNSFullAccess + AWSLambdaBasicExecutionRole cover "interact with SQS and SNS" + logging.
  • All function config is set at create time in the template — KodeKloud blocks post-hoc update-function-configuration, so getting it right in the stack matters.

Step 2 — Inject /root/index.py into the template (indented under ZipFile: |)

The inline ZipFile block scalar needs each code line indented to 10 spaces (deeper than the ZipFile: | at 8). sed handles it; appending is all that's needed since the Lambda is the last resource.

sed 's/^/          /' /root/index.py >> /root/datacenter-priority-stack.yml

# Sanity-check the tail of the template shows your indented code
tail -20 /root/datacenter-priority-stack.yml

Two things to verify:

  • Handler match: the template uses Handler: index.lambda_handler. If the function inside /root/index.py is named something other than lambda_handler, change the handler to index.<actual_function_name>. Check with grep '^def ' /root/index.py.
  • Inline code size limit: CloudFormation inline ZipFile caps at ~4096 bytes. wc -c /root/index.py — if it's over 4 KB, you can't inline it; you'd zip it to S3 and use Code: {S3Bucket, S3Key} instead. A simple priority processor is almost always well under 4 KB.

Step 3 — Deploy the stack

REGION=us-east-1

aws cloudformation deploy \
  --template-file /root/datacenter-priority-stack.yml \
  --stack-name datacenter-priority-stack \
  --capabilities CAPABILITY_NAMED_IAM \
  --region $REGION

⚠️ --capabilities CAPABILITY_NAMED_IAM is mandatory — the stack creates an IAM role with an explicit name (lambda_execution_role). CloudFormation refuses to create named IAM resources without this acknowledgment (CAPABILITY_IAM alone isn't enough for named ones). Omit it and the deploy fails with an InsufficientCapabilities error.

Step 4 — Test (publish to SNS, invoke Lambda)

topicarn=$(aws sns list-topics \
  --query "Topics[?contains(TopicArn, 'datacenter-Priority-Queues-Topic')].TopicArn" \
  --output text)

aws sns publish --topic-arn $topicarn --message 'High Priority message 1' \
  --message-attributes '{"priority":{"DataType":"String","StringValue":"high"}}'
aws sns publish --topic-arn $topicarn --message 'High Priority message 2' \
  --message-attributes '{"priority":{"DataType":"String","StringValue":"high"}}'
aws sns publish --topic-arn $topicarn --message 'Low Priority message 1' \
  --message-attributes '{"priority":{"DataType":"String","StringValue":"low"}}'
aws sns publish --topic-arn $topicarn --message 'Low Priority message 2' \
  --message-attributes '{"priority":{"DataType":"String","StringValue":"low"}}'

# Give SNS a couple seconds to fan out to the queues, then invoke the Lambda
sleep 5
aws lambda invoke --function-name datacenter-priorities-queue-function \
  --region $REGION /tmp/out.json
cat /tmp/out.json

⚠️ Don't loop the invoke — KodeKloud deletes any Lambda invoked > 300 times in an hour. A handful of test invokes is fine; don't script a hammer loop.

The priority message attribute is what the SNS filter policy keys on: high messages match HighSubscription's filter and land in the high queue; low in the low queue. The Lambda reads high first, so high-priority messages process ahead of low — the task's success condition.

Verify

# Stack created cleanly
aws cloudformation describe-stacks --stack-name datacenter-priority-stack \
  --region $REGION --query 'Stacks[0].StackStatus'

# All resources present
aws cloudformation describe-stack-resources --stack-name datacenter-priority-stack \
  --region $REGION --query 'StackResources[].{Type:ResourceType,Status:ResourceStatus,Id:PhysicalResourceId}' \
  --output table

# Messages fanned out to the queues (before Lambda drains them)
for Q in datacenter-High-Priority-Queue datacenter-Low-Priority-Queue; do
  URL=$(aws sqs get-queue-url --queue-name $Q --region $REGION --query QueueUrl --output text)
  echo "$Q:"; aws sqs get-queue-attributes --queue-url $URL \
    --attribute-names ApproximateNumberOfMessages --region $REGION \
    --query 'Attributes.ApproximateNumberOfMessages' --output text
done

# Lambda processing order — check its logs
aws logs tail /aws/lambda/datacenter-priorities-queue-function --region $REGION --since 5m

Want: stack CREATE_COMPLETE, all resources CREATE_COMPLETE, messages present in both queues after publish, and the Lambda logs showing high-priority messages processed before low. That ordering in the logs is the task's proof.

Debug order

  1. Deploy fails InsufficientCapabilities → missing --capabilities CAPABILITY_NAMED_IAM.
  2. Deploy fails on the IAM role (AccessDenied/not authorized) → the lab didn't grant role creation for this scenario, or you left inline Policies in the template. Confirm you're using ManagedPolicyArns only. If it still fails, the role may be pre-created — remove the role resource and reference the existing lambda_execution_role ARN in the function's Role.
  3. Messages don't reach the queues → SNS filter policy attribute mismatch (must be priority), or the QueuePolicy didn't allow SNS SendMessage. Also RawMessageDelivery: true matters so the SQS message body is the raw message, not SNS-wrapped JSON — check what index.py expects.
  4. Lambda errors on invokeImportModuleError (handler/filename mismatch — must be index.lambda_handler for index.py), or AccessDenied (role missing SQS/SNS perms — the managed policies cover it).
  5. Inline code rejected at deployindex.py > 4096 bytes; switch to S3-packaged Code.

Full KodeKloud compliance checklist

Constraint Applied
Region us-east-1
Lambda timeout ≤ 10s ⚠️ Timeout: 10
Lambda memory ≤ 256 MB ⚠️ MemorySize: 256
IAM managed policies (no inline/custom) ⚠️ ManagedPolicyArns
IAM within login-user scope ⚠️ AWS-managed policies
Config at create, not updated later ⚠️ all in template
SQS/SNS basic ops only (standard) standard queue/topic
≤ 300 Lambda invocations/hr ⚠️ single test invoke
CFN named-IAM capability CAPABILITY_NAMED_IAM