
What an AWS Lambda function is, and what its execution role decides
Published
From the course: Hands-on Cloud for Freshers, session 7: Code without servers: Lambda
An interviewer leans back and asks, “So what is a Lambda function, really?” You know the slogan: code without servers. Then the follow-up comes: “Your function returns a 502 when it writes to the database. Why?” The slogan does not answer that. This article claims that a fresher can answer both questions with two ideas. First, a Lambda function is a small contract between your code and a runtime that starts, reuses and discards environments for you. Second, the execution role decides everything that code may touch. Most first failures happen because the role and the code disagree, and the article works through the questions in the order an interviewer tends to ask them.
A Lambda function is code plus configuration, run only on demand
The question: “What is a Lambda function, in your own words?”
A Lambda function is your code plus a few settings that AWS runs only when it is invoked, meaning called. The settings are the runtime, the handler, the memory, the timeout and the role. You never see or patch a machine. AWS creates an environment to run the code in, scales it out when requests arrive together, and throws it away when things go quiet.
Think of it as a food stall that only lights its stove when an order arrives. You write the recipe and pin a few instructions to the wall: which cuisine to cook (the runtime), which recipe card to start from (the handler), and how long a dish may take before it is abandoned (the timeout). The stall owner lights the stove, opens a second stove for a second order, and lets them go cold. You pay per order cooked, not for the stall standing there.
A strong answer names what this changes. Without a server to keep running, “is the server up?” stops being the hard question. The hard questions become who may call this code, what may this code touch and what happened on that one request.
Start-up code runs once per environment; the handler runs once per request
The handler is a contract that is called once per request
The question: “What does Lambda actually call, and what does it pass in?”
Lambda calls one function in your code, the handler, once for each request. In Python, a file named lambda_function.py that defines lambda_handler(event, context) has the handler lambda_function.lambda_handler. The event is a document describing what triggered the call. For a web request it holds the method, path, headers and body. The context describes the invocation itself, such as its request ID and its log group name.
Whatever the handler returns becomes the response. If you return a dictionary with statusCode, headers and body, Lambda turns it into the HTTP response. If you return plain JSON with no status code, Lambda assumes status 200 and content type application/json.
Interviewers like the next detail because it shows you understand the lifecycle. Code outside the handler, such as imports and creating an SDK client, runs once when an environment starts. Later invocations in that same environment reuse it. That is why the Lambda documentation advises creating SDK clients outside the handler: you pay the start-up cost once per environment, not once per request.
Think of it as the cook laying out knives before service rather than fetching them for every dish.
Two defaults are worth knowing, and so is how to choose a runtime. A runtime is named by an identifier such as python3.14, and each identifier has a published deprecation date. When the runtimes page was checked on 2026-10-02, python3.14 was the newest generally available Python runtime. python3.15 was listed as a public preview, which the page says is not for production.
The settings a function carries, with their defaults and what each one governs
The execution role is the only thing deciding what your code may touch
The question: “Where does your function get permission to write to the database?”
When Lambda runs your code, the code acts as the execution role. A role is an identity in IAM, the AWS permissions service, and it has two halves:
- a trust policy saying who may take it on, which for an execution role is the Lambda service,
lambda.amazonaws.com; - permission policies saying what it may do once it has been taken on.
Think of it as a staff badge. The badge opens the store room and the noticeboard and no other door. The cook did not choose the badge. Whoever hired them did, and the badge is checked at every door.
The answer that impresses is this one: there are no keys in the code. The Python SDK finds temporary credentials for the role in the function’s environment by itself. The Lambda documentation says you do not need to provide credentials to set up a client. If a candidate pastes an access key into a function, it shows they have not understood the role.
The trust policy lets Lambda take on the role; permission policies decide which doors it opens
The default role explains the classic 502
The question: “You created a function with every default, and saving a record returns 502. What happened?”
This is the failure a first attempt usually hits. You write a handler that saves a note to a DynamoDB table, which is a managed key-value database. You create the function in the console and accept the defaults. A request returns a bare 502. A status in the 5XX range is what a function URL, the function’s public web address, returns when the function itself fails. The response gives no reason.
The function’s log has the reason:
[ERROR] ClientError: An error occurred (AccessDeniedException) when calling the PutItem operation:
User: arn:aws:sts::111122223333:assumed-role/hoc-notes-api-role-x1y2/hoc-notes-api is not
authorized to perform: dynamodb:PutItem on resource: arn:aws:dynamodb:ap-south-1:111122223333:table/hoc-notes
The code was fine. The console’s default option creates a new role with the managed policy AWSLambdaBasicExecutionRole. That policy grants exactly three actions: logs:CreateLogGroup, logs:CreateLogStream and logs:PutLogEvents. So the function could log its own error, but it could not save the note.
The role may write logs but not the table, so the caller sees only 502 while the log names the cause
Read the error as a sentence. The subject is the role, the verb is the action, and the object is the table. Every permission problem inside a function reads that way, so the fix is always the same: add exactly that action, on exactly that resource, to the role. In the console, that means choosing Use another role under Permissions and picking a role built for read and write on the one table, with basic logging added.
An interviewer may push: “Why not just attach an administrator policy?” It would make the error go away. It would also mean that a bug in this one function could change anything in the account. In our view, saying that unprompted is one of the clearest signs a fresher has thought about permissions rather than just made them work.
Who may call the function is a separate decision
The question: “So the role controls who can call it?”
No, and this is the trap. The execution role decides what the function may call. A different policy decides who may invoke the function: the resource-based policy, which is attached to the function itself.
The two failures look alike but happen on opposite sides of the function. A 403 from the function URL, with no START line in the log, means the request was refused before your code ran. In that case the resource-based policy is the place to look. An AccessDeniedException inside the log means the code ran and the role was refused. Being able to tell these apart in one sentence is worth more in an interview than any definition.
Two permission failures, on opposite sides of the function
A small notes function shows each idea in a few lines
The question: “Walk me through a function you wrote.”
A useful one to describe is a notes API: a single Python handler that lists notes on GET and adds a note on POST. Each line choice answers one of the ideas above.
- The client is created at module level.
table = boto3.resource("dynamodb").Table(TABLE_NAME)sits outside the handler, so each environment pays for it once. - The table name comes from an environment variable,
TABLE_NAME, with a fallback. The configuration stays out of the code. - The method is read from the event, at
event["requestContext"]["http"]["method"]. A function URL delivers requests in the same payload format version 2.0 shape that Amazon API Gateway uses. - The body is decoded when
isBase64Encodedis true. That flag means Lambda encoded a binary request body as text. - Bad input returns 400, not an exception. Empty text, invalid JSON or text over 500 characters each get a clear error. An exception reaches the caller only as the opaque 5XX described earlier.
json.dumps(..., default=str)handles DynamoDB numbers. The SDK returns them as PythonDecimalvalues, whichjson.dumpsrefuses on its own.OPTIONSreturns 204 because browsers send it before some cross-origin calls.
Each kind of request gets a deliberate status code instead of an unhandled failure
There is one honest weakness to mention before the interviewer finds it. The GET handler uses table.scan(), which reads every item, up to 1 MB per call, and signals when more remains. That is fine for a small table. For a large one it is the wrong tool, and you would design a key you can query by instead. Naming the limit of your own code reads as experience, not weakness.
The log is the only witness to a single request
The question: “How would you find out what happened on one failed request?”
A function has no screen, so its log is the only record of what one request did. Lambda sends output to CloudWatch Logs, the AWS logging service, as long as the role allows the three logs: actions. By default the log group is /aws/lambda/<function-name>, and each environment writes its own stream inside it.
Every invocation writes START, END and REPORT lines, each carrying the request ID. The REPORT line gives Duration, Billed Duration, Memory Size and Max Memory Used. It also shows Init Duration, but only when a new environment had to start. Anything the code prints appears between START and END.
Think of it as the stall’s order book: one entry per order, written whether the dish came out right or not.
What to practise before the interview
Practise these until you can say each one aloud without notes:
- Explain the handler contract, including what runs once per environment and what runs once per request.
- Name the two halves of an execution role and what
AWSLambdaBasicExecutionRoleallows. - Read an
AccessDeniedExceptionas subject, verb and object, then state the exact fix. - Separate a 403 with no START line from an AccessDenied inside the log.
- Defend one design choice in your own function, and name one of its limits.
Key takeaways
- A Lambda function is code plus configuration that runs on demand. The handler is called once per request, and code outside it runs once per environment.
- The execution role decides what the code may touch. Its trust policy names
lambda.amazonaws.com, and the code holds no keys. - The default role can only write logs. That is why a first database write often fails with a bare 5XX.
- Fix permission errors with exactly the missing action on exactly that resource, never with broad access.
- Who may call the function is decided by a separate resource-based policy, and the log tells you which side refused.
This article does not settle how to authenticate callers properly, how to configure CORS (the headers that let a browser page read the function’s responses), or what any of this costs at scale. Each of those is a decision in its own right.