Run code without a server: an AWS Lambda function, invoked and logged

short · 25 min · Task statement 3.3

Task

Create a small AWS Lambda function in Python, give it the narrowest role it needs, invoke it with an event you write yourself, and read its log in Amazon CloudWatch Logs. Along the way, look at the container services from the lesson (Amazon ECS, Amazon EKS and Amazon ECR) with read-only commands, so you can see that nothing exists there until you create it. At the end you can show what serverless leaves to you, the code, its permissions and its data, and what it takes away: there is no instance, operating system or host to choose, patch or scale, and nothing to pay for while the function sits idle.

Steps

  1. Confirm who you are signed in as and which Region you are in:
aws sts get-caller-identity --query Arn --output text
echo $AWS_REGION

The ARN should name an IAM Identity Center role (it contains AWSReservedSSO) or an IAM user. If it ends in :root, stop, sign out, and sign back in as your administrative identity. 2. Look at the container services the lesson describes. These commands only read, and cost nothing:

aws ecs list-clusters
aws eks list-clusters
aws ecr describe-repositories

In an account that has never used them, each prints an empty list. ECR is where container images would be stored, ECS and EKS are the two orchestrators, and AWS Fargate would run their tasks without instances. Lambda, which you use next, needs none of them. 3. Write the function's code and package it as a zip file. The text it returns is invented sample text:

cat > lambda_function.py <<'EOF'
import json

def lambda_handler(event, context):
    name = event.get("name", "world")
    print("Received event for " + name)
    return {"message": "Hello, " + name + ", from AWS Lambda"}
EOF
zip function.zip lambda_function.py
  1. Create the role the function runs as. The trust policy lets only the Lambda service assume it, and the AWS managed policy AWSLambdaBasicExecutionRole allows nothing except writing logs:
cat > trust.json <<'EOF'
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {"Service": "lambda.amazonaws.com"},
      "Action": "sts:AssumeRole"
    }
  ]
}
EOF
aws iam create-role --role-name clf-lab15-lambda-role --assume-role-policy-document file://trust.json
aws iam attach-role-policy --role-name clf-lab15-lambda-role --policy-arn arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole

These IAM permissions are your side of the shared responsibility model: AWS runs the servers, but what the function may touch is yours to decide. 5. Create the function, then wait until it is ready:

ROLE_ARN=$(aws iam get-role --role-name clf-lab15-lambda-role --query 'Role.Arn' --output text)
echo $ROLE_ARN
aws lambda create-function --function-name clf-lab15-hello --runtime python3.13 --handler lambda_function.lambda_handler --role $ROLE_ARN --zip-file fileb://function.zip
aws lambda wait function-active-v2 --function-name clf-lab15-hello

The ARN looks like arn:aws:iam::111122223333:role/clf-lab15-lambda-role, with your own account ID. If create-function reports that the role cannot be assumed by Lambda, the new role has not reached every part of IAM yet: wait ten seconds and run the create-function line again. Notice what you did not choose: an instance type, an operating system or a number of servers. 6. Invoke the function with an event of your own, then read the reply:

aws lambda invoke --function-name clf-lab15-hello --cli-binary-format raw-in-base64-out --payload '{"name": "CloudShell"}' response.json
cat response.json

The first command prints "StatusCode": 200. The file holds {"message": "Hello, CloudShell, from AWS Lambda"}. In a real system the event would come from an S3 upload, an API Gateway request, a queue or a schedule rather than from you. 7. Read the log Lambda wrote, and the few settings the function has:

aws logs tail /aws/lambda/clf-lab15-hello --since 15m
aws lambda get-function-configuration --function-name clf-lab15-hello --query '[Runtime,MemorySize,Timeout]' --output text

The log shows your Received event for CloudShell line between START and END, and a REPORT line with the billed duration: Lambda charges for requests and compute time, not for idle time. If the log group is not there yet, wait a minute and run the first command again. The second command prints python3.13 128 3: a runtime, a memory size and a timeout in seconds are all you manage.

Verify

Run these three read-only checks:

aws lambda get-function-configuration --function-name clf-lab15-hello --query 'State' --output text

Expected output: Active.

aws iam list-attached-role-policies --role-name clf-lab15-lambda-role --query 'length(AttachedPolicies)'

Expected output: 1, the basic execution policy and nothing else.

aws logs describe-log-groups --log-group-name-prefix /aws/lambda/clf-lab15-hello --query 'length(logGroups)'

Expected output: 1, which proves the function ran and wrote its log.

Cost and clean-up

Lambda requests and compute time, and CloudWatch Logs storage, may be charged outside the AWS Free Tier; for one function invoked a few times the amount is very small, but it is not guaranteed to be zero. A log group keeps its logs forever unless you delete it, which is why the clean-up does. See the AWS Lambda and Amazon CloudWatch pricing pages, or the AWS Pricing Calculator, for current rates. IAM roles are free.

Created by this lab: the Lambda function clf-lab15-hello; the CloudWatch Logs log group /aws/lambda/clf-lab15-hello, made by Lambda on the first invocation; the IAM role clf-lab15-lambda-role, with the AWS managed policy AWSLambdaBasicExecutionRole attached; the working files lambda_function.py, function.zip, trust.json and response.json in your CloudShell home directory.

  1. Delete the function:
aws lambda delete-function --function-name clf-lab15-hello
  1. Delete its log group, which Lambda does not remove for you:
aws logs delete-log-group --log-group-name /aws/lambda/clf-lab15-hello
  1. Detach the policy AWSLambdaBasicExecutionRole from the role, then delete the role clf-lab15-lambda-role. A role cannot be deleted while a policy is attached:
aws iam detach-role-policy --role-name clf-lab15-lambda-role --policy-arn arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
aws iam delete-role --role-name clf-lab15-lambda-role
  1. Remove the working files lambda_function.py, function.zip, trust.json and response.json from CloudShell:
rm -f lambda_function.py function.zip trust.json response.json

Check it is gone

aws lambda list-functions --query "Functions[?FunctionName=='clf-lab15-hello'] | length(@)"

Expected output: 0.

aws logs describe-log-groups --log-group-name-prefix /aws/lambda/clf-lab15-hello --query 'length(logGroups)'

Expected output: 0.

aws iam list-roles --query "Roles[?RoleName=='clf-lab15-lambda-role'] | length(@)"

Expected output: 0.

Notes

Where this sits in the lesson's list, from most managed by you to least: Amazon EC2, then containers on EC2 with ECS or EKS, then containers on Fargate, then Lambda. This lab sits at the bottom. A container lab would need an image in ECR and a cluster, and an EKS cluster is charged from the moment it exists, so the container services stay read-only here. Lambda has a maximum run time for each invocation, set by AWS and listed on the Lambda quotas page, which is why a process that must run for days belongs on EC2 or in containers instead.

This is an independent study companion for the AWS Certified Cloud Practitioner (CLF-C02) exam and is not produced by or endorsed by Amazon Web Services. AWS, Amazon Web Services and AWS Certified Cloud Practitioner are trademarks of Amazon.com, Inc. or its affiliates.

This is an independent study companion for AWS Certified Cloud Practitioner CLF-C02 and is not produced by or endorsed by CompTIA.