## Task 48 The Nautilus DevOps team needs to implement a Lambda function using a CloudFormation stack. Create a CloudFormation template named /root/devops-lambda.yml on the AWS client host and configure it to create the following components. The stack name must be devops-lambda-app. Create a Lambda function named devops-lambda. Use the Runtime Python. The function should print the body Welcome to KKE AWS Labs!. Ensure the status code is 200. Create and use the IAM role named lambda_execution_role. ### Solution # Lambda via CloudFormation Task (devops-lambda-app) — KodeKloud-Constrained A simple single-Lambda CloudFormation stack. The only real substance is the two-resource template (role + function) with inline code, plus the KodeKloud limits baked in from the start. ## KodeKloud limits applied to this task | Limit | Where | Why | |---|---|---| | **Lambda timeout ≤ 10s** | `Timeout: 10` | > 10s auto-reset to 3s; > 30s suspends session | | **Lambda memory ≤ 256 MB** | `MemorySize: 256` | > 256 auto-reset to 128 | | **Config set at CREATE time** | all in the template | post-hoc Lambda config updates are blocked | | **IAM: managed policies only** | role uses `ManagedPolicyArns` | inline/custom policy creation is denied | | **IAM within login scope** | `AWSLambdaBasicExecutionRole` (AWS-managed) | can't exceed the `kk_labs_user` scope | | **Region us-east-1** | deploy `--region us-east-1` | only allowed region | | **CFN named-IAM capability** | `CAPABILITY_NAMED_IAM` | stack creates a *named* IAM role | ## Step 1 — Write the template ```bash cat > /root/devops-lambda.yml << 'YAML' AWSTemplateFormatVersion: '2010-09-09' Description: devops-lambda function with execution role Resources: # IAM role — MANAGED policy only (KodeKloud-safe), named as the task requires 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/service-role/AWSLambdaBasicExecutionRole # Lambda function — inline code, KodeKloud timeout/memory caps DevopsLambda: Type: AWS::Lambda::Function Properties: FunctionName: devops-lambda Handler: index.lambda_handler Runtime: python3.13 Role: !GetAtt LambdaExecutionRole.Arn Timeout: 10 MemorySize: 256 Code: ZipFile: | def lambda_handler(event, context): return { 'statusCode': 200, 'body': 'Welcome to KKE AWS Labs!' } Outputs: FunctionName: Value: !Ref DevopsLambda RoleArn: Value: !GetAtt LambdaExecutionRole.Arn YAML ``` Every requirement mapped: - **`FunctionName: devops-lambda`** — req 1. - **`Runtime: python3.13`** — req 2 ("Python"). 3.13 is current/supported; 3.14 is the newest if the grader wants absolute latest, but 3.13 is the safe pick. (Avoid 3.10/3.11 — AL2 deprecation track.) - **The inline handler returns `statusCode: 200` + `body: 'Welcome to KKE AWS Labs!'`** — reqs 3 & 4. "Print the body" maps to the returned `body` field (the invocation response payload), exactly matching the earlier console-Lambda task. - **`LambdaExecutionRole` named `lambda_execution_role`** with `AWSLambdaBasicExecutionRole` attached — req 5. That managed policy grants CloudWatch Logs write (all a bare Lambda needs), and being AWS-managed it complies with the KodeKloud "no inline/custom, stay within login scope" rule. - **`Handler: index.lambda_handler`** — CloudFormation's inline `ZipFile` is written to a file named `index.py` internally, so the handler is `index.`. The function is `lambda_handler`, hence `index.lambda_handler`. (No hyphen problem here — inline code, not an uploaded `lambda-function.py`.) - **`Timeout: 10` / `MemorySize: 256`** — KodeKloud caps, set at create. ## Step 2 — Deploy ```bash REGION=us-east-1 aws cloudformation deploy \ --template-file /root/devops-lambda.yml \ --stack-name devops-lambda-app \ --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`). Without it, deploy fails `InsufficientCapabilities`. ## Step 3 — Verify ```bash # Stack created cleanly aws cloudformation describe-stacks --stack-name devops-lambda-app \ --region $REGION --query 'Stacks[0].StackStatus' # Function exists with correct config aws lambda get-function-configuration --function-name devops-lambda \ --region $REGION \ --query '{Name:FunctionName,Runtime:Runtime,Timeout:Timeout,Memory:MemorySize,Role:Role}' # Invoke and confirm the response body + status code aws lambda invoke --function-name devops-lambda --region $REGION /tmp/out.json cat /tmp/out.json ``` Want: stack `CREATE_COMPLETE`; the function showing `python3.13`, `Timeout: 10`, `Memory: 256`, role ending `lambda_execution_role`; and the invoke returning `{"statusCode": 200, "body": "Welcome to KKE AWS Labs!"}`. That payload is the task's proof. ## Debug order 1. **Deploy `InsufficientCapabilities`** → add `--capabilities CAPABILITY_NAMED_IAM`. 2. **Stack `ROLLBACK_COMPLETE` on the role** → the lab may pre-create `lambda_execution_role`, causing a name collision (`already exists`). Check `aws iam get-role --role-name lambda_execution_role`; if it exists, remove the `LambdaExecutionRole` resource from the template and set the function's `Role` to that role's ARN directly. Then — since `ROLLBACK_COMPLETE` is terminal — `aws cloudformation delete-stack --stack-name devops-lambda-app && aws cloudformation wait stack-delete-complete ...` before redeploying. 3. **Timeout became 3s / memory 128** → you exceeded the caps somewhere; confirm `Timeout: 10` and `MemorySize: 256`. 4. **Invoke returns wrong body** → typo in the inline string; must be exactly `Welcome to KKE AWS Labs!`. ```