Lambda — DVA-C02 Mastery Guide
- Sources: course chapter 14 (AWS Lambda), used as the syllabus only; all wording here is original. Written 2026-10-12.
- Values checked against AWS documentation on 2026-10-12. The marker
⚠️ Changed: <current>. Older material may say <old>.flags a value or behaviour that differs from the course or older practice tests: learn the current value.⚠️ unverifiedmarks a value that could not be confirmed: never rely on it in a question. Every change is listed in Changed vs older material; confirmed rows also live innotes/current-facts.md.- Tier 1 (highest exam weight), bank target 40 questions. Domains: Development with AWS Services (primary), Troubleshooting and Optimization, Deployment.
- How to use it:
/explain <topic>teaches from these sections (concept → exam facts → developer angle → traps → 3 check questions);/quiz lambdaasks bank questions whosesource_notelinks point at the## N.headings below;/drill lambdafires short recall prompts fromnotes/lambda-cheat-sheet.md. Suggested loop: read a section and answer its practice questions before opening the answers →/explainwhat is still unclear →/quiz lambda→/drill lambda.
How Lambda appears on DVA-C02
Lambda is the default compute service of the exam ("Develop code for AWS Lambda" is a task statement of its own). Questions rarely ask what Lambda is; they ask what a developer must configure so that a function is called the right way, fails safely, scales without hurting other systems and ships without downtime:
- Development: invocation types, event and context objects, event source mappings, destinations and dead-letter queues, environment variables and secrets, layers and packaging, VPC access, function URLs.
- Troubleshooting and Optimization: decoding
429,502and timeout symptoms, throttles versus errors, a risingIteratorAge, cold starts, memory and timeout tuning, missing logs or traces. - Deployment: versions, aliases and weighted traffic shifting, container images, code in CloudFormation, code storage and package limits.
Typical question shapes: a scenario with one deciding constraint; an error code or metric to diagnose; "Which TWO actions…" with five options; the exact setting, API action, CLI flag or condition key that does the job. Deciding constraints map to Lambda features: LEAST operational overhead → destinations, partial batch responses, filters and managed retries instead of custom code; LOWEST latency → provisioned concurrency or SnapStart; MOST secure → least-privilege execution role, Secrets Manager instead of environment variables, AWS_IAM function URLs; fewest code changes → a configuration setting such as a batching window, reserved concurrency or an alias weight.
Domain map: Development → §1–§4, §8–§11 · Troubleshooting and Optimization → §5, §6, §12, §13 · Deployment → §7, §8 · Security angles → §4, §9, §11.
Out of scope here (owned by other modules): API Gateway integrations, authorizers and integration timeouts (see notes/api-gateway-guide.md); Lambda@Edge and CloudFront Functions (see notes/cloudfront-guide.md); S3 event notification behaviour (see notes/s3-guide.md); SQS, SNS and Kinesis service settings (see notes/sqs-sns-kinesis-guide.md); DynamoDB Streams design (see notes/dynamodb-guide.md); EventBridge rules, X-Ray sampling rules and CloudWatch Logs Insights (see notes/monitoring-guide.md); SAM templates and CodeDeploy deployment groups (see notes/sam-guide.md and notes/cicd-guide.md); KMS, Secrets Manager and Parameter Store internals (see notes/security-encryption-guide.md); IAM policy evaluation and PassRole depth (see notes/advanced-identity-guide.md); NAT gateways, endpoints and routing (see notes/vpc-guide.md); Step Functions (see notes/other-services-guide.md); RDS Proxy (see notes/rds-guide.md); ALB target groups in depth (see notes/ec2-mastery-guide.md §8); EFS mechanics (see notes/ec2-mastery-guide.md §7). No OS administration or network design.
| # | Topic | Why it is on the exam |
|---|---|---|
| 1 | Invocation types and synchronous calls | Who waits for the result decides who handles errors; 200 with FunctionError is still a failure |
| 2 | Asynchronous invocation with retries and destinations | Built-in retries, event age, destinations vs DLQs: failure handling without code |
| 3 | Event source mappings for queues and streams | SQS, Kinesis and DynamoDB Streams settings decide duplicates, stalls and scaling |
| 4 | Execution roles and resource based policies | What the function may call vs who may call the function; the classic "trigger never fires" fix |
| 5 | Concurrency scaling and throttling | Account pool, reserved concurrency and 429 handling under a stated load |
| 6 | Cold starts with provisioned concurrency and SnapStart | Picking the latency fix that fits the runtime, traffic shape and budget |
| 7 | Versions and aliases with traffic shifting | Immutable versions, movable aliases, canary and linear releases with rollback |
| 8 | Deployment packages with layers and container images | Size limits, layers, images, runtimes and code updates that do (or don't) deploy |
| 9 | Environment variables and secrets | Configuration without code changes; secrets kept out of plain variables |
| 10 | VPC access and storage options | Reaching private resources, losing internet access, /tmp vs EFS |
| 11 | Function URLs and response streaming | The cheapest HTTPS endpoint, its two-permission model, and large or streamed responses |
| 12 | Observability with logs metrics and traces | Missing logs, misleading metrics, absent X-Ray segments, recursive loops |
| 13 | Performance tuning and limits | Memory as the CPU dial, timeouts, cost math and the hard limits questions hinge on |
1. Invocation types and synchronous calls
Concept
- A function never chooses how it is called; the caller does. Lambda offers three paths:
- Synchronous (
RequestResponse): the caller waits until the handler returns and receives the result or the error. Used by the SDK/CLIInvokecall, API Gateway, Application Load Balancers, function URLs, Cognito triggers, Step Functions task states that wait for the result, Amazon Lex, S3 Batch Operations and Amazon Data Firehose transformations. - Asynchronous (
Event): Lambda places the event in an internal queue, answers immediately, and runs the function later with automatic retries (§2). Used by S3 event notifications, SNS, EventBridge rules and EventBridge Scheduler, CloudWatch Logs subscriptions, SES, AWS Config, CloudFormation custom resources, CodeCommit and CodePipeline, and IoT. - Event source mapping: Lambda itself polls a queue or stream and calls the function synchronously with a batch of records (§3).
- Synchronous (
- Every invocation hands the handler two arguments: the event, a JSON document whose shape depends on the caller, and the context, metadata about this invocation and its execution environment.
- Synchronous calls get no retry layer from Lambda: the caller sees the error and decides what happens next, so client-side backoff and idempotent handlers matter.
- Newer options sit next to this classic model: durable functions (checkpointed multi-step executions lasting up to one year) and Lambda Managed Instances (functions running on Lambda-managed EC2 capacity in your account, EC2 pricing plus a 15% management fee). Recognise the names; exam questions target the classic model.
Exam facts
InvocationType:RequestResponse(default, HTTP200),Event(asynchronous, HTTP202),DryRun(validates the parameters and the caller's permission without running any code, HTTP204).- Payload quotas (not adjustable): 6 MB request and 6 MB response for synchronous calls. ⚠️ Changed: 1 MB per asynchronous request (since 2025-10-24; one request is billed for the first 256 KB and one more per extra 64 KB). Older material may say 256 KB. Streamed responses can reach 200 MB (§11); request line plus header values are capped at 1 MB combined. Larger data → write it to S3 and pass the object key.
- A function error does not change the HTTP status:
Invokestill returns200, adds theX-Amz-Function-Errorheader (FunctionErrorin SDK responses) and putserrorType/errorMessagein the payload. Non-2xx codes mean Lambda could not run the function at all. - Invocation errors worth recognising:
TooManyRequestsException(429, throttled, §5),RequestTooLargeException(413, the request payload is over the quota of its invocation type: 6 MB synchronous or 1 MB asynchronous),ResourceNotFoundException(404, wrong name, version or alias),InvalidParameterValueException(400),ResourceConflictException(409, an update is in progress),ServiceException(500), and a 502 family that points at configuration:KMSAccessDeniedException(environment variables can't be decrypted, §9),ENILimitReachedException,SubnetIPAddressLimitReachedExceptionandEC2ThrottledException(VPC networking, §10),ResourceNotReadyException(inactive function whose VPC connection is being rebuilt). LogType=Tail(synchronous only) returns the last 4 KB of the execution log, base64-encoded, inX-Amz-Log-Result.ClientContext(up to 3,583 bytes, base64) reaches the function only on synchronous calls.X-Amz-Executed-Version(ExecutedVersion) reports which version actually ran: the way to see which side of a weighted alias served a request (§7).- Lambda never retries a synchronous call. AWS SDKs retry some throttling and server errors by themselves; API Gateway relays the error to its client; S3 Batch Operations retries when the function reports
TemporaryFailure. - Long synchronous calls need a client read timeout (or keep-alive) longer than the function timeout; otherwise the client gives up while the function may still be running.
- Context object (Python names):
aws_request_id,function_name,function_version,invoked_function_arn(shows the alias or version used),memory_limit_in_mb,log_group_name,log_stream_name,get_remaining_time_in_millis(), plusidentityandclient_contextfor mobile callers. - ALB as a caller: target type
lambda, one function per target group, same account and Region, request body and response JSON up to 1 MB each. With the target group attributelambda.multi_value_headers.enabled=true, repeated headers and query keys arrive as arrays (multiValueHeaders,multiValueQueryStringParameters); without it only the last value is passed. The load balancer needslambda:InvokeFunctionin the function's resource-based policy (elasticloadbalancing.amazonaws.com, scoped to the target group ARN). ALB status codes and health checks: see notes/ec2-mastery-guide.md §8. Other synchronous front doors: API Gateway (see notes/api-gateway-guide.md), function URLs (§11), Lambda@Edge (see notes/cloudfront-guide.md).
Developer angle
# synchronous call that also returns the tail of the log (AWS CLI v2 needs the binary-format flag for raw JSON)
aws lambda invoke --function-name orders-api:live \
--cli-binary-format raw-in-base64-out \
--payload '{"orderId": "o-1001"}' \
--log-type Tail --query LogResult --output text response.json | base64 --decode
# permission check only: nothing runs, StatusCode 204
aws lambda invoke --function-name orders-api --invocation-type DryRun response.json
import json
import boto3
lambda_client = boto3.client("lambda")
def call_orders(order_id):
resp = lambda_client.invoke(FunctionName="orders-api:live", InvocationType="RequestResponse",
Payload=json.dumps({"orderId": order_id}))
body = json.loads(resp["Payload"].read())
if "FunctionError" in resp: # StatusCode is still 200 here
raise RuntimeError(f"{body.get('errorType')}: {body.get('errorMessage')}")
return resp["ExecutedVersion"], body # which version of the alias answered
def handler(event, context): # stop cleanly before the timeout
done = []
for item in event["items"]:
if context.get_remaining_time_in_millis() < 10_000:
break # leave time to save progress
done.append(item)
return {"processed": len(done), "requestId": context.aws_request_id}
aws configure set cli-binary-format raw-in-base64-outmakes raw JSON payloads the AWS CLI v2 default. Callers needlambda:InvokeFunctionon the function, version or alias ARN: in their identity policy (same account) or the function's resource-based policy (services, other accounts, §4).
Traps
- Trap: "
StatusCode200 means the function succeeded." → Reality: function errors return 200 as well; checkFunctionError/X-Amz-Function-Error. - Trap: "Lambda retries a failed synchronous invocation twice." → Reality: the two automatic retries apply to asynchronous invocations only; synchronous callers retry themselves.
- Trap: "Ask AWS to raise the 6 MB synchronous payload limit." → Reality: it is not adjustable; pass an S3 key or stream the response (§11).
- Trap: "The ALB passes every value of a repeated query parameter." → Reality: only when multi-value headers are enabled on the target group; otherwise the last value wins.
- Trap: "A 502 from
Invokeis a bug in the handler." → Reality: 502-class Lambda errors mean configuration (KMS, VPC, runtime) stopped the function from running; handler bugs come back as 200 plusFunctionError.
Practice questions
-
A developer's batch script invokes a pricing function synchronously with the AWS SDK and treats every response with status code 200 as a success. During testing, several calls returned 200 even though the function's CloudWatch logs show an unhandled exception for those requests. How should the script detect these failures MOST reliably?
- A. Treat status code 202 as the only successful result for synchronous calls.
- B. Send each request with
InvocationTypeset toDryRunfirst so that errors surface early. - C. Check the response for the
FunctionErrorfield and read the error details from the payload. - D. Request
LogTypeTailand search the decoded log text for the wordERROR.
-
An Application Load Balancer forwards requests to a Lambda function in a target group. Clients call URLs such as
/search?tag=red&tag=blue, but the function only ever receivesblue, and responses that set two cookies reach the browser with only one. Which TWO changes fix both problems? (Select TWO.)- A. Register a second Lambda function in the same target group to handle repeated keys.
- B. Enable the
lambda.multi_value_headers.enabledattribute on the target group. - C. Switch the integration to asynchronous invocation so that the ALB queues complete requests.
- D. Increase the function's memory so that larger header maps fit into the event.
- E. Read
multiValueQueryStringParametersand return the cookies inmultiValueHeadersin the function code.
Answers
- C. A function error still returns 200; the
FunctionErrorfield (headerX-Amz-Function-Error) is the documented signal and the payload holdserrorTypeanderrorMessage. A is wrong because 202 belongs to asynchronous calls; B runs no code at all; D is fragile and limited to the last 4 KB of the log. - B, E. With the attribute enabled the ALB sends arrays in
multiValueQueryStringParametersand expectsmultiValueHeadersin the response, so the code must read and write those fields. A fails because a target group holds one function; C is impossible because the ALB always invokes synchronously; D has no effect on how the event is built.
2. Asynchronous invocation with retries and destinations
Concept
- For an asynchronous call Lambda accepts the event into an internal queue and replies
202 Acceptedat once; the caller never sees the result. A separate Lambda process reads the queue, runs the function and deals with failures for you. - Three knobs control failure handling: how many times function errors are retried, how old an event may get before it is dropped, and where a record of the outcome goes (on-success and on-failure destinations, or the older dead-letter queue).
- Delivery is at least once: an event can arrive twice even when nothing failed, because the internal queue is eventually consistent. Handlers must be idempotent, for example a conditional write keyed on an event or object ID. Destinations make chaining declarative: the success record of one function can feed an EventBridge bus or another function without any SDK call in the code.
Exam facts
- Function errors (exceptions, runtime errors, timeouts): by default 2 retries, about 1 minute before the first and 2 minutes before the second, so 3 attempts in total.
MaximumRetryAttemptsaccepts 0–2. - Throttles (429) and Lambda system errors (5xx) are not counted as attempts: the event returns to the queue and is retried for up to 6 hours, with a backoff that grows from 1 second to at most 5 minutes.
MaximumEventAgeInSecondsaccepts 60–21,600 (default and maximum 6 hours). - An event that exhausts its retries or ages out is discarded unless a destination or DLQ catches it. A long backlog can also make events expire before they ever run.
- Destinations, configurable per function, version or alias: ⚠️ Changed: on failure an SQS standard queue, SNS standard topic, S3 bucket (failure records only), Lambda function or EventBridge bus; on success the same list without S3. Older material may list only SQS, SNS, Lambda and EventBridge.
- The destination record is JSON with
requestContext(conditionisRetriesExhaustedorEventAgeExceeded;approximateInvokeCount),requestPayload,responseContextandresponsePayload. EventBridge receives it asdetailwithsourcelambdaand adetail-typeof "Lambda Function Invocation Result - Success" or "… - Failure"; S3 stores it as an object underaws/lambda/async/<function-name>/. - The execution role needs the target's write action:
sqs:SendMessage,sns:Publish,lambda:InvokeFunction,events:PutEvents, ors3:PutObjectpluss3:ListBucket(andkms:GenerateDataKeyfor an SSE-KMS bucket). A missing permission, a FIFO target or an oversized record incrementsDestinationDeliveryFailures. - Dead-letter queue (
DeadLetterConfig.TargetArn): an SQS standard queue or SNS standard topic, set at function level (published versions inherit the setting), receives only the original event plus the attributesRequestID,ErrorCodeandErrorMessage(first 1 KB). Delivery problems incrementDeadLetterErrors. AWS prefers on-failure destinations (richer record, more targets); both can be configured together, and failures then go to both. - SNS topics accept 256 KB messages by default, so a record built from a payload near the 1 MB asynchronous limit can fail delivery to an SNS destination; prefer SQS or S3 for large events (SNS limits: see notes/sqs-sns-kinesis-guide.md).
- Reserved concurrency
0on an asynchronously invoked function sends new events straight to the DLQ or on-failure destination without retries. - Metrics:
AsyncEventsReceived(events queued),AsyncEventAge(time spent waiting; grows with retries and throttles),AsyncEventsDropped(discarded without a successful run). - Scheduled (cron) jobs: EventBridge Scheduler invokes the function asynchronously with an execution role that it assumes (needs
lambda:InvokeFunction); a classic EventBridge rule uses a resource-based statement forevents.amazonaws.comor, ⚠️ Changed: since 2025-03-11, an IAM execution role on the target (RoleArnallowinglambda:InvokeFunction), and needs the statement only when no role is set. Older material may say a rule always needs the resource-based statement. Schedules and rules: see notes/monitoring-guide.md. - CodePipeline invokes the function asynchronously and waits until the code reports back with
PutJobSuccessResultorPutJobFailureResult(see notes/cicd-guide.md). S3 notification and SNS subscription settings: see notes/s3-guide.md and notes/sqs-sns-kinesis-guide.md.
Developer angle
# fire-and-forget call: the CLI prints only StatusCode 202
aws lambda invoke --function-name resize-image --invocation-type Event \
--cli-binary-format raw-in-base64-out \
--payload '{"bucket": "media-uploads", "key": "cat.jpg"}' response.json
# one retry, at most one hour in the queue, success record to EventBridge, failure record to SQS
aws lambda put-function-event-invoke-config --function-name resize-image \
--maximum-retry-attempts 1 --maximum-event-age-in-seconds 3600 \
--destination-config '{"OnSuccess":{"Destination":"arn:aws:events:eu-west-1:111122223333:event-bus/media"},"OnFailure":{"Destination":"arn:aws:sqs:eu-west-1:111122223333:resize-failures"}}'
# change one setting later without wiping the others
aws lambda update-function-event-invoke-config --function-name resize-image \
--maximum-retry-attempts 0
# legacy dead-letter queue (function level, original event only)
aws lambda update-function-configuration --function-name resize-image \
--dead-letter-config TargetArn=arn:aws:sns:eu-west-1:111122223333:resize-dlq
The execution role of resize-image needs events:PutEvents on the bus and sqs:SendMessage on the queue for those two destinations. Idempotent handler for duplicate S3 notifications (DynamoDB conditional write):
import boto3
from botocore.exceptions import ClientError
table = boto3.resource("dynamodb").Table("processed-events")
def handler(event, context):
for record in event["Records"]:
obj = record["s3"]["object"]
try: # one marker item per object version
table.put_item(Item={"pk": obj["key"] + "#" + obj.get("versionId", "null")},
ConditionExpression="attribute_not_exists(pk)")
except ClientError as err:
if err.response["Error"]["Code"] == "ConditionalCheckFailedException":
continue # duplicate delivery: already handled
raise
# ... resize the image here
put-function-event-invoke-configoverwrites the whole configuration (omitted fields are removed);update-function-event-invoke-configchanges only what you pass. Destinations and DLQs belong to asynchronous invocation. Failures of SQS-triggered functions are handled on the queue (§3).
Traps
- Trap: "Asynchronous invocations retry three times." → Reality: two retries (three attempts) for function errors; throttles and system errors are retried for up to 6 hours instead.
- Trap: "A DLQ captures the error and the function's response." → Reality: the DLQ gets the original event plus three attributes; destinations carry the full invocation record.
- Trap: "Use an SQS FIFO queue as the on-failure destination to keep order." → Reality: FIFO queues and FIFO topics are not supported (
DestinationDeliveryFailures). - Trap: "Send successful results to an S3 destination." → Reality: S3 accepts on-failure records only.
- Trap: "No errors means no duplicates." → Reality: the queue is eventually consistent; design for repeated events.
- Trap: "Destinations can only be set on
$LATEST." → Reality: they can be set per version or alias; the DLQ is the setting that lives at function level.
Practice questions
-
A photo-processing function is invoked by S3 event notifications. The operations team wants every event that still fails after Lambda's automatic retries to be captured together with the error message and the function's response, without adding code to the handler. Which solution meets these requirements with the LEAST operational overhead?
- A. Configure an on-failure destination that points to an SQS standard queue.
- B. Configure a dead-letter queue on the function that points to an SNS topic.
- C. Wrap the handler in a try/except block that sends failed events to an SQS queue.
- D. Create a CloudWatch Logs subscription filter that copies error lines to an S3 bucket.
-
A function invoked asynchronously by an EventBridge rule calls a partner API that is unavailable for up to 20 minutes at a time. Events older than 15 minutes are worthless, and a failed event must not be retried more than once. Which TWO configuration changes meet these requirements? (Select TWO.)
- A. Set
MaximumRetryAttemptsto 1 withupdate-function-event-invoke-config. - B. Set the function timeout to 900 seconds so that each attempt can wait out the outage.
- C. Set reserved concurrency to 1 so that only one event is processed at a time.
- D. Set
MaximumEventAgeInSecondsto 900 withupdate-function-event-invoke-config. - E. Configure an SQS FIFO queue as the on-failure destination to preserve event order.
- A. Set
Answers
- A. An on-failure destination receives the full invocation record (request, response, condition) with no code. B keeps only the event and a short error attribute; C adds custom code; D depends on log parsing and loses the structured response.
- A, D. One retry and a 900-second maximum event age implement both rules. B wastes duration and still violates the 15-minute freshness rule; C throttles the function without limiting retries; E fails because FIFO destinations are not supported.
3. Event source mappings for queues and streams
Concept
- An event source mapping (ESM) is a Lambda resource whose pollers read records from a queue or stream and invoke the function synchronously with a batch. ⚠️ Changed: supported sources are Amazon SQS, Kinesis Data Streams, DynamoDB Streams, Amazon MSK, self-managed Apache Kafka, Amazon MQ and Amazon DocumentDB. Older material may list only SQS, Kinesis and DynamoDB Streams.
- Because Lambda does the reading (long polling for SQS), the execution role needs the read permissions on the source (for SQS:
sqs:ReceiveMessage,sqs:DeleteMessage,sqs:GetQueueAttributes, all inAWSLambdaSQSQueueExecutionRole; managed policies per source: §4); no resource-based policy on the function is involved. - Queues and streams fail differently:
- SQS: messages stay invisible while a batch runs and are deleted after success; after a failure they reappear when the visibility timeout ends, possibly regrouped. Standard queues don't preserve order; FIFO queues keep order within a message group.
- Streams (Kinesis, DynamoDB): records stay in the shard; Lambda reads each shard in order and, by default, retries a failing batch until it succeeds or the records expire, which blocks that shard.
- Either way, every ESM processes records at least once, so handlers must be idempotent.
Exam facts
- Batching: Lambda invokes when the batch size is reached, the batching window (
MaximumBatchingWindowInSeconds, 0–300 s) expires, or the payload reaches 6 MB. Default window: 0 s for SQS, Kinesis and DynamoDB; 500 ms for MSK, Kafka, MQ and DocumentDB. - Batch size: ⚠️ Changed: SQS standard queues accept up to 10,000 messages per batch (default 10; above 10 the batching window must be at least 1 s); FIFO queues allow at most 10 and take no batching window. Older material may say 1–10 for every SQS queue. Kinesis and DynamoDB Streams: default 100, maximum 10,000.
- SQS settings: visibility timeout of at least 6 × the function timeout (plus the batching window); Lambda refuses a mapping whose function timeout exceeds the queue's visibility timeout. Put the DLQ in the source queue's redrive policy (
maxReceiveCountof at least 5 recommended). Queue and function share a Region (another account is allowed). Encrypted queues also needkms:Decrypt. If the function has reserved concurrency, give it at least 5. - SQS scaling (default mode): ⚠️ Changed: starts with 5 concurrent invocations, adds up to 300 per minute (since 2023-11-06) and reaches at most 1,250 concurrent invocations per mapping. Older material may say +60 instances per minute and up to 1,000 batches.
ScalingConfig.MaximumConcurrency(2–1,000) caps one mapping; keep the function's reserved concurrency ≥ the sum of its mappings' maximum concurrency. A FIFO queue can't use more concurrent invocations than it has message groups with waiting messages. - Provisioned mode (SQS, MSK, self-managed Kafka): you set minimum and maximum event pollers (SQS minimum 2–200, default 2); an SQS poller handles up to 1 MB/s or 10 concurrent invocations and scaling adds up to 1,000 concurrency per minute; extra cost; for SQS it can't be combined with maximum concurrency.
- Partial batch responses: enable
FunctionResponseTypesReportBatchItemFailuresand return{"batchItemFailures": [{"itemIdentifier": "<id>"}]}(SQSmessageId; for streams the record's sequence number); only the listed items are retried. A thrown exception, invalid JSON or an unknown or emptyitemIdentifierfails the whole batch; an empty or null list means full success. On a FIFO queue, stop at the first failure and report it plus every later message to keep the order. - Stream settings:
StartingPositionTRIM_HORIZON,LATESTorAT_TIMESTAMP(Kinesis, not DynamoDB);ParallelizationFactor1–10 concurrent batches per shard, keeping order per partition key;BisectBatchOnFunctionErrorsplits a failing batch in two (splits don't consume retries);MaximumRetryAttempts-1 (default, until expiry) to 10,000;MaximumRecordAgeInSeconds-1 (default) or 60–604,800;TumblingWindowInSeconds0–900 for aggregations; enhanced fan-out consumers get a dedicated pipe per shard. - Discarded batches: ⚠️ Changed: stream and Kafka mappings send an on-failure record to an SQS queue, SNS topic or S3 bucket, and Kafka mappings can also use a Kafka topic; SQS and SNS receive metadata only (shard and sequence-number range), S3 also receives the whole batch. Older material may list only SQS and SNS. SQS mappings have no destination: the queue's DLQ does that job.
- Filtering:
FilterCriteriawith up to 5 patterns per mapping (quota increase to 10), combined with OR, using EventBridge pattern syntax. Data keys:body(SQS),data(Kinesis),dynamodb(DynamoDB Streams). Unmatched SQS messages are deleted; unmatched stream records are skipped. Filtering cuts invocations and cost. - Monitoring:
IteratorAge(streams) rises when the function falls behind or a shard is blocked; opt-in mapping metrics (--metrics-config Metrics=EventCount) addPolledEventCount,FilteredOutEventCount,InvokedEventCount,FailedInvokeEventCountand, for streams,DroppedEventCount. - Queue and stream internals (visibility timeout mechanics, shards, KCL): see notes/sqs-sns-kinesis-guide.md; DynamoDB Streams view types: see notes/dynamodb-guide.md.
Developer angle
# SQS: batches of up to 100, 5-second window, partial failures, at most 50 concurrent runs for this queue
aws lambda create-event-source-mapping --function-name billing-worker:live \
--event-source-arn arn:aws:sqs:eu-west-1:111122223333:invoices \
--batch-size 100 --maximum-batching-window-in-seconds 5 \
--function-response-types ReportBatchItemFailures \
--scaling-config MaximumConcurrency=50
# Kinesis: 4 batches per shard, split bad batches, give up after 3 retries or 1 hour, keep failures in SQS
aws lambda create-event-source-mapping --function-name clickstream \
--event-source-arn arn:aws:kinesis:eu-west-1:111122223333:stream/clicks \
--starting-position LATEST --batch-size 500 --parallelization-factor 4 \
--bisect-batch-on-function-error --maximum-retry-attempts 3 \
--maximum-record-age-in-seconds 3600 \
--destination-config '{"OnFailure":{"Destination":"arn:aws:sqs:eu-west-1:111122223333:click-failures"}}'
# only refunds reach the function; pause the mapping without deleting it
aws lambda update-event-source-mapping --uuid a1b2c3d4-5678-90ab-cdef-11111EXAMPLE \
--filter-criteria '{"Filters": [{"Pattern": "{\"body\": {\"type\": [\"refund\"]}}"}]}'
aws lambda update-event-source-mapping --uuid a1b2c3d4-5678-90ab-cdef-11111EXAMPLE --no-enabled
import json
def handler(event, context): # SQS partial batch response
failures = []
for record in event["Records"]:
try:
invoice = json.loads(record["body"])
if invoice.get("amount", 0) <= 0:
raise ValueError("invalid amount")
except Exception:
failures.append({"itemIdentifier": record["messageId"]})
return {"batchItemFailures": failures}
list-event-source-mappings --function-name <name>shows each mapping's UUID andState; updates are applied asynchronously.
Traps
- Trap: "Attach a DLQ to the function to catch failed SQS messages." → Reality: configure the redrive policy (DLQ) on the source queue.
- Trap: "A visibility timeout equal to the function timeout is enough." → Reality: use at least 6 × the timeout (plus the window), or messages reappear while still being processed; Lambda rejects a function timeout longer than the visibility timeout.
- Trap: "Raising
ParallelizationFactorbreaks record order." → Reality: order is kept per partition key; it only adds concurrent batches per shard. - Trap: "Lambda skips a poison record in a Kinesis shard." → Reality: with the defaults (-1 retries, -1 age) the shard stalls until the record expires; set retries, a maximum age, bisecting and an on-failure destination.
- Trap: "Set the SQS batch size to 1,000 and leave the window at 0." → Reality: batch sizes above 10 require a batching window of at least 1 second.
- Trap: "An on-failure destination on the SQS mapping stores failed messages." → Reality: SQS mappings have none; the queue's DLQ does that job.
- Trap: "Returning the failed IDs is enough for partial retries." → Reality:
ReportBatchItemFailuresmust be enabled on the mapping, and throwing an exception still retries the whole batch.
Practice questions
-
A function consumes an SQS standard queue with a batch size of 10. Occasionally one malformed message makes the function throw, and the other nine messages are processed again on every retry, which creates duplicate charges. The developer wants only the failing message to be retried while keeping batches of 10 for throughput. What should the developer do?
- A. Reduce the batch size to 1 so that each message is retried on its own.
- B. Move to a FIFO queue and put malformed messages in their own message group.
- C. Configure an on-failure destination on the event source mapping for malformed messages.
- D. Enable
ReportBatchItemFailureson the mapping and return the failedmessageIdvalues inbatchItemFailures.
-
A Kinesis-triggered function stopped making progress on one shard after a producer wrote a corrupt record;
IteratorAgekeeps rising for that shard while the other shards are healthy. The team wants processing to move past bad records automatically and to keep a copy of whatever is skipped. Which TWO actions meet these requirements? (Select TWO.)- A. Increase the function timeout to the maximum of 900 seconds.
- B. Increase the stream's retention period so that the corrupt record stays available longer.
- C. Set
MaximumRetryAttemptsand enableBisectBatchOnFunctionErroron the event source mapping. - D. Increase
ParallelizationFactorto 10 so that more batches from the shard run at the same time. - E. Add an on-failure destination, such as an S3 bucket, to the event source mapping.
Answers
- D. Partial batch responses return only the failed message to the queue while the batch size stays at 10. A meets the retry goal but gives up the required batching; B doesn't stop the healthy messages from being retried; C fails because SQS mappings don't support on-failure destinations.
- C, E. A retry limit with bisecting isolates the bad record and lets the shard continue; the on-failure destination keeps the skipped batch (S3 stores the records themselves). A and B only prolong the stall; D adds concurrency but the failing batch is still retried until it expires.
4. Execution roles and resource based policies
Concept
- Lambda permissions answer two separate questions:
- What may the function do? → its execution role, an IAM role that Lambda assumes for each invocation. The trust policy names the service principal
lambda.amazonaws.com; the permission policies cover what the code calls and what Lambda does on the function's behalf (pollers, VPC network interfaces, logs). - Who may invoke or manage the function? → the caller's identity-based policy plus the function's resource-based policy, which lets AWS services and principals in other AWS accounts perform actions such as
lambda:InvokeFunctionon the function, a version or an alias.
- What may the function do? → its execution role, an IAM role that Lambda assumes for each invocation. The trust policy names the service principal
- Push-style integrations (S3, SNS, EventBridge, API Gateway, ALB, CloudWatch Logs, other accounts) need a statement in the resource-based policy (⚠️ an EventBridge rule can use an IAM execution role on the target instead, §2); pull-style event source mappings rely on the execution role.
- The role's temporary credentials reach the code as environment variables (
AWS_ACCESS_KEY_ID,AWS_SECRET_ACCESS_KEY,AWS_SESSION_TOKEN) that the SDK default credential chain finds automatically (SnapStart functions get them from the container credentials endpoint instead): keys never belong in code or configuration.
Exam facts
- Trust policy:
"Principal": {"Service": "lambda.amazonaws.com"}with"Action": "sts:AssumeRole". Don't callsts:AssumeRoleon the function's own role from the code. - Handing a role to Lambda when you create or update a function requires
iam:PassRoleon that role (depth: see notes/advanced-identity-guide.md). - AWS managed policies for execution roles:
AWSLambdaBasicExecutionRole(CloudWatch Logs),AWSLambdaSQSQueueExecutionRole,AWSLambdaKinesisExecutionRole,AWSLambdaDynamoDBExecutionRole,AWSLambdaMSKExecutionRole,AWSLambdaVPCAccessExecutionRole(network interfaces plus logs),AWSXRayDaemonWriteAccess(traces),CloudWatchLambdaInsightsExecutionRolePolicy(Lambda Insights). - Least privilege: one role per function with resource-scoped statements; IAM Access Analyzer can generate a policy from the role's CloudTrail activity.
- The
lambda:SourceFunctionArncondition key matches calls made with a given function's credentials, for example to deny the network-interface actions to the code while Lambda still manages them. - Resource-based policy: at most 20 KB per function.
AddPermission(add-permission) appends one Allow statement for one principal, with a short list of conditions (aws:SourceArn,aws:SourceAccount,aws:PrincipalOrgID, plus the function URL keys). ⚠️ Changed: since 2026-08-25PutResourcePolicy(put-resource-policy) writes a complete JSON policy with several principals and actions, explicitDenystatements and any IAM global condition key; it overwrites the whole policy, including statements added withadd-permission. Older material may say a function policy can only be built one Allow statement at a time with limited conditions. PutResourcePolicyneedslambda:PutResourcePolicy,lambda:AddPermissionandlambda:RemovePermission; read the current document withget-resource-policyand pass itsRevisionId(--revision-id) to avoid overwriting a concurrent change.- Confused-deputy protection: for service principals add the source resource (
--source-arn: bucket, topic, rule or target group ARN) and--source-account; S3 bucket ARNs contain no account ID. - Cross-account invocation needs both sides: the function's resource-based policy allows the other account's principal and that principal's identity policy allows
lambda:InvokeFunction. Within one account, either policy is enough. - Scope: a statement can target the unqualified function ARN, a version or an alias;
function:<name>:*covers every version and alias but not the unqualified ARN, so list both to cover everything. Useaws:PrincipalOrgID(or--principal-org-id) to allow a whole AWS Organization. - The console adds trigger permissions automatically; the CLI, CloudFormation (
AWS::Lambda::Permission) and SDKs don't. Policy evaluation, permission boundaries and SCPs: see notes/advanced-identity-guide.md; IAM fundamentals: see notes/iam-guide.md.
Developer angle
# lambda-trust.json: Allow, Principal {"Service": "lambda.amazonaws.com"}, Action sts:AssumeRole
aws iam create-role --role-name thumbs-exec \
--assume-role-policy-document file://lambda-trust.json
aws iam attach-role-policy --role-name thumbs-exec \
--policy-arn arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
# let one bucket in this account invoke the live alias
aws lambda add-permission --function-name thumbs:live --statement-id s3-uploads \
--action lambda:InvokeFunction --principal s3.amazonaws.com \
--source-arn arn:aws:s3:::media-uploads --source-account 111122223333
# full-policy workflow: read, edit, write back with the revision check
aws lambda get-resource-policy \
--resource-arn arn:aws:lambda:eu-west-1:111122223333:function:thumbs
aws lambda put-resource-policy \
--resource-arn arn:aws:lambda:eu-west-1:111122223333:function:thumbs \
--policy file://thumbs-policy.json --revision-id "$REVISION_ID"
thumbs-policy.json: allow a role from a member account and deny every caller outside the organization, for the function and all its versions and aliases:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AnalyticsInvoke",
"Effect": "Allow",
"Principal": { "AWS": "arn:aws:iam::444455556666:role/analytics-batch" },
"Action": "lambda:InvokeFunction",
"Resource": "arn:aws:lambda:eu-west-1:111122223333:function:thumbs:live"
},
{
"Sid": "DenyOutsideOrg",
"Effect": "Deny",
"Principal": "*",
"Action": "lambda:InvokeFunction",
"Resource": ["arn:aws:lambda:eu-west-1:111122223333:function:thumbs",
"arn:aws:lambda:eu-west-1:111122223333:function:thumbs:*"],
"Condition": { "StringNotEquals": { "aws:PrincipalOrgID": "o-a1b2c3d4e5" } }
}
]
}
- In CloudFormation grant each trigger with
AWS::Lambda::Permission(Action,FunctionName,Principal,SourceArn,SourceAccount).aws lambda get-policy --function-name thumbsstill returns the policy as a JSON string.
Traps
- Trap: "The S3 trigger was created with the CLI, so it works." → Reality: without a resource-based statement for
s3.amazonaws.comS3 cannot invoke the function; the console adds it, the CLI and CloudFormation don't. - Trap: "Give the SQS queue permission to invoke the function." → Reality: the mapping polls with the execution role; add
sqs:ReceiveMessage,sqs:DeleteMessageandsqs:GetQueueAttributesto the role. - Trap: "
put-resource-policyadds my new statement." → Reality: it replaces the whole policy; statements fromadd-permissionvanish unless you include them. - Trap: "Block one bucket with
add-permission." → Reality:add-permissiononly creates Allow statements; an explicit Deny needs a full JSON policy. - Trap: "A cross-account call only needs the function's resource policy." → Reality: the caller's identity policy must allow
lambda:InvokeFunctiontoo. - Trap: "Put access keys in environment variables for the SDK." → Reality: the execution role's temporary credentials are already injected; static keys in configuration are a security finding.
Practice questions
-
A developer defines an S3 bucket notification and a Lambda function in one CloudFormation template. The stack fails while creating the bucket notification, with an error saying that the destination configuration cannot be validated. What is the MOST likely fix?
- A. Attach
AWSLambdaBasicExecutionRoleto the function's execution role. - B. Add an
AWS::Lambda::Permissionresource that allowss3.amazonaws.comto invoke the function, scoped withSourceArnandSourceAccount. - C. Add
s3:GetObjecton the bucket to the function's execution role. - D. Replace the notification with an event source mapping between the bucket and the function.
- A. Attach
-
An analytics account must invoke a function in the production account. The security team also requires that no principal outside the company's AWS Organization can ever invoke the function, even if someone later adds a broad statement. Which TWO actions meet these requirements? (Select TWO.)
- A. Write a full JSON resource-based policy with
put-resource-policythat allows the analytics role and explicitly denies callers whoseaws:PrincipalOrgIDdiffers. - B. Run
add-permissionwith--principal '*'and rely on the analytics role's identity policy to limit access. - C. Grant
lambda:InvokeFunctionon the production function ARN in the analytics role's identity policy. - D. Attach the
AWSLambdaRolemanaged policy to the production function's execution role. - E. Create an event source mapping in the analytics account that targets the production function.
- A. Write a full JSON resource-based policy with
Answers
- B. S3 validates that it may invoke the function when the notification is created; the resource-based permission (scoped to the bucket and account) provides that. A and C change what the function can do, not who can call it; D is impossible because S3 notifications are push-based, not an event source mapping.
- A, C. Cross-account invocation needs the function's resource policy and the caller's identity policy; the explicit Deny on
aws:PrincipalOrgIDoverrides any later broad Allow. B opens the function to everyone; D changes the function's own permissions; E fails because event source mappings read queues and streams.
5. Concurrency scaling and throttling
Concept
- Concurrency is the number of requests being processed at the same moment. In the default compute model one execution environment handles one request at a time, so concurrency ≈ average requests per second × average duration in seconds (100 requests/s × 0.5 s ≈ 50).
- All functions in a Region draw from the account's concurrency quota; how fast a single function can grow is limited separately by the concurrency scaling rate. Reserved concurrency sets aside a fixed slice for one function: it guarantees that capacity and caps the function at it. It costs nothing.
- When no concurrency is available a request is throttled. The outcome depends on the invocation type: synchronous callers receive an error, asynchronous events wait in the queue and retry, event source mappings back off and retry.
Exam facts
- Default quota: 1,000 concurrent executions per Region, raisable to tens of thousands; new accounts start lower and AWS raises them with usage. ⚠️ Changed: request increases in Service Quotas (quota code
L-B99A9384). Older material may say to open a support ticket. - You can reserve up to the unreserved concurrency minus 100; those 100 units always stay in the shared pool. Reserved concurrency
0stops every invocation (a kill switch). - ⚠️ Changed: each function can add up to 1,000 execution environments every 10 seconds (10,000 requests per second every 10 seconds), independently of other functions, until the account quota is reached (since 2023-11-26; not adjustable; unused rate doesn't build up). Older material may say an account-wide initial burst of 500–3,000 followed by +500 per minute.
- Synchronous request-rate ceiling: 10 requests per second per environment, so 10 × concurrency (a function URL with reserved concurrency 100 accepts at most 1,000 requests per second). Asynchronous calls are limited only by concurrency.
- ⚠️ Changed: a throttled synchronous call fails with
TooManyRequestsException(HTTP 429) and a reason ofConcurrentInvocationLimitExceeded(account pool exhausted) orReservedFunctionConcurrentInvocationLimitExceeded(the function's own cap). Older material may call it a generic "ThrottleError". - Throttled asynchronous events are retried for up to 6 hours (§2); throttled SQS polling backs off and the messages return after the visibility timeout (§3).
- Metrics:
ConcurrentExecutions(read the Maximum statistic),UnreservedConcurrentExecutions,ClaimedAccountConcurrency(unreserved use plus every reserved and provisioned allocation),Throttles. Throttled requests appear in neitherInvocationsnorErrors. - Typical uses of reserved concurrency: protect a downstream system with few connections, guarantee capacity for a critical function, or emergency-stop a function. For a single SQS mapping prefer
MaximumConcurrency(§3); for per-client limits use API Gateway throttling and usage plans (see notes/api-gateway-guide.md). - Mismatch to plan for: API Gateway's default account throttle of 10,000 requests per second versus Lambda's default 1,000 concurrency. Load test and raise the Lambda quota before launch. Provisioned concurrency also comes out of the account pool (§6).
Developer angle
aws lambda get-account-settings # AccountLimit and AccountUsage
aws lambda put-function-concurrency --function-name report-builder \
--reserved-concurrent-executions 50
aws lambda put-function-concurrency --function-name report-builder \
--reserved-concurrent-executions 0 # emergency stop
aws lambda delete-function-concurrency --function-name report-builder
aws service-quotas request-service-quota-increase --service-code lambda \
--quota-code L-B99A9384 --desired-value 3000
# alarm as soon as anything is throttled
aws cloudwatch put-metric-alarm --alarm-name report-builder-throttles \
--namespace AWS/Lambda --metric-name Throttles --dimensions Name=FunctionName,Value=report-builder \
--statistic Sum --period 60 --evaluation-periods 1 --threshold 1 \
--comparison-operator GreaterThanOrEqualToThreshold \
--alarm-actions arn:aws:sns:eu-west-1:111122223333:ops-alerts
import boto3
from botocore.config import Config
# client side: back off on 429 responses and wait long enough for slow functions
lambda_client = boto3.client("lambda", config=Config(
retries={"max_attempts": 8, "mode": "adaptive"}, read_timeout=120))
- Backoff with jitter and SDK retry modes in general: see notes/cli-sdk-policies-guide.md.
Traps
- Trap: "Reserved concurrency costs extra." → Reality: it is free; provisioned concurrency is the billed feature.
- Trap: "Reserved concurrency removes cold starts." → Reality: it only reserves and caps capacity; environments are still created on demand (§6).
- Trap: "Reserve all 1,000 units for the critical function." → Reality: at least 100 units must stay unreserved.
- Trap: "Throttles show up in the
Errorsmetric." → Reality: they are counted only inThrottles. - Trap: "Lambda retries a throttled synchronous call." → Reality: the caller receives 429 and must retry with backoff (SDKs do some of this automatically).
- Trap: "A function with reserved concurrency 0 keeps working through its async queue slowly." → Reality: new asynchronous events go straight to the DLQ or on-failure destination without retries.
- Trap: "All functions share one burst of 500 per minute." → Reality: scaling is per function: 1,000 environments every 10 seconds.
Practice questions
-
A nightly report function opens one connection per invocation to a legacy database that accepts at most 40 connections. At month end the function scales out and the database starts rejecting connections, which also breaks other applications. Which solution protects the database with the LEAST operational overhead?
- A. Set the function's reserved concurrency to a value below 40.
- B. Configure provisioned concurrency of 40 on the function's alias.
- C. Increase the function timeout so that each invocation holds its connection longer.
- D. Ask AWS to lower the account's concurrent executions quota.
-
An API that invokes a function synchronously returns errors to users during a marketing campaign. CloudWatch shows
Throttlesrising whileErrorsstays at zero, the Region'sConcurrentExecutionssits flat at the account quota of 1,000, and the function has no reserved concurrency. Which TWO statements describe the situation and a valid response? (Select TWO.)- A. The function code is failing, and these failures are hidden from the
Errorsmetric. - B. Callers receive
TooManyRequestsExceptionbecause the account's concurrency pool is exhausted. - C. Lambda stores the throttled requests and replays them for up to 6 hours.
- D. Requesting a higher concurrent executions quota in Service Quotas addresses the cause.
- E. Adding a dead-letter queue to the function captures the throttled requests.
- A. The function code is failing, and these failures are hidden from the
Answers
- A. Reserved concurrency caps how many environments can open connections at once, with one setting and no new infrastructure. B pre-warms environments but doesn't cap scaling; C makes connection pressure worse; D would starve every other function in the account.
- B, D. Without reserved concurrency the 429s come from the shared account pool (
ConcurrentInvocationLimitExceeded), so raising the quota fixes the cause. A is wrong because throttled requests never ran the code; C and E describe asynchronous behaviour, not synchronous API calls.
6. Cold starts with provisioned concurrency and SnapStart
Concept
- Execution environment lifecycle: Init (start extensions, start the runtime, run the code outside the handler) → Invoke (run the handler, repeated for every request the environment serves) → Shutdown. Between requests the environment is frozen and later thawed; Lambda still recycles environments every few hours.
- A cold start is a request that waits for a new environment: code download, environment start-up and Init code. AWS reports them in under 1% of invocations, lasting from under 100 ms to over a second; heavy frameworks (Java, .NET) suffer most. Code-level levers: create SDK clients, configuration and connections outside the handler so warm invocations reuse them, import only the modules you need, lazy-load rarely used libraries, keep packages small.
- Provisioned concurrency (PC) keeps a set number of environments initialized ahead of traffic for one version or alias: double-digit millisecond starts, billed while configured.
- SnapStart runs Init once when you publish a version, snapshots the initialized memory and disk, and resumes new environments from that snapshot: sub-second starts without paying for idle environments (no extra charge on Java).
Exam facts
- Init limit: 10 seconds for on-demand environments (if exceeded, Lambda retries Init at the first invocation under the function timeout). With PC, SnapStart or Managed Instances initialization may run up to 15 minutes (130 s or the function timeout, whichever is higher).
- ⚠️ Changed: the Init phase is billed for every configuration since 2025-08-01 (it appears in
Billed Duration). Older material may say Init is free for on-demand .zip functions on managed runtimes. Cold starts showInit Durationin theREPORTlog line and an initialization subsegment in X-Ray traces. - PC rules: configured on a published version or an alias, never on
$LATEST; up to the unreserved concurrency minus 100; must not exceed the function's reserved concurrency when both are set; traffic above it spills over to on-demand environments (ProvisionedConcurrencySpilloverInvocations). Triggers and integrations must invoke that alias or version to benefit.AWS_LAMBDA_INITIALIZATION_TYPEreportson-demand,provisioned-concurrency,snap-startorlambda-managed-instances. - PC auto scaling through Application Auto Scaling: scalable dimension
lambda:function:ProvisionedConcurrency, resource IDfunction:<name>:<alias>, target tracking onLambdaProvisionedConcurrencyUtilization(target 0.1–0.9) or scheduled scaling for known peaks. Configure an initial PC value first; the utilization metric isn't emitted while the function is idle. AWS suggests about 10% headroom over the measured peak. - ⚠️ Changed: SnapStart supports Java 11+, Python 3.12+ and .NET 8+ managed runtimes, for .zip packages and for container images built on the matching AWS base images. Older material may say Java only and .zip only. Node.js, Ruby and OS-only runtimes are not supported.
- SnapStart rules: activate with
SnapStartApplyOn=PublishedVersions, then publish a version (OptimizationStatusbecomesOn); only published versions and aliases use it; it can't be combined with PC, EFS, S3 Files or ephemeral storage above 512 MB; it isn't offered in Asia Pacific (New Zealand) and Asia Pacific (Taipei). - SnapStart cost: nothing extra on Java; Python and .NET pay for snapshot caching (minimum 3 hours per published version, for as long as the version is active) and for every restore.
- Runtime hooks: Java uses CRaC (
org.crac,Resource.beforeCheckpoint()andafterRestore()); Python usessnapshot_restore_py(@register_before_snapshot,@register_after_restore). Restore plus after-restore hooks must complete within 10 seconds, otherwiseSnapStartTimeoutException. - Uniqueness: anything created during Init (random seeds, UUIDs, temporary credentials, cached timestamps) is cloned into every restored environment; regenerate it after restore or inside the handler, and re-validate network connections.
- Java snapshots are deleted after 14 days without invocations; the next call gets
SnapStartNotReadyExceptionwhile a new snapshot is built. - Choosing: strict, predictable latency or a known traffic schedule → PC; heavy Java, .NET or Python initialization with spiky traffic and no budget for idle capacity → SnapStart. VPC attachment is no longer a cold-start penalty (shared network interfaces, §10).
Developer angle
import os
import uuid
import boto3
from snapshot_restore_py import register_after_restore # only needed with SnapStart (Python)
# Init code: runs once per execution environment, reused by every warm invocation
table = boto3.resource("dynamodb").Table(os.environ["TABLE_NAME"])
instance_id = None
@register_after_restore
def refresh_unique_state(): # never reuse an ID captured in the snapshot
global instance_id
instance_id = str(uuid.uuid4())
def handler(event, context):
table.put_item(Item={"pk": event["orderId"], "status": "NEW", "by": instance_id})
return {"saved": event["orderId"]}
# provisioned concurrency on the live alias, then target-tracking at 70% utilization
aws lambda put-provisioned-concurrency-config --function-name checkout \
--qualifier live --provisioned-concurrent-executions 20
aws application-autoscaling register-scalable-target --service-namespace lambda \
--resource-id function:checkout:live \
--scalable-dimension lambda:function:ProvisionedConcurrency \
--min-capacity 20 --max-capacity 200
aws application-autoscaling put-scaling-policy --service-namespace lambda \
--resource-id function:checkout:live \
--scalable-dimension lambda:function:ProvisionedConcurrency \
--policy-name pc-70 --policy-type TargetTrackingScaling \
--target-tracking-scaling-policy-configuration '{"TargetValue": 0.7, "PredefinedMetricSpecification": {"PredefinedMetricType": "LambdaProvisionedConcurrencyUtilization"}}'
# SnapStart: enable, publish, verify on the new version
aws lambda update-function-configuration --function-name quote-engine \
--snap-start ApplyOn=PublishedVersions
aws lambda publish-version --function-name quote-engine
aws lambda get-function-configuration --function-name quote-engine:7 --query SnapStart
Traps
- Trap: "Configure provisioned concurrency on
$LATESTfor the dev stage." → Reality: PC needs a published version or an alias. - Trap: "Turn on SnapStart and provisioned concurrency together for the lowest latency." → Reality: the two can't be combined.
- Trap: "Provisioned concurrency means the function can't be throttled." → Reality: excess traffic spills to on-demand concurrency and can still hit the account or reserved limit.
- Trap: "SnapStart is Java-only." → Reality: Java 11+, Python 3.12+ and .NET 8+, including containers built on AWS base images.
- Trap: "Create the database client inside the handler to keep it fresh." → Reality: that reconnects on every call; create it in Init and reuse it (re-validate after a SnapStart restore).
- Trap: "A random seed set at import time is safe with SnapStart." → Reality: every restored environment shares it; regenerate after restore.
- Trap: "PC is configured, so the API is warm." → Reality: the integration must invoke the alias or version with PC; the unqualified ARN runs
$LATESTon demand.
Practice questions
-
A Java 21 function behind an API spends about 6 seconds initializing its framework, so users see slow responses after deployments and whenever traffic spikes. Traffic is unpredictable and the team does not want to pay for idle pre-initialized capacity. Which solution meets these requirements?
- A. Configure provisioned concurrency on the function's live alias.
- B. Increase the function's memory to 10,240 MB.
- C. Activate SnapStart, publish a new version, and point the alias at that version.
- D. Move the framework initialization into the handler.
-
A team configured provisioned concurrency of 50 on the
livealias of a checkout function, yet manyREPORTlog lines for user requests still includeInit Duration, andProvisionedConcurrencySpilloverInvocationsstays at zero. Which TWO statements identify the cause or confirm the fix? (Select TWO.)- A. The API integration invokes the unqualified function ARN, so requests run
$LATESTon demand; point it at thelivealias ARN. - B. Provisioned environments are used only after the function's reserved concurrency is removed.
- C. Provisioned concurrency must be configured on
$LATESTfor unqualified invocations to use it. - D. SnapStart must be enabled before provisioned environments start serving requests.
- E. The
ProvisionedConcurrencyInvocationsmetric on the alias shows whether requests use the provisioned environments.
- A. The API integration invokes the unqualified function ARN, so requests run
Answers
- C. SnapStart restores initialized Java environments from a snapshot, cutting cold starts without paying for idle environments. A meets the latency goal but bills for idle capacity; B adds CPU but the framework still initializes from scratch; D moves the cost to every invocation.
- A, E. Zero spillover plus constant cold starts means the provisioned alias isn't being invoked; calls to the unqualified ARN use
$LATEST.ProvisionedConcurrencyInvocationsconfirms when the alias is used. B is wrong because PC and reserved concurrency can coexist; C is impossible because PC can't be set on$LATEST; D is wrong because SnapStart and PC can't be combined at all.
7. Versions and aliases with traffic shifting
Concept
- You edit only
$LATEST. Publishing creates a numbered version: an immutable copy of the code and nearly all configuration with its own qualified ARN (…:function:orders:7). Version numbers only increase and are never reused, even after the function is deleted and recreated. - An alias (
…:function:orders:live) is a named, movable pointer to one version (or to$LATEST). Point triggers, event source mappings, API integrations, permissions and provisioned concurrency at aliases so a release becomes "move the alias" and a rollback "move it back". - A weighted alias sends a share of traffic to a second version: the building block of canary and linear releases, which CodeDeploy (usually driven by SAM) automates with alarms and automatic rollback.
Exam facts
- Frozen in a version: code, runtime, architecture, memory, timeout, layers, environment variables, VPC settings, DLQ, execution role, tracing, SnapStart, logging and storage settings. Still configurable on a published version: triggers, destinations, provisioned concurrency, asynchronous invocation settings, database proxies. Reserved concurrency is a function-level setting.
- Lambda publishes a new version only if code or configuration changed since the last one; otherwise the latest version number stays. An unqualified ARN invokes
$LATEST. An alias name can't be purely numeric, and an alias can't point to another alias. - Weighted alias: at most two versions, both published (no
$LATEST), with the same execution role and the same DLQ configuration (or none).AdditionalVersionWeightsgives the extra version its share ({"7": 0.1}= 10%); the primary version gets the rest. Routing is probabilistic, so low traffic can show uneven splits. - Which version answered: the
X-Amz-Executed-Versionresponse header (synchronous calls), theSTART RequestId: … Version: Nlog line, and theExecutedVersiondimension on alias metrics. - CodeDeploy predefined Lambda configurations (prefix
CodeDeployDefault.):LambdaCanary10Percent5Minutes,LambdaCanary10Percent10Minutes,LambdaCanary10Percent15Minutes,LambdaCanary10Percent30Minutes;LambdaLinear10PercentEvery1Minute,LambdaLinear10PercentEvery2Minutes,LambdaLinear10PercentEvery3Minutes,LambdaLinear10PercentEvery10Minutes;LambdaAllAtOnce. The Lambda AppSpec names the function, the alias, the current and the target version;BeforeAllowTrafficandAfterAllowTraffichooks run validation functions. Details: see notes/cicd-guide.md and notes/sam-guide.md (AutoPublishAlias,DeploymentPreference). - With provisioned concurrency on a weighted alias, size PC for both versions' shares or expect spillover. Function URLs attach to an alias or
$LATEST, never to a numbered version (§11). A resource-based statement on an alias ARN covers that alias only; usefunction:<name>:*for every qualifier.
Developer angle
aws lambda publish-version --function-name orders --description "invoice rounding fix"
aws lambda create-alias --function-name orders --name live --function-version 6
# canary: 10% of live traffic to version 7
aws lambda update-alias --function-name orders --name live \
--routing-config 'AdditionalVersionWeights={"7"=0.1}'
# promote: everything to version 7 and clear the weights
aws lambda update-alias --function-name orders --name live \
--function-version 7 --routing-config 'AdditionalVersionWeights={}'
# roll back instantly
aws lambda update-alias --function-name orders --name live --function-version 6
- Check which version served a synchronous call with
ExecutedVersionin theInvokeresponse (code in §1). With SAM,AutoPublishAlias: liveplusDeploymentPreference(Type: Canary10Percent5Minutes,Alarms, hook functions) generates the versions, the alias and the CodeDeploy deployment for you (see notes/sam-guide.md).
Traps
- Trap: "Update an environment variable on version 5." → Reality: versions are immutable; change
$LATEST, publish, move the alias. - Trap: "Point the canary alias at
$LATESTplus version 3." → Reality: both versions of a weighted alias must be published. - Trap: "Alias
prodcan point to aliasblue." → Reality: aliases point only to versions. - Trap: "Triggers on the unqualified ARN are fine in production." → Reality: every deployment to
$LATESTgoes live at once; invoke an alias. - Trap: "A 5% weight guarantees exactly 5% of requests." → Reality: routing is probabilistic; low traffic can deviate a lot.
- Trap: "Two versions with different execution roles can share a weighted alias." → Reality: the role and the DLQ settings must match.
Practice questions
-
A team wants to send 10% of production traffic to a new function version for 30 minutes and roll back automatically if a CloudWatch alarm fires, with as little manual work as possible. Clients already call the
prodalias. What should the team do?- A. Create a second alias for the new version and update clients to call both aliases.
- B. Publish the new version and schedule a cron job that calls
update-aliasafter 30 minutes. - C. Point the
prodalias at$LATESTand deploy the new code during a quiet period. - D. Use CodeDeploy with the
CodeDeployDefault.LambdaCanary10Percent30Minutesconfiguration and the alarm on theprodalias.
-
A developer tries to give the
betaalias anAdditionalVersionWeightsentry so that 20% of requests go to version 9 while version 8 keeps 80%. The update fails. Which TWO conditions can cause the failure? (Select TWO.)- A. Version 8 has more memory configured than version 9.
- B. Version 9 uses a different execution role than version 8.
- C. Version 9 has a dead-letter queue configured while version 8 has none.
- D. The function has reserved concurrency configured.
- E. Version 9 was published less than an hour after version 8.
Answers
- D. CodeDeploy shifts 10% through the alias, waits 30 minutes and rolls back on the alarm with no manual steps. A pushes routing into the clients; B adds a scheduled script and has no automatic rollback; C sends all traffic to unpublished code at once.
- B, C. Weighted aliases require the same execution role and the same DLQ configuration on both versions. Different memory (A), reserved concurrency (D) and publish timing (E) don't block weighted routing.
8. Deployment packages with layers and container images
Concept
- The package type is chosen at creation and can't be changed later: a .zip archive that runs on a managed runtime (or on an OS-only runtime with your own
bootstrap), or a container image stored in Amazon ECR. - Runtimes: managed runtimes for Node.js, Python, Java, .NET and Ruby; the OS-only runtime
provided.al2023for Go, Rust and custom runtimes. Every supported runtime runs on x86_64 and arm64. Lambda patches managed runtimes automatically; deprecated runtimes stop receiving patches and later block creates and updates. - Layers are .zip archives (libraries, a custom runtime, shared data) that Lambda extracts under
/opt; many functions can share one versioned layer, and a small function package stays editable in the console. - Container images carry large or complex dependencies (up to 10 GB) and fit an existing Docker workflow; the image must include the Lambda Runtime Interface Client, which the AWS base images already contain.
Exam facts
- .zip quotas (not adjustable): 50 MB zipped for a direct upload (API, SDK, CLI or console), larger archives through S3 (bucket in the function's Region, any account); 250 MB unzipped for the function plus all its layers; 3 MB for editing in the console code editor.
- ⚠️ Changed: 300 GB of Lambda-managed code storage per Region for .zip functions and layers, counting every published version (since 2026-07-15). Older material may say 75 GB. Self-managed S3 code storage (
S3ObjectStorageMode=REFERENCE, a versioned bucket whose policy letslambda.amazonaws.comcalls3:GetObjectands3:GetObjectVersion) reads code from your own bucket and doesn't count; losing access to that object makes the functionInactive. Deleting unused versions frees managed storage. - ⚠️ Changed: only the Node.js, Python and Ruby runtimes include an AWS SDK (Node.js: SDK for JavaScript v3; Python: boto3); Java, .NET and OS-only runtimes include none, and AWS recommends bundling the SDK version you tested with. Older material may say the AWS SDK comes with every function.
- ⚠️ Changed: Go and Rust run on the OS-only runtime
provided.al2023(thego1.xruntime was deprecated on 2024-01-08). Older material may describe Go and Rust as community-supported custom runtimes. - Deprecation timeline: notice at least 180 days ahead; after deprecation the console no longer creates or updates such functions; creation is blocked at least 30 days later and updates at least 60 days later; existing functions keep being invoked.
- CloudFormation code:
Code.ZipFileinline source for Node.js and Python only (zip ≤ 4 MB, handlerindex.<name>, no extra dependencies,cfn-responseavailable for custom resources); otherwiseS3Bucket+S3Key(+S3ObjectVersion). Uploading a new object under the same key changes nothing: change the key or the version in the template. Templates in depth: see notes/cloudformation-guide.md. - Layers: at most 5 per function; .zip functions only (not container images); each layer version is immutable, has an ARN ending in its number and is pinned exactly; contents merge into
/opt(runtime paths such aspython/,nodejs/node_modules/,java/lib/, plusbin/andlib/for all runtimes); share withadd-layer-version-permission(accounts or an organization). AWS advises against layers for Go and Rust (single binary; extra loading slows Init). A layer can pin a specific SDK version. Functions keep a layer version they already use even after it is deleted or access is revoked; new functions can't add a deleted version. - Container images: 10 GB uncompressed including all image layers; repository in Amazon ECR in the function's Region (another account works with ECR permissions); AWS base images at
public.ecr.aws/lambda/<runtime>:<tag>. ⚠️ Changed: the Node.js 20+, Python 3.12+, Java 21+, .NET 8+ and Ruby 3.3+ base images are built on Amazon Linux 2023. Older material may showamazon/aws-lambda-nodejs:12-style images built on Amazon Linux 2 (Node.js 12 is long deprecated). Custom base images need the Runtime Interface Client; the Runtime Interface Emulator (built into the AWS base images) answers onlocalhost:9000/2015-03-31/functions/function/invocationsfor local tests. - Lambda resolves the image tag to a digest when you deploy: pushing a new image under the same tag changes nothing until
update-function-code --image-uri …runs. If the image is deleted from ECR or Lambda loses ECR access, the function enters theFailedstate and every invocation fails; after weeks without invocations it becomesInactiveand the first call is rejected while Lambda re-optimizes the image. Build for the function's architecture (docker buildx build --platform linux/amd64orlinux/arm64, with--provenance=false). Lambda runs the image as a least-privileged non-root user. - Image best practices: start from AWS base images (cached by Lambda), use multi-stage builds, put rarely changing Dockerfile steps first and the most volatile ones last (better layer caching), keep functions that share large layers in one repository.
- Code signing: AWS Signer signing profiles plus a code signing configuration on the function;
UntrustedArtifactOnDeploymentWarn(default,SignatureValidationErrorsmetric) orEnforce(blocks the deployment); layers added to the function must be signed too; only new deployments are checked. - Dependencies travel inside the package (
pip install --target,node_modules,.jarfiles) or a layer; native libraries must be compiled for Amazon Linux (use the base image or a build container).
Developer angle
# .zip with dependencies larger than 50 MB: stage it in S3
pip install -r requirements.txt --target package/ && cp app.py package/
(cd package && zip -r ../function.zip .)
aws s3 cp function.zip s3://deploy-artifacts-eu-west-1/orders/function-42.zip
aws lambda update-function-code --function-name orders \
--s3-bucket deploy-artifacts-eu-west-1 --s3-key orders/function-42.zip --publish
# shared layer: publish, attach (the list replaces all layers), share with the organization
aws lambda publish-layer-version --layer-name pdf-tools --zip-file fileb://pdf-layer.zip \
--compatible-runtimes python3.13 python3.14 --compatible-architectures arm64
aws lambda update-function-configuration --function-name invoice-render \
--layers arn:aws:lambda:eu-west-1:111122223333:layer:pdf-tools:3
aws lambda add-layer-version-permission --layer-name pdf-tools --version-number 3 \
--statement-id org-read --action lambda:GetLayerVersion --principal '*' \
--organization-id o-a1b2c3d4e5
Container image from an AWS base image:
FROM public.ecr.aws/lambda/python:3.13
COPY requirements.txt ${LAMBDA_TASK_ROOT}
RUN pip install -r requirements.txt --target "${LAMBDA_TASK_ROOT}"
COPY app.py ${LAMBDA_TASK_ROOT}
CMD [ "app.handler" ]
docker buildx build --platform linux/arm64 --provenance=false -t invoice-api:1.4.0 .
docker run --platform linux/arm64 -p 9000:8080 invoice-api:1.4.0 # terminal 1: local RIE endpoint
curl "http://localhost:9000/2015-03-31/functions/function/invocations" -d '{"id": "inv-7"}' # terminal 2
aws ecr get-login-password --region eu-west-1 | docker login --username AWS \
--password-stdin 111122223333.dkr.ecr.eu-west-1.amazonaws.com
docker tag invoice-api:1.4.0 111122223333.dkr.ecr.eu-west-1.amazonaws.com/invoice-api:1.4.0
docker push 111122223333.dkr.ecr.eu-west-1.amazonaws.com/invoice-api:1.4.0
aws lambda update-function-code --function-name invoice-api --publish \
--image-uri 111122223333.dkr.ecr.eu-west-1.amazonaws.com/invoice-api:1.4.0
OrdersFunction: # Runtime, Handler and Role omitted
Type: AWS::Lambda::Function
Properties:
Code: # CloudFormation redeploys only when the key or version changes
S3Bucket: deploy-artifacts-eu-west-1
S3Key: orders/function-42.zip
S3ObjectVersion: 3HL4kqtJlcpXroDTDmJ.rmSpXd3dIbrHY
Traps
- Trap: "Add a layer to the container-image function." → Reality: layers work only with .zip functions; bake dependencies into the image.
- Trap: "I pushed the same tag again, so the function runs the new image." → Reality: Lambda pinned the old digest; run
update-function-code. - Trap: "Re-uploading
function.zipto the same S3 key makes CloudFormation redeploy." → Reality: changeS3KeyorS3ObjectVersion. - Trap: "Uploading through S3 lifts the 250 MB limit." → Reality: S3 lifts only the 50 MB upload limit; above 250 MB unzipped, use a container image.
- Trap: "Use
go1.xfor a new Go function." → Reality: it's deprecated; useprovided.al2023with an executable namedbootstrap. - Trap: "An image built on an Apple-silicon laptop runs on an x86_64 function." → Reality: build with
--platformmatching the function's architecture. - Trap: "
update-function-configuration --layersadds one layer." → Reality: the list replaces all layers; pass every ARN you want to keep.
Practice questions
-
A data-science team needs a function that loads machine learning libraries totalling about 1.8 GB uncompressed. The team's builds already run in a Docker-based CI pipeline. Which deployment approach meets the requirement with the LEAST effort?
- A. Split the libraries across five Lambda layers and attach them to a .zip function.
- B. Package the function as a container image based on an AWS base image and deploy it from Amazon ECR.
- C. Upload the .zip archive to S3 and reference it with
S3BucketandS3Key. - D. Download the libraries from S3 into
/tmpat the start of every invocation.
-
A developer uploads a new
app.zipto the same S3 key and runs a CloudFormation stack update, but the function still runs the old code. Separately, a teammate pushes a fixed container image to ECR with the sameprodtag, and that function also keeps running the old image. Which TWO actions fix these problems with the least disruption? (Select TWO.)- A. Turn on image tag immutability in the ECR repository.
- B. Increase each function's memory to force new execution environments.
- C. Delete and recreate both stacks so that the functions pick up the latest artifacts.
- D. Change
S3KeyorS3ObjectVersionin the template so that CloudFormation detects new code. - E. Call
update-function-codewith the image URI (or the new digest) to redeploy the image.
Answers
- B. A container image holds up to 10 GB and fits the existing Docker pipeline. A and C still hit the 250 MB unzipped limit for a function plus its layers; D adds download time to every cold start and custom loading code.
- D, E. CloudFormation deploys new S3 code only when the code location changes, and Lambda pins an image digest until
update-function-coderuns. A prevents tag reuse but deploys nothing; B doesn't change the deployed code; C works but is the most disruptive option.
9. Environment variables and secrets
Concept
- Environment variables are key-value strings stored in the function's version-specific configuration. They let the same code run in dev, test and prod with different table names, endpoints or log levels, and publishing a version freezes them.
- Lambda always encrypts environment variables at rest with AWS KMS and decrypts them when an environment initializes; the plain values are visible to anyone allowed to read the function configuration (and to decrypt with the key).
- Real secrets (database passwords, API keys, tokens) belong in AWS Secrets Manager or Parameter Store
SecureStringparameters, fetched at runtime with caching. The environment variable then holds only the secret's name or ARN.
Exam facts
- 4 KB for all of a function's variables combined (not adjustable). Keys start with a letter, have at least two characters and contain only letters, digits and
_; values are literal strings (no variable expansion); reserved keys can't be set. - Reserved variables Lambda sets:
_HANDLER,AWS_REGIONandAWS_DEFAULT_REGION,AWS_EXECUTION_ENV,AWS_LAMBDA_FUNCTION_NAME,AWS_LAMBDA_FUNCTION_VERSION,AWS_LAMBDA_FUNCTION_MEMORY_SIZE,AWS_LAMBDA_INITIALIZATION_TYPE,AWS_LAMBDA_LOG_GROUP_NAME,AWS_LAMBDA_LOG_STREAM_NAME(not set for SnapStart functions),AWS_ACCESS_KEY_ID/AWS_SECRET_ACCESS_KEY/AWS_SESSION_TOKEN(execution role),AWS_LAMBDA_RUNTIME_API,LAMBDA_TASK_ROOT,LAMBDA_RUNTIME_DIR,_X_AMZN_TRACE_ID. Also preset but overridable:AWS_XRAY_CONTEXT_MISSING(LOG_ERROR),AWS_XRAY_DAEMON_ADDRESS,TZ(:UTC),PATH,LD_LIBRARY_PATH,PYTHONPATH,NODE_OPTIONS. update-function-configuration --environmentreplaces the wholeVariablesmap: send every variable each time, and pass--revision-idfromget-function-configurationto avoid overwriting a concurrent change.- Encryption at rest: an AWS managed key by default (no KMS permissions needed, no charge) or a customer managed key (
--kms-key-arn) for control over rotation and access. Configuring a customer managed key needskms:CreateGrantandkms:Encrypt; viewing or editing the values needskms:Decrypt(deny it to hide them); the console list needskms:ListAliases. - When Lambda can't decrypt the variables at start-up, invocations fail with
KMSAccessDeniedException,KMSDisabledException,KMSInvalidStateExceptionorKMSNotFoundException(HTTP 502). - Console encryption helpers encrypt selected values client-side for protection in transit; the code must then call KMS
Decryptitself and the execution role needskms:Decrypt. - ⚠️ Changed: AWS recommends Secrets Manager instead of environment variables for credentials and other sensitive values. Older material may present KMS-encrypted environment variables as the place for secrets.
- AWS Parameters and Secrets Lambda Extension (layer
AWS-Parameters-and-Secrets-Lambda-Extension): a local HTTP endpoint on port2773(PARAMETERS_SECRETS_EXTENSION_HTTP_PORT) withGET /secretsmanager/get?secretId=<name or ARN>and the headerX-Aws-Parameters-Secrets-Tokenset to the session token; it caches up to 1,000 items for 300 seconds by default (SECRETS_MANAGER_TTL,SSM_PARAMETER_STORE_TTL; 0 disables caching). With frequent rotation lower the TTL or requestversionStage=AWSCURRENT. - Alternatives: the Powertools for AWS Lambda parameters utility (Secrets Manager, Parameter Store and AppConfig; default cache 5 seconds) or an SDK call cached in Init code.
- Execution role:
secretsmanager:GetSecretValueon the secret (pluskms:Decryptwhen the secret uses a customer managed key);ssm:GetParameterfor Parameter Store. Deploy-time injection with CloudFormation dynamic references, and Secrets Manager versus Parameter Store: see notes/security-encryption-guide.md.
Developer angle
aws lambda get-function-configuration --function-name payments \
--query '{vars: Environment.Variables, rev: RevisionId}'
aws lambda update-function-configuration --function-name payments \
--environment 'Variables={TABLE_NAME=payments-prod,LOG_LEVEL=INFO,DB_SECRET_ID=prod/payments/db}' \
--kms-key-arn arn:aws:kms:eu-west-1:111122223333:key/1234abcd-12ab-34cd-56ef-1234567890ab \
--revision-id "$REVISION_ID"
Read a secret through the extension (Python standard library only; the execution role needs secretsmanager:GetSecretValue on arn:aws:secretsmanager:eu-west-1:111122223333:secret:prod/payments/db-*, the -* covering the random suffix Secrets Manager adds to secret ARNs):
import json
import os
import urllib.parse
import urllib.request
def get_secret(secret_id):
url = "http://localhost:2773/secretsmanager/get?secretId=" + urllib.parse.quote(secret_id)
# SnapStart functions: resolve the token from the SDK credential chain instead
req = urllib.request.Request(url, headers={"X-Aws-Parameters-Secrets-Token": os.environ["AWS_SESSION_TOKEN"]})
with urllib.request.urlopen(req, timeout=2) as resp:
return json.loads(json.loads(resp.read())["SecretString"])
def handler(event, context):
creds = get_secret(os.environ["DB_SECRET_ID"]) # served from the extension cache after the first call
return {"user": creds["username"]}
Traps
- Trap: "Update one variable with
--environment Variables={LOG_LEVEL=DEBUG}." → Reality: that deletes every other variable; send the full map. - Trap: "Change the endpoint on version 12 in place." → Reality: environment variables are part of the immutable version; publish a new one.
- Trap: "Encrypted at rest means only the function can read the password." → Reality: anyone who can read the configuration (and decrypt with the key) sees the value; use Secrets Manager.
- Trap: "Encryption helpers need no code changes." → Reality: the function must call KMS
Decryptand its role needskms:Decrypt. - Trap: "Store a 6 KB certificate in an environment variable." → Reality: all variables share 4 KB; keep large values in Secrets Manager, Parameter Store or S3.
- Trap: "Call
GetSecretValueon every invocation." → Reality: cache it (extension or Init-time cache) to cut latency, cost and API throttling. - Trap: "
KMSAccessDeniedException(502) is a handler bug." → Reality: Lambda couldn't decrypt the variables: fix the key state or the key policy and grants.
Practice questions
-
A function reads a database password from an environment variable encrypted with the AWS managed key. An audit finds that developers with read-only Lambda access can see the password in the console, and the password must now rotate every 30 days without redeploying the function. Which solution meets these requirements with the LEAST operational overhead?
- A. Store the password in Secrets Manager with rotation enabled and read it through the AWS Parameters and Secrets Lambda Extension.
- B. Re-encrypt the environment variable with a customer managed key and remove
kms:Decryptfrom the developers. - C. Enable encryption helpers so that the value is encrypted client-side, and redeploy the function every month.
- D. Store the password in an S3 object and download it in the handler on every invocation.
-
After a teammate edits a function, every invocation fails with HTTP 502 and
KMSDisabledException, and theLOG_LEVELvariable added last week has disappeared. Which TWO actions explain and fix the problems? (Select TWO.)- A. Increase the function timeout because decryption runs out of time during Init.
- B. Re-enable the customer managed key used for the environment variables, or switch the function back to the AWS managed key.
- C. Re-send the complete
Variablesmap, because--environmentreplaced every variable with the one the teammate passed. - D. Add
kms:Decryptto the caller's identity policy so thatInvokecan decrypt the payload. - E. Publish a new version, because
$LATESTcan't hold environment variables.
Answers
- A. Secrets Manager hides the value from Lambda readers and rotates it without redeployments; the extension caches it. B hides the value but still needs a redeploy for each rotation; C keeps the monthly redeploy; D adds latency and still has no rotation.
- B, C.
KMSDisabledExceptionmeans Lambda can't decrypt the variables because the key is disabled, and--environmentoverwrote the whole map. A is unrelated to KMS state; D confuses the caller's permissions with the function's; E is wrong because$LATESTholds environment variables.
10. VPC access and storage options
Concept
- Every function already runs inside a VPC that Lambda owns, with internet access but no route to your private resources. Setting
VpcConfig(subnets plus security groups) connects the function to your VPC through Hyperplane elastic network interfaces that Lambda creates per subnet and security group combination and shares between functions. - Once attached, the function reaches private resources (RDS, ElastiCache, internal APIs) but loses its default internet path: outbound internet needs private subnets routed through a NAT gateway, and AWS APIs can be reached through VPC endpoints instead (design: see notes/vpc-guide.md). Logs still reach CloudWatch because the Lambda service delivers them.
- Storage inside a function:
/tmp(fast, ephemeral, per environment), layers (read-only/opt), Amazon EFS (shared, persistent, POSIX, mounted over NFS), Amazon S3 Files (a bucket mounted as a file system), or S3 through the SDK (durable objects).
Exam facts
- Execution role permissions for VPC attachment (or
AWSLambdaVPCAccessExecutionRole):ec2:CreateNetworkInterface,ec2:DescribeNetworkInterfaces,ec2:DescribeSubnets,ec2:DeleteNetworkInterface,ec2:AssignPrivateIpAddresses,ec2:UnassignPrivateIpAddresseson"Resource": "*". The person configuring it needsec2:DescribeSecurityGroups,ec2:DescribeSubnets,ec2:DescribeVpcsandec2:GetSecurityGroupsForVpc. - Network interface lifecycle: a new VPC function stays
Pending(not invocable) for possibly several minutes while its interface is created; during an update of an existing function (StatestaysActive,LastUpdateStatusInProgress), earlier versions keep serving but further updates are blocked. After 14 idle days Lambda reclaims the interface and marks the functionInactive; the next call fails and the function returns toPendinguntil networking is rebuilt. Removing the VPC configuration can take up to 20 minutes. - One Hyperplane interface supports up to 65,000 connections; Lambda's quota is 3,000 network interfaces per VPC (adjustable, shared with services such as EFS). Reusing the same subnet and security group combination lets functions share interfaces.
- A public subnet gives a VPC-attached function neither a public IP nor internet access. Use at least two subnets in different Availability Zones. VPCs with dedicated instance tenancy can't be attached directly.
- Outbound IPv6 needs
Ipv6AllowedForDualStack=trueand only dual-stack subnets (IPv6-only subnets aren't supported). - Security groups: allow the function's security group as the source in the database's security group; the function's group needs outbound access. For connection storms from scaling functions use RDS Proxy (see notes/rds-guide.md) or reserved concurrency (§5).
- Governance condition keys on
CreateFunctionandUpdateFunctionConfiguration:lambda:VpcIds,lambda:SubnetIds,lambda:SecurityGroupIds(for example, deny functions without a VPC).lambda:SourceFunctionArncan deny the network-interface actions to the function's own code. - VPC-related invoke errors (HTTP 502):
SubnetIPAddressLimitReachedException(no free IP addresses),ENILimitReachedException,EC2ThrottledException,EC2AccessDeniedException(missing role permissions),InvalidSubnetIDException,InvalidSecurityGroupIDException;ResourceNotReadyExceptionafter inactivity. /tmp: 512 MB by default at no charge, configurable from 512 to 10,240 MB in 1-MB steps (the extra size is billed); content survives warm invocations of the same environment but is never shared or durable; SnapStart requires 512 MB. ⚠️ Changed: everything in/tmpis encrypted at rest with an AWS managed key, with no setup; generate KMS data keys in code only when you need your own key or application-level encryption. Older material may say/tmpis only protected if your own code encrypts it with KMS data keys.- EFS: the function must be attached to the VPC and use an access point (enforced POSIX user and root directory), with mount targets in the function's Availability Zones, NFS port 2049 allowed between the security groups, a
LocalMountPathunder/mnt/, andelasticfilesystem:ClientMount(+ClientWrite) orAmazonElasticFileSystemClientReadWriteAccesson the role. Each environment opens its own connection, so heavy scaling can hit EFS connection and burst limits. A function uses EFS or S3 Files, not both. EFS details: see notes/ec2-mastery-guide.md §7.
| Option | Persistence | Shared between environments | Size | Typical use |
|---|---|---|---|---|
/tmp |
Ephemeral, per environment | No | 512–10,240 MB | Scratch files, cached downloads |
Layer (/opt) |
Immutable per layer version | Yes, read-only | Within 250 MB unzipped | Libraries, static reference data |
| EFS | Durable | Yes, read and write | Elastic | Shared models, large reference data, files between invocations |
| S3 (SDK) | Durable | Yes | Object storage | Inputs and outputs, large objects |
Developer angle
aws lambda update-function-configuration --function-name orders-db \
--vpc-config SubnetIds=subnet-0a1b2c3d4e5f60718,subnet-0f1e2d3c4b5a69788,SecurityGroupIds=sg-0123456789abcdef0
aws lambda wait function-updated-v2 --function-name orders-db
aws lambda get-function-configuration --function-name orders-db \
--query '{state: State, update: LastUpdateStatus, reason: LastUpdateStatusReasonCode}'
aws lambda update-function-configuration --function-name orders-db --ephemeral-storage Size=4096
aws lambda update-function-configuration --function-name orders-db \
--file-system-configs Arn=arn:aws:elasticfilesystem:eu-west-1:111122223333:access-point/fsap-0123456789abcdef0,LocalMountPath=/mnt/models
Keep the network-interface permissions for Lambda but deny them to the function's own code:
{ "Version": "2012-10-17",
"Statement": [{ "Effect": "Deny", "Resource": "*",
"Action": ["ec2:CreateNetworkInterface", "ec2:DeleteNetworkInterface", "ec2:DescribeNetworkInterfaces",
"ec2:DescribeSubnets", "ec2:DetachNetworkInterface", "ec2:AssignPrivateIpAddresses",
"ec2:UnassignPrivateIpAddresses"],
"Condition": { "ArnEquals": {
"lambda:SourceFunctionArn": "arn:aws:lambda:eu-west-1:111122223333:function:orders-db" } } }] }
- Scripts that update a function and then invoke it should wait for
StateActiveandLastUpdateStatusSuccessful(the waiter above); otherwise the next update fails withResourceConflictException.
Traps
- Trap: "Put the function in a public subnet to give it internet access." → Reality: it never gets a public IP; use private subnets behind a NAT gateway, or VPC endpoints for AWS APIs.
- Trap: "VPC attachment adds a network-interface cold start to every new environment." → Reality: interfaces are created at configuration time and shared; that old penalty is gone.
- Trap: "After VPC attachment the function still reaches DynamoDB over the internet." → Reality: only through a NAT gateway or a DynamoDB gateway endpoint.
- Trap: "
/tmpis a cache shared by all invocations." → Reality: it belongs to one execution environment and disappears with it. - Trap: "Mount EFS by file system ID." → Reality: Lambda mounts through an access point, from the VPC, under
/mnt/. - Trap: "Combine SnapStart with 2 GB of
/tmp." → Reality: SnapStart allows only 512 MB of ephemeral storage. - Trap: "
SubnetIPAddressLimitReachedExceptionmeans the concurrency quota was hit." → Reality: the subnets ran out of free IP addresses; add or enlarge subnets.
Practice questions
-
A function was attached to private subnets so that it can query an RDS database. Since the change, its calls to a third-party HTTPS API time out while database queries succeed. What should the developer do?
- A. Move the function to public subnets so that it receives a public IP address.
- B. Create an interface VPC endpoint for the third-party API's domain name.
- C. Attach the
AWSLambdaVPCAccessExecutionRolepolicy to the execution role. - D. Route the private subnets' internet-bound traffic through a NAT gateway in a public subnet.
-
An inference function needs a 3 GB model that hundreds of concurrent environments must read and that data scientists replace weekly without redeploying the function. Downloading the model from S3 during every cold start makes cold starts too slow. Which TWO steps are required for the best-fitting solution? (Select TWO.)
- A. Package the model in a Lambda layer and attach it to the function.
- B. Raise ephemeral storage to 10,240 MB so that the model fits in
/tmp. - C. Attach the function to the VPC that contains the EFS file system's mount targets.
- D. Create an EFS access point and mount it under a path that starts with
/mnt/. - E. Enable SnapStart so that the model is restored from the snapshot.
Answers
- D. A VPC-attached function reaches the internet only through a NAT gateway from private subnets. A fails because Lambda never assigns public IPs; B applies to AWS services and PrivateLink providers, not an arbitrary public API; C supplies interface permissions the function evidently already has.
- C, D. EFS shares one copy of the model across all environments and can be updated without redeploying; Lambda mounts it from the VPC through an access point under
/mnt/. A exceeds the 250 MB unzipped limit; B still downloads per environment; E would freeze the weekly model into published versions and can't use more than 512 MB of/tmp.
11. Function URLs and response streaming
Concept
- A function URL is a built-in HTTPS endpoint,
https://<url-id>.lambda-url.<region>.on.aws, for one alias or for$LATEST. It never changes while it exists, is dual-stack (IPv4 and IPv6), uses the request and response shape of API Gateway HTTP API payload format 2.0, and costs nothing beyond normal Lambda charges. - Access depends on the URL's auth type plus the function's resource-based policy:
AWS_IAM(callers sign requests with SigV4) orNONE(public, but the policy must still allow it). - Response streaming sends the response in pieces as it is produced (faster time to first byte, payloads beyond 6 MB) when the URL's invoke mode is
RESPONSE_STREAMor the caller usesInvokeWithResponseStream. - Function URL or API Gateway: the URL is the simplest and cheapest option for webhooks, prototypes and single-function endpoints; choose API Gateway for custom domain names, Cognito or Lambda authorizers, API keys and usage plans, request validation and transformation, caching, WAF or private APIs (see notes/api-gateway-guide.md).
Exam facts
- Attach a URL to an alias or
$LATESTonly, never to a numbered version. Deleting a URL is permanent and a new URL gets a new address. Function URLs are reachable over the public internet only (no PrivateLink, although the Lambda API itself supports it). - ⚠️ Changed: since October 2025 a new function URL needs both
lambda:InvokeFunctionUrlandlambda:InvokeFunction, otherwise callers get403 Forbidden, even withNONE. Scope theInvokeFunctionstatement withlambda:InvokedViaFunctionUrl=trueand the URL statement withlambda:FunctionUrlAuthType. The console and SAM create both statements forNONE; with the CLI, CloudFormation or the API you add them yourself. Older material may saylambda:InvokeFunctionUrlalone is enough. AWS_IAM: same-account callers need the two permissions in their identity policy or in the function's resource policy; cross-account callers need both. Requests are SigV4-signed for the servicelambda. Deleting a URL with auth typeNONEleaves its resource-policy statements behind; remove them yourself.- CORS settings on the URL (
AllowOrigins,AllowMethods,AllowHeaders,ExposeHeaders,AllowCredentials,MaxAge) are added by Lambda to responses and answer preflightOPTIONSrequests; don't also send CORS headers from the code (duplicateAccess-Control-Allow-Originvalues break browsers). - Throttling: reserved concurrency caps a URL (429 above it, and above 10 × the reserved value in requests per second); reserved concurrency 0 effectively deactivates the URL.
- Responses: return
statusCode,headers,body,cookiesandisBase64Encoded; a plain JSON value becomes a200withcontent-type: application/json. Put cookies in thecookiesarray, not inset-cookieheaders. InvokeMode:BUFFERED(default, up to 6 MB) orRESPONSE_STREAM. ⚠️ Changed: a streamed response can reach 200 MB (since 2025-07-31); the first 6 MB flow at full speed, the rest at up to 2 MBps. Older material may say 20 MB. Native streaming is for Node.js managed runtimes (other languages need a custom runtime or the Lambda Web Adapter). Streams also work throughInvokeWithResponseStreamand API Gateway REST proxy integrations; function URLs can't stream to clients inside a VPC, so useInvokeWithResponseStreamthrough an interface endpoint there. A streaming function keeps running, and billing, after the client disconnects.- IAM actions for governance:
lambda:CreateFunctionUrlConfig,lambda:UpdateFunctionUrlConfig,lambda:GetFunctionUrlConfig,lambda:ListFunctionUrlConfigs,lambda:DeleteFunctionUrlConfig; an SCP can deny any auth type other thanAWS_IAMwithlambda:FunctionUrlAuthType.
Developer angle
aws lambda create-function-url-config --function-name webhook --qualifier live \
--auth-type NONE \
--cors-config '{"AllowOrigins": ["https://app.example.com"], "AllowMethods": ["POST"]}'
# with the CLI you add both public statements yourself
aws lambda add-permission --function-name webhook --qualifier live \
--statement-id url-public --action lambda:InvokeFunctionUrl \
--principal '*' --function-url-auth-type NONE
aws lambda add-permission --function-name webhook --qualifier live \
--statement-id url-invoke --action lambda:InvokeFunction \
--principal '*' --invoked-via-function-url
# calling an AWS_IAM URL: sign the request with SigV4 for the lambda service
curl --aws-sigv4 "aws:amz:eu-west-1:lambda" \
--user "$AWS_ACCESS_KEY_ID:$AWS_SECRET_ACCESS_KEY" \
-H "x-amz-security-token: $AWS_SESSION_TOKEN" \
-H 'content-type: application/json' -d '{"ping": true}' \
https://abcdefghij1234567890.lambda-url.eu-west-1.on.aws/
A streaming handler (Node.js managed runtime; set the URL's invoke mode to RESPONSE_STREAM):
export const handler = awslambda.streamifyResponse(async (event, responseStream, context) => {
for (const part of ["header\n", "rows...\n", "footer\n"]) {
responseStream.write(part);
}
responseStream.end();
});
update-function-url-config --invoke-mode RESPONSE_STREAMswitches an existing URL;get-function-url-configshows the auth type, CORS and invoke mode.
Traps
- Trap: "Auth type
NONEneeds no resource policy." → Reality: the policy must allow bothlambda:InvokeFunctionUrlandlambda:InvokeFunction, or every call gets 403. - Trap: "Create the URL on version 12 for a stable endpoint." → Reality: URLs attach only to an alias or
$LATEST; use an alias that points at version 12. - Trap: "Reach the function URL privately through an interface VPC endpoint." → Reality: function URLs don't support PrivateLink; use the Lambda API endpoint or API Gateway private APIs.
- Trap: "Add CORS headers in the code and on the URL." → Reality: configure CORS on the URL only, or browsers see duplicate headers.
- Trap: "
429from a function URL means the URL is broken." → Reality: the function hit its concurrency (reserved concurrency or the account pool). - Trap: "Python functions stream natively." → Reality: native streaming is a Node.js runtime feature; other languages need a custom runtime or the Lambda Web Adapter.
Practice questions
-
A team exposes a webhook receiver through a Lambda function URL with auth type
NONE, created with the AWS CLI. Every request from the SaaS provider receives403 Forbidden, although the function's resource-based policy allowslambda:InvokeFunctionUrlfor all principals. What is the MOST likely cause?- A. Function URLs with auth type
NONErequire requests signed with SigV4. - B. The policy lacks a statement that allows
lambda:InvokeFunctionfor calls made through the function URL. - C. The function URL is attached to a published version instead of an alias.
- D. The function's reserved concurrency is too low for the webhook traffic.
- A. Function URLs with auth type
-
A company wants a mobile back end in a partner AWS account to call one of its functions through a function URL. Only signed requests from a single IAM role in that account may succeed. Which TWO configurations are required? (Select TWO.)
- A. Set the function URL's auth type to
AWS_IAM. - B. Add CORS settings that allow the partner's web origin.
- C. Create an interface VPC endpoint for the function URL in the partner's VPC.
- D. Grant the partner role
lambda:InvokeFunctionUrlandlambda:InvokeFunctionin both the function's resource-based policy and the role's identity policy. - E. Set reserved concurrency to 0 for all other callers.
- A. Set the function URL's auth type to
Answers
- B. New function URLs need both actions, and the CLI does not add the
lambda:InvokeFunctionstatement (scoped withlambda:InvokedViaFunctionUrl). A is wrong becauseNONEaccepts unsigned requests; C can't happen because URLs attach only to aliases or$LATEST; D would cause 429, not 403. - A, D.
AWS_IAMrequires SigV4-signed requests, and a cross-account caller needs the permissions on both sides. B matters only for browsers; C is impossible because function URLs don't support PrivateLink; E would throttle everyone, including the partner.
12. Observability with logs metrics and traces
Concept
- Lambda produces three kinds of telemetry with no extra code: logs (whatever the function writes to stdout or stderr, plus the platform's
START,ENDandREPORTlines), metrics in theAWS/Lambdanamespace, and, once enabled, X-Ray traces. - Logs go to CloudWatch Logs by default (
/aws/lambda/<function-name>); Amazon S3 and Amazon Data Firehose can be chosen as destinations, and advanced logging controls add structured JSON output, log-level filtering and custom log groups. - Tracing: Active tracing makes Lambda send segments for sampled requests; PassThrough (the default) only forwards the trace header. Custom subsegments, annotations and downstream calls need instrumentation in the code; AWS now points to OpenTelemetry (ADOT, or the AWS Lambda layer for OpenTelemetry) because the X-Ray SDKs are in maintenance mode.
Exam facts
- Logging permissions:
logs:CreateLogGroup,logs:CreateLogStream,logs:PutLogEvents(AWSLambdaBasicExecutionRole). Without them the function still runs but nothing is logged. The console adds the permission when you choose a log group; the CLI does not. - Advanced logging controls (
--logging-config):LogFormatText(default) orJSON; level filtering needsJSON, withApplicationLogLevelTRACE,DEBUG,INFO,WARN,ERRORorFATALandSystemLogLevelDEBUG,INFOorWARN(both default toINFO); messages below the chosen level are dropped, and a level set in code (for examplelogger.setLevel()) wins.LogGroupshares one group across functions (stream names then include the function name and version); custom group names must not start withaws/. Logs can take 5 to 10 minutes to appear. REPORTline:Duration,Billed Duration,Memory Size,Max Memory Usedand, on cold starts,Init Duration; the newer format addsStatus(timeout,error) andError Type. Timeouts logTask timed out after N seconds.- Key metrics:
Invocations(equals billed requests; excludes throttles),Errors(function errors, including timeouts),Throttles,Duration(supports percentiles; excludes Init),ConcurrentExecutions,DeadLetterErrors,DestinationDeliveryFailures,IteratorAge,AsyncEventsReceived,AsyncEventAge,AsyncEventsDropped,ProvisionedConcurrencySpilloverInvocations,RecursiveInvocationsDropped. - X-Ray: enable with
--tracing-config Mode=Active(SAMTracing: Active); the role needsxray:PutTraceSegmentsandxray:PutTelemetryRecords(AWSXRayDaemonWriteAccess). Lambda's sampling is fixed at the first request each second plus 5% of the rest and can't be configured. Each trace shows anAWS::Lambdasegment (service side) and anAWS::Lambda::Functionsegment (your code). The tracing mode is frozen in published versions. Tracing isn't supported for MSK, self-managed Kafka, Amazon MQ or DocumentDB event source mappings. - ⚠️ Changed: in
PassThroughmode Lambda doesn't send traces even when an upstream service such as API Gateway sampled the request; enableActivetracing to get Lambda segments. Older material may say upstream sampling alone produces Lambda traces._X_AMZN_TRACE_IDcarries the trace header (Java 17+ uses thecom.amazonaws.xray.traceHeadersystem property instead);AWS_XRAY_CONTEXT_MISSINGdefaults toLOG_ERROR;AWS_XRAY_DAEMON_ADDRESSpoints to the daemon that Lambda runs for you. - ⚠️ Changed: the X-Ray SDKs and daemon are in maintenance mode (security fixes only since 2026-02-25), and AWS recommends OpenTelemetry-based instrumentation. Older material may present the X-Ray SDK as the standard approach. Details: see notes/monitoring-guide.md.
- Recursive loop detection: Lambda tracks request chains through Lambda, SQS, SNS, S3 and EventBridge custom buses and stops the next invocation after about 16 loops (
RecursiveInvocationException, metricRecursiveInvocationsDropped, Health Dashboard and email notice; the stopped event goes to the DLQ or on-failure destination). Allow intentional loops withput-function-recursion-config --recursive-loop Allow. It needs supported SDK versions and doesn't see loops through other services such as DynamoDB. - CloudWatch Lambda Insights (an extension layer plus
CloudWatchLambdaInsightsExecutionRolePolicy) adds system-level metrics such as memory and CPU use. Logs Insights, subscription filters and alarms in general: see notes/monitoring-guide.md.
Developer angle
aws lambda update-function-configuration --function-name orders \
--logging-config LogFormat=JSON,ApplicationLogLevel=WARN,SystemLogLevel=INFO,LogGroup=/shop/orders
aws lambda update-function-configuration --function-name orders --tracing-config Mode=Active
aws logs tail /shop/orders --follow --since 10m
aws lambda put-function-recursion-config --function-name orders --recursive-loop Terminate
CloudWatch Logs Insights query over REPORT lines (memory headroom, latency, cold starts):
filter @type = "REPORT"
| stats max(@maxMemoryUsed / 1000 / 1000) as maxMemoryMB,
avg(@duration) as avgDurationMs,
count(@initDuration) as coldStarts
by bin(1h)
- With the configuration above, a Python
logger.debug(...)orlogger.info(...)call is dropped andlogger.warning(...)is kept, with no code change.
Traps
- Trap: "No logs means the function never ran." → Reality: check the execution role for
logs:CreateLogStreamandlogs:PutLogEvents;Invocationsshows whether it ran. - Trap: "Raise the X-Ray sampling rate in the function configuration." → Reality: Lambda's sampling is fixed (1 per second plus 5%).
- Trap: "API Gateway tracing is on, so Lambda segments appear." → Reality: turn on
Activetracing on the function as well. - Trap: "
Durationincludes the cold start." → Reality: readInit Durationin theREPORTline. - Trap: "A VPC-attached function can't log without a NAT gateway." → Reality: Lambda captures stdout and stderr and sends them to CloudWatch Logs itself (Lambda Managed Instances are the exception: they need a route to CloudWatch Logs and X-Ray).
- Trap: "Recursive loop detection covers every service." → Reality: only loops through Lambda, SQS, SNS, S3 and EventBridge custom buses.
Practice questions
-
A developer enables X-Ray tracing on an API Gateway stage, but the service map shows no Lambda nodes for the integrated function. The execution role already allows
xray:PutTraceSegmentsandxray:PutTelemetryRecords. What should the developer do?- A. Raise the X-Ray sampling rate in the function configuration to 100%.
- B. Set the
AWS_XRAY_CONTEXT_MISSINGenvironment variable toRUNTIME_ERROR. - C. Change the function's tracing configuration to
Active. - D. Add a layer that runs the X-Ray daemon inside the function.
-
After a teammate replaced a function's execution role, the function writes nothing to CloudWatch Logs, yet the
Invocationsmetric keeps rising and the function's downstream writes still happen. The team also wants DEBUG output only in the test stage. Which TWO actions address these needs? (Select TWO.)- A. Enable Active tracing so that log lines are forwarded through X-Ray.
- B. Add
logs:CreateLogStreamandlogs:PutLogEvents(for exampleAWSLambdaBasicExecutionRole) to the new execution role. - C. Increase the function's memory because logging stops when memory runs out.
- D. Add a resource-based policy statement that allows
logs.amazonaws.comto invoke the function. - E. Use the JSON log format and set a lower
ApplicationLogLevelon the test stage's function.
Answers
- C. In
PassThroughmode Lambda only forwards the header;Activetracing makes it send segments. A isn't configurable for Lambda; B changes SDK behaviour, not whether segments are sent; D is unnecessary because Lambda runs the daemon for you. - B, E. The new role lacks the CloudWatch Logs permissions, and JSON logging with
ApplicationLogLevelfilters levels per function configuration. A sends traces, not logs; C is unrelated to log delivery; D concerns who can invoke the function.
13. Performance tuning and limits
Concept
- Memory is the one performance dial: CPU scales in proportion to configured memory, so more memory often finishes CPU-bound work faster and the bill can stay flat or even shrink. Network bandwidth is 625 Mbps per environment by default; functions outside a VPC can request more, which then grows with memory.
- Cost = requests + duration (GB-seconds, rounded up to 1 ms) + optional extras (ephemeral storage above 512 MB, provisioned concurrency, SnapStart caching for Python and .NET, Init time). The free tier covers 1 million requests and 400,000 GB-seconds per month.
- Timeout: default 3 seconds, maximum 900 seconds (15 minutes). Longer or stateful jobs belong in Step Functions, ECS or Fargate, or durable functions. Many questions hinge on the hard limits collected below.
Exam facts
- Memory: 128 MB (default and minimum) to 10,240 MB in 1-MB steps. ⚠️ Changed: 1,769 MB buys one whole vCPU, and the 10,240 MB maximum gives up to 6 vCPUs. Older material may say 1,792 MB. Only multi-threaded code benefits beyond one vCPU. New accounts can start with lower memory and concurrency quotas that AWS raises with usage.
- Right-sizing: watch
Max Memory UsedinREPORTlines; test settings with AWS Lambda Power Tuning (an open-source Step Functions tool) or accept AWS Compute Optimizer recommendations (x86_64 functions only). - Timeout: default 3 s, maximum 900 s for standard functions. Lambda Managed Instances (the newer EC2-backed option) allow up to 5,400 s (90 minutes) for asynchronous and event source mapping invocations, except Amazon MQ and DocumentDB; synchronous calls stay at 15 minutes. Set the timeout with a margin over the slowest normal run and below the caller's own timeout (API Gateway integrations: see notes/api-gateway-guide.md).
- Pricing (US East (N. Virginia), x86, first tier; volatile): $0.20 per million requests. ⚠️ Changed: duration costs $0.0000166667 per GB-second (about $1 per 60,000 GB-seconds). Older material may say $1.00 per 600,000 GB-seconds. Check the pricing page for arm64 rates and other Regions.
- Worked example: 3 million requests a month at 512 MB and 200 ms average = 3,000,000 × 0.2 s × 0.5 GB = 300,000 GB-seconds, inside the free tier; requests beyond the free million cost 2 × $0.20 = $0.40.
- Other limits to know: environment variables 4 KB; resource-based policy 20 KB; 5 layers; 250 MB unzipped package; 50 MB direct upload; 10 GB container image;
/tmp512–10,240 MB; 6 MB synchronous and 1 MB asynchronous payload; 1,024 file descriptors and 1,024 processes or threads; 10 console test events; control-plane API calls 15 per second per Region, all such APIs combined (GetFunction100 per second); network bandwidth 625 Mbps per environment. Architecture: switching to arm64 (Graviton) requires code and native libraries built for arm64 and is set with--architecturesonupdate-function-code(orcreate-function); every supported runtime offers both architectures. - Good habits: initialize outside the handler, keep packages small, reuse connections, never let a function call itself in an unbounded loop, keep configuration in environment variables, and use queues or Step Functions instead of waiting inside a function. Alarm on
Errors,Throttles,Durationp99 andIteratorAge.
Developer angle
aws lambda update-function-configuration --function-name image-score \
--memory-size 2048 --timeout 60
aws lambda update-function-code --function-name image-score \
--zip-file fileb://image-score-arm64.zip --architectures arm64
aws lambda get-function-configuration --function-name image-score \
--query '{memory: MemorySize, timeout: Timeout, arch: Architectures, tmp: EphemeralStorage.Size}'
- Duration cost per invocation = memory in GB × billed seconds × the GB-second price: 512 MB for 9 s is 4.5 GB-s, and 1,536 MB for 3 s is also 4.5 GB-s, so three times the memory can make CPU-bound code three times faster at no extra duration cost (CPU grows in proportion to memory); the bigger setting is cheaper only when the run shortens even more. Single-threaded code stops speeding up at about 1,769 MB (one vCPU).
- Power Tuning runs the same payload at several memory sizes and charts cost against duration, so the "sweet spot" is measured instead of guessed.
Traps
- Trap: "Lambda has a separate CPU setting." → Reality: CPU follows memory; raise memory.
- Trap: "More memory always costs more." → Reality: shorter duration can offset the higher rate; measure it.
- Trap: "Set the timeout to 900 seconds everywhere to be safe." → Reality: a long timeout keeps stuck invocations running (cost, held concurrency, larger SQS visibility timeouts); size it to the real workload and to the caller's timeout.
- Trap: "A 25-minute batch job fits in Lambda if I raise the timeout." → Reality: 15 minutes is the hard maximum for standard functions; split the work, use Step Functions or a container service.
- Trap: "More vCPUs speed up single-threaded code." → Reality: beyond one vCPU (1,769 MB) only multi-threaded code benefits.
Practice questions
-
A single-threaded image-scoring function configured with 512 MB takes 9 seconds per request, and CPU utilization stays at its limit. The team needs responses under 3 seconds without rewriting the code, at the lowest cost that meets the target. What should the developer do first?
- A. Increase memory in steps (for example with AWS Lambda Power Tuning) toward about 1,769 MB and measure duration and cost.
- B. Set memory to 10,240 MB so that the function receives 6 vCPUs.
- C. Configure provisioned concurrency so that each request starts in a warm environment.
- D. Increase the function timeout from 10 seconds to 60 seconds.
-
A nightly job processes about 2 million records in a single Lambda invocation that needs roughly 25 minutes and keeps timing out. The team wants to stay serverless with minimal operational overhead. Which TWO approaches meet the requirement? (Select TWO.)
- A. Request a Service Quotas increase that raises the function timeout to 30 minutes.
- B. Split the records into chunks and process them in parallel with a Step Functions Distributed Map state that invokes the function per chunk.
- C. Increase memory to 10,240 MB so the job finishes within the current timeout.
- D. Publish the records in chunks to an SQS queue and let an event source mapping invoke the function with batches.
- E. Write intermediate results to
/tmpso that they survive the timeout and the next run continues.
Answers
- A. CPU scales with memory up to one full vCPU at 1,769 MB, which is what single-threaded code can use; measuring finds the cheapest size that meets 3 seconds. B pays for 6 vCPUs the code can't use; C removes cold starts but not CPU-bound duration; D only lets the slow run continue longer.
- B, D. Both spread the work over many short invocations that each stay under 15 minutes, with managed retries. A fails because the 900-second maximum isn't adjustable; C isn't guaranteed to cut a 25-minute job below 15 minutes; E fails because
/tmpbelongs to one environment and the next invocation may run elsewhere.
Quick decision rules
Invocation and failure handling (§1–§3)
- If you see a synchronous caller that must know whether the function failed, think
FunctionError/X-Amz-Function-Error(the status is still 200). - If you see "check permissions without running the code", think
InvocationType=DryRun. - If you see a payload over 6 MB (synchronous) or 1 MB (asynchronous), think store it in S3 and pass the key, or stream the response.
- If you see failed asynchronous events that must be kept with their error details, or chaining without SDK calls, think destinations (on-failure: SQS, SNS, S3, Lambda, EventBridge) rather than a DLQ.
- If you see asynchronous events that are worthless after N minutes or must be retried at most once, think
MaximumEventAgeInSecondsandMaximumRetryAttempts. - If you see an SQS-triggered function whose failed messages must be kept, think DLQ in the source queue's redrive policy.
- If you see a whole SQS batch reprocessed because of one bad message, think
ReportBatchItemFailureswithbatchItemFailures. - If you see SQS messages processed twice while the function is still running, think visibility timeout ≥ 6 × function timeout.
- If you see a stalled Kinesis or DynamoDB shard and a rising
IteratorAge, thinkBisectBatchOnFunctionError,MaximumRetryAttempts,MaximumRecordAgeInSecondsand an on-failure destination. - If you see a stream consumer that can't keep up, think
ParallelizationFactor(up to 10 per shard, order kept per partition key) or more shards. - If you see "only process some of the messages", think event filtering (
FilterCriteriaonbody,dataordynamodb). - If you see one queue using up a function's concurrency, think
MaximumConcurrencyon that SQS mapping.
Permissions (§4)
- If you see S3, SNS, EventBridge, an ALB or another account unable to invoke a function, think resource-based policy (
add-permissionwith--source-arnand--source-account); an EventBridge rule can instead use an execution role on the target. - If you see an event source mapping failing with access errors, think execution role permissions on the source.
- If you see several principals, an explicit Deny or rich conditions on who may invoke, think
put-resource-policy(full JSON, overwrites the policy).
- If you see a downstream database overwhelmed by a scaling function, think reserved concurrency (or RDS Proxy).
- If you see
TooManyRequestsException(429) with risingThrottles, think a concurrency quota increase in Service Quotas, or reserved concurrency for the critical function. - If you see predictable peaks that need the lowest latency, think provisioned concurrency with scheduled or target-tracking Application Auto Scaling.
- If you see slow Java, Python or .NET initialization and no budget for idle capacity, think SnapStart.
- If you see cold starts despite provisioned concurrency, think the caller isn't invoking the alias or version that has it.
Releases and packaging (§7–§8)
- If you see a canary or linear release with automatic rollback, think CodeDeploy with a weighted alias (
AutoPublishAliasandDeploymentPreferencein SAM). - If you see dependencies above 250 MB unzipped, think container image (up to 10 GB); libraries shared by many .zip functions, think layers (at most 5, under
/opt). - If you see a new image pushed but old code running, think
update-function-code(the tag was pinned to a digest); CloudFormation ignoring new S3 code, think a newS3KeyorS3ObjectVersion. - If you see "only signed code may be deployed", think code signing configuration set to
Enforce.
Configuration, networking and endpoints (§9–§11)
- If you see credentials or API keys for a function, think Secrets Manager with the Parameters and Secrets extension (cached), not environment variables.
- If you see
KMSAccessDeniedExceptionorKMSDisabledException(502) on every invoke, think the KMS key that protects the environment variables. - If you see a VPC-attached function that lost internet access, think NAT gateway from private subnets (or VPC endpoints for AWS services).
- If you see files shared by concurrent invocations, think EFS access point mounted under
/mnt/. - If you see a cheap HTTPS endpoint for a webhook, think function URL (
NONEwith both invoke permissions, orAWS_IAMwith SigV4); Cognito authorizers, API keys, caching or validation, think API Gateway. - If you see responses above 6 MB or a need for faster time to first byte, think response streaming (
RESPONSE_STREAM, up to 200 MB).
Observability and tuning (§12–§13)
- If you see no logs from a function that is clearly running, think missing
logs:CreateLogStream/logs:PutLogEventson the execution role. - If you see no Lambda segments in X-Ray, think
Activetracing on the function plusAWSXRayDaemonWriteAccess. - If you see a CPU-bound function, think more memory (CPU scales with it), measured with Power Tuning.
- If you see a job longer than 15 minutes, think splitting the work with SQS or Step Functions, or a container service.
Changed vs older material
Learn the Current column; older practice material may still show the Older material may say value. Lambda rows awaiting confirmation are listed in the ingest report; confirmed rows, with verification dates, live in notes/current-facts.md.
| Topic | Older material may say | Current (verified 2026-10-12) | Source |
|---|---|---|---|
| §1 · §2 · Asynchronous invocation payload | 256 KB | 1 MB (quota 1,024 KB, not adjustable) since 2025-10-24; one request billed for the first 256 KB plus one per extra 64 KB | What's New |
| §1 · Firehose name | Kinesis Data Firehose | Amazon Data Firehose (renamed 2024-02-09; row kept under sqs-sns-kinesis in notes/current-facts.md) |
What's New |
| §2 · Asynchronous destinations | SQS, SNS, Lambda or EventBridge | On failure also an S3 bucket (failure records only); on success SQS, SNS, Lambda or EventBridge | AWS docs |
| §2 · §4 · EventBridge rule permission to invoke | A resource-based statement for events.amazonaws.com is always required |
A resource-based statement or an IAM execution role on the target (RoleArn allowing lambda:InvokeFunction) since 2025-03-11; the statement is needed only when no role is set (row kept under monitoring in notes/current-facts.md) |
AWS docs |
| §3 · Event source mapping sources | Kinesis, SQS and DynamoDB Streams | Also Amazon MSK, self-managed Apache Kafka, Amazon MQ and Amazon DocumentDB | AWS docs |
| §3 · SQS batch size | 1–10 messages | Standard queues up to 10,000 (batching window ≥ 1 s above 10), FIFO up to 10, default 10 | AWS docs |
| §3 · SQS scaling | +60 instances per minute, up to 1,000 batches at once | Starts at 5, adds up to 300 concurrent invocations per minute (since 2023-11-06), at most 1,250 per mapping; provisioned mode scales further | AWS docs |
| §3 · Discarded-batch destinations | SQS or SNS | SQS, SNS or S3 (Kinesis, DynamoDB and Kafka mappings; S3 receives the whole batch) and Kafka topics for Kafka mappings | AWS docs |
| §4 · Function resource-based policies | Built one Allow statement at a time with AddPermission, limited conditions |
Full JSON via PutResourcePolicy (multiple principals and actions, explicit Deny, all global condition keys; overwrites the policy) since 2026-08-25; 20 KB |
AWS docs |
| §5 · Concurrency scaling rate | Account-wide burst of 500–3,000, then +500 per minute | 1,000 execution environments every 10 s per function (since 2023-11-26), not adjustable | AWS docs |
| §5 · Throttle error and quota increases | "ThrottleError 429"; open a support ticket for more concurrency | TooManyRequestsException (429) with ConcurrentInvocationLimitExceeded or ReservedFunctionConcurrentInvocationLimitExceeded; request increases in Service Quotas (L-B99A9384) |
AWS docs |
| §6 · Init phase billing | Init is free for on-demand .zip functions on managed runtimes | Billed for every configuration since 2025-08-01 | AWS blog |
| §6 · SnapStart runtimes and packaging | Java only, .zip packages only | Java 11+, Python 3.12+, .NET 8+; .zip and container images on AWS base images; Python and .NET pay for caching and restores | AWS docs |
| §8 · Code storage per Region | 75 GB | 300 GB of Lambda-managed storage (since 2026-07-15); self-managed S3 code storage doesn't count | What's New |
| §8 · Runtime-included AWS SDK | Every function gets the AWS SDK | Only Node.js, Python and Ruby runtimes include one; AWS recommends bundling your own | AWS docs |
| §8 · Go and Rust | Community-supported custom runtimes | OS-only runtime provided.al2023 (go1.x deprecated 2024-01-08) |
AWS docs |
| §8 · Container base images | amazon/aws-lambda-nodejs:12, built on Amazon Linux 2 |
public.ecr.aws/lambda/<runtime>:<tag>; Amazon Linux 2023 for Node.js 20+, Python 3.12+, Java 21+, .NET 8+, Ruby 3.3+ |
AWS docs |
| §9 · Where secrets go | KMS-encrypted environment variables are the place for secrets | AWS recommends Secrets Manager instead of environment variables for credentials (Parameters and Secrets extension for cached reads) | AWS docs |
§10 · /tmp encryption |
/tmp is protected only if your code encrypts it with KMS data keys |
Always encrypted at rest with an AWS managed key, no setup; KMS data keys only for your own key or application-level encryption | AWS docs |
| §11 · Function URL invoke permissions | lambda:InvokeFunctionUrl alone |
lambda:InvokeFunctionUrl plus lambda:InvokeFunction (scope with lambda:InvokedViaFunctionUrl) for URLs created since October 2025, else 403 |
AWS docs |
| §11 · Streamed response size | 20 MB | 200 MB (since 2025-07-31); first 6 MB uncapped, then 2 MBps | What's New |
| §12 · PassThrough tracing | Lambda sends traces automatically when the upstream request was sampled | PassThrough only forwards the header; enable Active tracing for Lambda segments |
AWS docs |
| §12 · X-Ray SDK | X-Ray SDK plus daemon is the standard way to instrument | Maintenance mode since 2026-02-25; OpenTelemetry recommended (row kept under monitoring in notes/current-facts.md) |
AWS docs |
| §13 · Memory for one full vCPU | 1,792 MB | 1,769 MB (up to 6 vCPUs at 10,240 MB) | AWS docs |
| §13 · Duration price | $1.00 per 600,000 GB-seconds | $0.0000166667 per GB-second (x86, US East (N. Virginia), first tier) ≈ $1 per 60,000 GB-seconds | AWS pricing |