An agent that reasons well and a deployment that's actually safe to run in production are two different achievements — this is the second one.
1Least Privilege Is the Boundary That Actually Matters
Everything built across this masterclass — the reasoning loop, the tool belt, the retrieval, the fine-tuned classifier — runs inside code that could, in principle, contain a bug or be influenced by unexpected input. The IAM role attached to the Lambda function is what actually limits the real-world consequences of that: scope it to precisely the model and resources the agent needs, nothing broader.
2What It Actually Took to Ship This Agent
Every piece across this masterclass existed because a plain chatbot genuinely couldn't do it: a real loop for multi-step tasks, real chunking and retrieval for grounded answers, a real trade-off analysis and dataset for specialization, and a real, permission-scoped deployment to actually run in production. None of it was optional decoration — each piece closed a real gap a single completion call couldn't close on its own.
3Step-by-Step Breakdown
The Function's Permissions Are Its Real Boundary. By default, an AWS Lambda function's execution role should grant it nothing beyond what it actually needs — not broad access to every S3 bucket or every Bedrock model in the account. TriageAgent's role should name the exact model it's allowed to invoke and nothing else, so a bug or a compromised dependency can't silently do more than the agent was ever meant to.
Why scope TriageAgent's IAM policy to one specific Bedrock model ARN instead of granting bedrock:InvokeModel on all resources ("Resource": "*")?
- →Least privilege: if the function is ever compromised or misused through a bug, its real blast radius is limited to exactly the one model it was actually meant to call, not every model available in the account.
- →Scoping the resource ARN is purely a cost-optimization setting with no security implication.
TriageAgent, Complete. You built a real agent from nothing: a genuine ReAct reasoning loop, a scoped tool belt, real sliding-window chunking, bounded memory, real keyword retrieval, a real fine-tuning dataset and honest holdout evaluation, and a real, least-privilege AWS deployment behind API Gateway and Lambda. This is the actual mechanism behind a production support-triage agent — not a black box anymore.
Level Up 🚀
Advanced cheat sheets, SEO tricks, and interview prep for this topic.
Browser Support
Fully supported.
Fully supported.
Fully supported.
Fully supported.
Accessibility (A11y)
1Log Every Real Action an Agent Takes in Production
A deployed agent's escalate_ticket or similar side-effecting action should be logged with enough detail that a human reviewing an incident later can reconstruct exactly what the agent did and why, not just that 'something happened.'
logger.info(f"escalated ticket {ticket_id}, priority={priority}")SEO Implications
- 1
Target 'least privilege IAM for Bedrock agent' and 'AI agent production checklist' separately
Developers shipping their first agent search for the specific IAM practice and the broader readiness checklist as distinct concerns.
Best Practices
Review and Tighten IAM Policies as a Standard Part of Every Deployment, Not a One-Time Setup Step
As an agent's tool belt grows over time, its IAM role should be reviewed to ensure it still reflects only what the current set of tools actually requires, not permissions left over from an earlier version.
Frequent Bugs
Granting a Lambda execution role broad, unscoped permissions during development and never tightening them before shipping.
Development conveniences like `"Resource": "*"` should always be narrowed to the specific resources actually used before a function is exposed to real production traffic.
Real-World Examples
Contained Blast Radius
If a bug ever caused TriageAgent's Lambda function to attempt an unintended action, a tightly scoped IAM role means that attempt fails immediately with an access-denied error, rather than silently succeeding against a resource the agent was never meant to touch.
AccessDeniedException: not authorized for this resource