Appearance
18.3 — AWS Compute and Identity for AI Systems
Version caveat: AWS service capabilities and defaults change continuously. The concepts and trade-offs below are durable; specific quotas, default limits, and console/CLI details should be verified against current AWS documentation before quoting them to a customer.
1. What is it?
The core AWS compute services (EC2, ECS, EKS, Lambda, ECR) and the identity service underlying all of them (IAM) — taught as a decision framework for "where does my AI application's code actually run," not a memorized list of service names.
2. Why does it exist?
18.1/18.2 taught you to reason about a Linux host and its network. This chapter answers where that host actually comes from in AWS, and — just as important — who is allowed to do what to it and the resources around it. IAM is placed first, deliberately, because every other AWS service's security model is built on IAM — nothing else in this chapter (or 18.4/ 18.5) makes sense without it.
3. What problem does it solve?
It solves "which AWS compute service should this specific AI workload run on, and why" — a question every AI FDE gets asked, directly or implicitly, in nearly every deployment conversation (Part 14, 18.23), and one that has a genuinely different right answer depending on the workload's shape (a bursty webhook handler vs. a long-running LangGraph agent vs. a GPU-bound self-hosted model).
4. How does it work internally?
IAM — Identity and Access Management
IAM controls who (a user, a role, a service) can do what (an action, like s3:GetObject) to which resource (a specific S3 bucket, say), evaluated via policies — JSON documents attaching permissions to an identity. The single most important IAM concept for an AI FDE is the IAM role (as distinct from an IAM user): a role is an identity with permissions that something assumes temporarily — an EC2 instance, an ECS task, a Lambda function — rather than a long-lived credential belonging to a person. An AI application's code should almost never contain a static AWS access key; instead, it runs as (or attached to) a role, and the AWS SDK picks up temporary, automatically-rotated credentials from the environment. This is the AWS-specific instance of Part 9.5's least- privilege and no-hardcoded-secrets principles, and it is the single highest-leverage AWS security practice in this entire chapter.
BAD: application code contains AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY
hardcoded or in a long-lived .env file with broad permissions
GOOD: application runs as an ECS task with an attached IAM Task Role
scoped to exactly: s3:GetObject on one specific bucket/prefix,
secretsmanager:GetSecretValue on one specific secret ARN,
nothing else — the SDK obtains short-lived credentials
automatically, with zero credential material in the code or imageLeast privilege in IAM means scoping a policy to the exact actions and exact resource ARNs needed — not s3:* on *, but s3:GetObject and s3:PutObject on arn:aws:s3:::my-bucket/documents/* specifically. This is not a compliance formality — it is the concrete, checkable answer to "what's the actual blast radius if this specific service is compromised," directly connecting to Part 9's excessive-agency reasoning applied at the infrastructure-identity layer instead of the AI-tool layer.
IAM mechanics: trust policies, AssumeRole, policy evaluation, and cross-account access
The IAM content above covers what a role is and why an application should use one instead of a static credential. The mechanics of how a role actually gets used — and how access crosses account boundaries, which is a near-daily reality in FDE work — deserve their own treatment, since nothing else in IAM (or the rest of this chapter) makes sense without them.
Trust policy vs. permission policy — two different documents, two different questions. Every IAM role has exactly two kinds of policy attached to it, answering two entirely different questions:
- A permission policy (section 4's examples above) answers "once assumed, what can this role actually do" — the
s3:GetObject-on-a- specific-bucket kind of statement. - A trust policy (every role has exactly one) answers "who is allowed to assume this role in the first place" — a separate JSON document, attached to the role itself, listing which principals (an AWS account, a specific IAM role/user ARN, an AWS service like
ecs-tasks.amazonaws.com) are permitted to callsts:AssumeRoleagainst it.
Conflating these two is a common, consequential mistake: a role can have an extremely narrow, well-scoped permission policy and still be a serious security problem if its trust policy allows anyone to assume it — the permission policy limits what an attacker could do once inside, but the trust policy is the actual front door.
sts:AssumeRole mechanics. AssumeRole is the AWS STS (Security Token Service) API call that exchanges proof of an allowed identity (per the target role's trust policy) for a set of temporary credentials (an access key, secret key, and session token, expiring after a configurable duration — typically up to a few hours) scoped to that role's permission policy. This is the literal mechanism behind section 4's "an EC2 instance/ECS task assumes a role" language: the underlying compute service calls AssumeRole on your behalf, the SDK picks up the resulting temporary credentials automatically, and — critically — those credentials expire and are automatically refreshed, meaning there is no long-lived secret to leak in the first place, only a short window of temporary access even in a worst-case compromise.
Policy evaluation order: explicit deny always wins. When multiple policies apply to a single request (a permission policy plus a permission boundary plus, in an AWS Organizations setup, an SCP), AWS evaluates them with one non-negotiable rule: an explicit Deny in any applicable policy always overrides any Allow, regardless of how many other policies grant the action. Absent any explicit statement either way, the default is deny (an action is only permitted if some policy explicitly allows it). This ordering — explicit deny, then explicit allow, then default deny — is what makes permission boundaries and SCPs (below) work as genuine ceilings rather than merely additional grants.
Permission boundaries and SCPs — ceilings, not grants. A permission boundary is a policy attached to an IAM user or role that sets the maximum permissions that identity's own permission policies can ever grant, no matter how permissive those permission policies are written — useful for letting a team self-manage roles for their own service (they can attach whatever permission policy they want) while guaranteeing none of those roles can ever exceed a boundary you control centrally. A Service Control Policy (SCP), in an AWS Organizations multi-account setup, does the same thing at the account level — an account-wide ceiling no identity in that account can exceed, regardless of what IAM policies exist inside it. Neither a permission boundary nor an SCP grants any permission by itself; both only restrict what an otherwise-valid Allow can actually do — the explicit-deny-wins evaluation order above is exactly why this works: exceeding the boundary/SCP ceiling behaves as an implicit deny that no permission policy inside it can override.
Cross-account access — the FDE-relevant case. An IAM role's trust policy can name a different AWS account as a trusted principal, not just identities within the same account — this is the mechanism behind nearly every FDE deployment engagement where you (from your own or your consulting firm's AWS account) need to deploy and operate the Enterprise AI Assistant inside a customer's AWS account, without the customer ever handing you a long-lived credential for their account.
IAM User/Role: fde-engineervendor account 111111111111 — permission policy allows sts:AssumeRole on the customer role's specific ARN only
IAM Role: ai-assistant-deploy-rolecustomer account 222222222222 — trust policy ONLY trusts fde-engineer's ARN + requires an External ID (prevents confused-deputy)
Temporary credentials returned, valid only for the session duration and only for what ai-assistant-deploy-role's OWN permission policy allows — the FDE's access to the customer account is fully bounded by that one role, fully revocable by the customer at any time (delete/edit the trust policy), and fully visible in the customer's own CloudTrail (18.5) as this specific role.
The External ID in the trust policy's Condition block (a value only the customer and the vendor know, required as part of the AssumeRole call) exists specifically to prevent the confused-deputy problem: without it, if the vendor's role ARN were ever reused to set up cross-account trust for a different, unrelated customer, that other customer could potentially assume a role they shouldn't have access to, simply by knowing the vendor's role ARN (which is not secret). Requiring an External ID means knowing the ARN alone is insufficient — this is the concrete, standard AWS-recommended mitigation, not an optional hardening step, for exactly the cross-account engagement pattern most AI FDE deployments into a customer's own AWS account actually use.
This is precisely 18.22's Design 6 scenario (deploying inside a customer's VPC) at the identity-mechanics level: 18.22 discusses the deployment shape; this is the actual trust-policy/AssumeRole/External-ID mechanism that makes that deployment model secure and auditable rather than requiring the customer to hand over a static, long-lived credential.
EC2 — Elastic Compute Cloud
An EC2 instance is a virtual machine — the most direct, most flexible, and most operationally-heavy compute option: you choose the OS image, you patch it, you manage its lifecycle. For an AI workload, EC2 remains the right choice specifically when you need full control over the runtime — most commonly, GPU instances for self-hosting a model (18.16), or when a customer's constraints (18.23) require a specific, audited VM configuration that a managed service abstracts away.
EC2 instances come in families optimized for different resource ratios — general purpose (m), compute-optimized (c), memory-optimized (r), and GPU/accelerated-computing instances (p, g families — 18.16's self-hosting territory) — choosing a family matched to the actual workload shape (GPU-bound inference vs. a memory-heavy application) rather than defaulting to general-purpose out of habit. Pricing comes in three models with a real cost/commitment trade-off: On-Demand (pay per second/hour, no commitment, most expensive per unit), Reserved Instances/Savings Plans (a 1- or 3-year commitment in exchange for a substantial discount — right for predictable, steady workloads, 18.18), and Spot Instances (spare AWS capacity at a steep discount, but reclaimable by AWS with short notice — see the failure-modes subsection below). An AMI (Amazon Machine Image) is the template an EC2 instance boots from (OS plus any pre-installed software); EBS (Elastic Block Store) is the persistent, network-attached disk volume an instance uses for storage that survives an instance stop/restart (distinct from ephemeral instance-store volumes, which don't).
ECS — Elastic Container Service
ECS runs containers (Part 7.1) without you managing the underlying VMs directly when using Fargate (its serverless launch mode) — you define a task (a container spec: image, CPU/memory, environment) and AWS runs it. This is the most common, lowest-operational-overhead way to run a containerized FastAPI + LangGraph AI service in AWS, and is frequently the right default choice for a straightforward AI application that doesn't have Kubernetes-specific requirements already.
EKS — Elastic Kubernetes Service
EKS is managed Kubernetes (18.9 covers Kubernetes itself in depth) — AWS runs the Kubernetes control plane; you run your workloads on it (on EC2 worker nodes, or Fargate). EKS is the right choice specifically when you need Kubernetes's specific capabilities (complex multi-service orchestration, a customer's existing Kubernetes-based platform standard — 18.23's "we already use Kubernetes" scenario) — not merely because Kubernetes is well-known; ECS solves the same "run my containers reliably" problem with meaningfully less operational complexity when you don't specifically need Kubernetes's extra capabilities.
You run workloads on EKS via one of two compute models: managed node groups (EC2 instances AWS provisions and patches on your behalf, but which you still choose the instance type/count/Spot-vs-On-Demand mix for) or Fargate profiles (serverless — no EC2 instances to manage at all; specific pod labels/namespaces are matched to run on Fargate instead of a node group). Managed node groups fit workloads needing GPU instances (self-hosted models, 18.16) or fine-grained control over instance type; Fargate profiles fit workloads wanting Lambda-like "no server to manage" simplicity for standard, non-GPU workloads, at Fargate's own cost/ flexibility trade-off (section 12).
The IAM-to-Kubernetes-RBAC bridge — critical, and easy to get wrong: Kubernetes has its own, separate permission system (RBAC — Roles and RoleBindings, governing who can do what inside the cluster, 18.9), distinct from AWS IAM (which governs AWS API access, this chapter's IAM-mechanics section above). EKS bridges the two: an IAM identity (a user or role) is mapped to a Kubernetes RBAC identity either via the legacy aws-auth ConfigMap (a ConfigMap in kube-system listing which IAM ARNs map to which Kubernetes usernames/groups) or the newer EKS access entries (an AWS-API-managed alternative to editing the ConfigMap directly, doing the same IAM-to-RBAC mapping through the EKS API instead). Without an explicit mapping, an IAM identity with full AWS-level EKS permissions still can't run a single kubectl command against the cluster — AWS IAM authenticates the request (proves who you are to AWS), but Kubernetes RBAC authorizes it (decides what that identity can do inside the cluster), and these are two genuinely separate authorization checks, not one. This is expanded further in 18.9, where Kubernetes RBAC itself (Roles, RoleBindings, and namespace-scoped permissions) is covered in depth — this chapter's job is establishing that the bridge exists and requires its own explicit configuration, not assuming IAM permissions alone are sufficient to operate a cluster.
Lambda — serverless functions
Lambda runs a single function in response to an event (an HTTP request via API Gateway, an S3 upload, a queue message) with no server to manage at all, scaling automatically to zero when idle. For AI workloads, Lambda fits well for event-driven, short-lived work: a webhook handler (Part 10.1) triggering a document-ingestion pipeline (18.15), a lightweight API endpoint with modest, bursty traffic. Lambda fits poorly for long-running AI workloads — a Lambda function has a maximum execution duration (verify the current limit against AWS documentation before quoting it), which directly conflicts with a long-running, potentially-interrupted LangGraph agent execution (Part 5.7's durable-execution chapter) that might legitimately run for minutes with human-in-the-loop pauses (Part 5.4) spanning far longer — an architecture mismatch worth catching early rather than discovering after building on the wrong primitive.
ECR — Elastic Container Registry
ECR stores the Docker images (Part 7.1) your ECS/EKS/Lambda-container workloads run from — the AWS-native answer to "where does the image push in docker push actually go" (Part 7.1/7.2's CI/CD pipeline references this exact step).
Failure modes, by compute service
Each compute service has its own distinctive way of failing under real production conditions — worth knowing specifically, not just "the service can fail" in the abstract:
- EC2 — Spot interruption: a Spot Instance can be reclaimed by AWS with a short notice window (verify the current notice period against AWS documentation — historically around two minutes) whenever AWS needs the capacity back, regardless of what your instance is doing at that moment. Since EC2 is this chapter's framed GPU-hosting option (section 4, 18.16), a Spot-based GPU fleet serving self-hosted model inference needs to handle this specifically: draining in-flight requests within the notice window, and either falling back to On-Demand capacity or another AZ/ instance pool, rather than assuming Spot capacity is simply "a cheaper EC2."
- ECS — task ENI-attachment failures: an
awsvpc-mode ECS task (section 17's task-definition example) gets its own elastic network interface (ENI) in your VPC; a task can fail to start specifically because ENI attachment fails (a subnet has run out of available IP addresses, a common failure once a subnet is sized too small for the actual task count during a scale-up event) — a failure that looks like "the task won't start" but is actually a subnet-sizing/IP-exhaustion problem, not an application or image issue. - Lambda — throttling: Lambda enforces a concurrency limit (both account-wide and optionally per-function); exceeding it causes new invocations to be throttled (rejected) rather than queued indefinitely — a real failure mode for a bursty workload that briefly exceeds its configured/account concurrency ceiling, distinct from a code-level error, and one worth explicitly provisioning/reserving concurrency for on any Lambda function with a hard reliability requirement.
- EKS — node-group scaling failures: a managed node group failing to scale up (an Auto Scaling Group hitting an account-level EC2 instance-type quota, an AZ temporarily lacking capacity for the requested instance type, or a Spot-backed node group finding no available Spot capacity at the requested price) leaves pods stuck in a
Pendingstate with no node to schedule onto — a distinct, infrastructure-layer failure from a pod-level scheduling problem (18.10 coverskubectl-based diagnosis of this specific symptom in depth).
EC2 vs. ECS vs. EKS vs. Lambda — the actual decision framework
Need full OS/runtime control, or GPU hardware for self-hosted models?
→ EC2
Need to run containers, with the LEAST operational overhead, and don't
already have (or need) Kubernetes-specific requirements?
→ ECS (Fargate) — the common default for a straightforward AI service
Need Kubernetes specifically — a customer's existing platform standard,
complex multi-service orchestration, or portability across clouds
(18.20's multi-cloud chapter)?
→ EKS
Workload is short-lived, event-driven, and doesn't need to run longer
than Lambda's execution-duration limit?
→ Lambda
A long-running, potentially-paused agent workflow (Part 5.4/5.7's
interrupt/checkpoint pattern) does NOT fit Lambda's execution model well
— ECS/EKS (with a persistent process holding the checkpointed state, or a
checkpointer backed by RDS/DynamoDB so the PROCESS can restart even if
the workflow's logical execution spans much longer) is the better fit.Auto Scaling
Auto Scaling (EC2 Auto Scaling Groups, or ECS/EKS's own scaling mechanisms) adjusts the number of running instances/tasks based on a metric (CPU utilization, request count, queue depth) — the cloud implementation of Part 7.5/18.13's horizontal scaling concept. For an AI workload specifically, scaling on CPU alone is often a poor signal (18.1's point that an I/O-bound AI service can look CPU-idle while still saturated) — scaling on request concurrency or queue depth (18.13) is usually the more accurate trigger.
5. Simple mental model
Think of AWS compute options on a spectrum of "how much of the machine do I want to think about": EC2 is renting an apartment (you handle everything inside it), ECS/Fargate is a serviced apartment (you bring your furniture — the container — but maintenance is handled), Lambda is a hotel room you only pay for while you're actually in it, and EKS is choosing to run your own apartment-management company's software (Kubernetes) inside AWS's building instead of using AWS's own management service.
6. Real-world example
A customer's document-ingestion pipeline (Part 10.2, 18.15) needs to run a parsing/embedding job triggered by each new S3 upload, typically completing in under a minute — a textbook Lambda fit (event-driven, short, bursty). The same customer's core RAG chat API needs to run continuously, hold persistent LangGraph checkpoint state across possibly-long human-in-the- loop pauses (Part 5.4), and scale predictably with concurrent chat sessions — a poor Lambda fit and a good ECS (or EKS, if the customer already runs Kubernetes) fit. Recommending Lambda for the first and ECS for the second — rather than picking one compute model for the whole system out of habit — is exactly the kind of differentiated, workload- shape-aware reasoning this chapter aims to build.
7. Architecture diagram
ECRsource images for both Lambda and ECS (Part 7.1/7.2 build-and-push pipeline)
S3 upload
User chat request
Lambdashort, event-driven ingestion job
ALB
Embeddings → Vector DB
ECS/Fargate tasklong-running API, LangGraph, checkpointed state
RDSPostgresSaver, Part 5.3
8. Production considerations
- Every compute resource should run as an IAM role, never a static credential — this is the single non-negotiable AWS security practice.
- Match compute choice to workload shape (section 4's decision framework) rather than standardizing on one service for convenience — a genuine, recurring architectural judgment call, not a memorization exercise.
- Auto Scaling triggers should reflect the AI workload's actual bottleneck (concurrency/queue depth, per 18.13) rather than defaulting to CPU utilization out of habit.
9. Common mistakes
- Hardcoding AWS credentials in application code or a committed
.envfile instead of using an IAM role — the most common, most serious AWS security mistake, and directly connects to 18.19/Part 9.5. - Choosing Lambda for a long-running, stateful agent workflow, discovering the execution-duration limit only after building on it.
- Granting
AdministratorAccessor overly broad wildcard policies to a role "to get it working," and never narrowing it afterward. - Standardizing on EKS for a simple, single-service AI application because it's the most "impressive"-sounding choice, incurring real, unnecessary operational complexity (18.9) that ECS would have avoided.
10. Security considerations
IAM's least-privilege discipline (section 4) is the foundational AWS security control everything else in Part 9/18.19 sits on top of — a perfectly-designed application-layer security model (RBAC, tenant isolation) built on top of an over-permissioned IAM role still leaves a severe, exploitable gap if the underlying infrastructure identity itself can do far more than the application needs.
11. Performance considerations
Fargate (ECS/EKS's serverless launch mode) has a cold-start characteristic for new tasks distinct from Lambda's cold starts (Part 7.4/18.9's startup- time discussion) — relevant when a scale-up event needs to happen quickly under a traffic spike; pre-warming or maintaining a minimum task count is a common mitigation, revisited concretely in 18.13.
12. Cost considerations
EC2 (especially with Reserved Instances or Savings Plans for predictable, steady workloads) is frequently cheaper per unit of compute than Fargate for large, constant workloads, at the cost of more operational overhead — a genuine trade-off, not a strictly-dominant choice either way, revisited with concrete framing in 18.18. Lambda's pay-per-invocation model can be either far cheaper or far more expensive than a continuously-running service depending on traffic pattern (very bursty/low-volume: Lambda wins; sustained high-volume: a continuously-running ECS/EC2 service is usually cheaper) — a calculation worth actually doing, not assuming.
13. When to use it
Any AI system deployed on AWS uses at least IAM and one of these compute services — this chapter's decision framework applies to essentially every AWS-hosted architecture in this handbook.
14. When NOT to over-apply it
A local prototype (Part 13.2) or an entirely different cloud provider (18.20) doesn't need AWS-specific service knowledge — the underlying concepts (compute-workload-shape matching, least-privilege identity) transfer; the specific service names in this chapter don't.
15. Alternatives and trade-offs
See section 4's full decision framework — EC2/ECS/EKS/Lambda are themselves the alternatives being compared throughout this chapter, each with a genuine, workload-dependent trade-off rather than one being universally "better."
16. Practical example — a minimal least-privilege IAM policy
json
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "ReadDocumentsBucketOnly",
"Effect": "Allow",
"Action": ["s3:GetObject"],
"Resource": "arn:aws:s3:::acme-docs-prod/ingest/*"
},
{
"Sid": "ReadOneSpecificSecret",
"Effect": "Allow",
"Action": ["secretsmanager:GetSecretValue"],
"Resource": "arn:aws:secretsmanager:us-east-1:123456789012:secret:acme/llm-api-key-*"
}
]
}Note the specific resource ARNs (not *) and the specific action list (not s3:*) — this policy grants exactly what a document-ingestion Lambda needs and nothing else; if this function's execution role were somehow compromised, the attacker could read documents from one bucket prefix and one secret, not enumerate or modify anything else in the account.
17. Production-quality example — an ECS task definition with a scoped role
json
{
"family": "ai-assistant",
"networkMode": "awsvpc",
"requiresCompatibilities": ["FARGATE"],
"cpu": "1024",
"memory": "2048",
"executionRoleArn": "arn:aws:iam::123456789012:role/ai-assistant-execution-role",
"taskRoleArn": "arn:aws:iam::123456789012:role/ai-assistant-task-role",
"containerDefinitions": [
{
"name": "ai-assistant",
"image": "123456789012.dkr.ecr.us-east-1.amazonaws.com/ai-assistant:1.4.2",
"portMappings": [{"containerPort": 8000, "protocol": "tcp"}],
"environment": [
{"name": "ENVIRONMENT", "value": "production"}
],
"secrets": [
{
"name": "LLM_API_KEY",
"valueFrom": "arn:aws:secretsmanager:us-east-1:123456789012:secret:acme/llm-api-key"
}
],
"healthCheck": {
"command": ["CMD-SHELL", "curl -f http://localhost:8000/health || exit 1"],
"interval": 30,
"timeout": 5,
"retries": 3
},
"logConfiguration": {
"logDriver": "awslogs",
"options": {
"awslogs-group": "/ecs/ai-assistant",
"awslogs-region": "us-east-1",
"awslogs-stream-prefix": "ai-assistant"
}
}
}
]
}Note the distinction between executionRoleArn (permissions ECS itself needs, e.g. to pull the image and write logs) and taskRoleArn (the permissions your application code actually gets at runtime, e.g. to read the secret referenced above) — a distinction that trips up many first-time ECS configurations and directly implements this chapter's least-privilege principle at the application-role level specifically.
18. Short exercise
Take an AI application you've designed earlier in this handbook (e.g., Part 16.1's enterprise support agent) and, for each of its components, decide EC2/ECS/EKS/Lambda using section 4's framework — then write the specific, scoped IAM policy statements each component's role would actually need, following section 16's specificity standard.
19. Interview questions
- Walk through EC2 vs. ECS vs. EKS vs. Lambda and when you'd choose each for a specific AI workload.
- What's the difference between an IAM user and an IAM role, and why does it matter for how an application authenticates to AWS?
- Why might Lambda be a poor fit for a long-running LangGraph agent workflow specifically?
- What does "least privilege" mean concretely, in terms of an actual IAM policy document?
20. FDE/customer scenario
A customer asks: "Why are you recommending ECS instead of Lambda for our chat API — Lambda seems simpler and cheaper?" A strong answer walks through the actual workload shape (a continuously-running API holding LangGraph checkpoint state across potentially long human-in-the-loop pauses, Part 5.4) against Lambda's execution-duration constraints and per-invocation cost model at sustained volume — a concrete, reasoned trade-off explanation (Part 12.3's translation discipline), not a vague "Lambda isn't good enough" dismissal.
Key takeaways
- IAM roles, not static credentials, are how AI application code should authenticate to AWS — the single highest-leverage AWS security practice.
- EC2/ECS/EKS/Lambda represent a real trade-off spectrum matched to workload shape (control needed, execution duration, traffic pattern), not a "pick your favorite" choice.
- Long-running, checkpointed agent workflows (Part 5.4/5.7) fit ECS/EKS meaningfully better than Lambda's execution-duration-bounded model.
Things you should be able to explain
- Why an IAM role is preferred over a static access key for an application.
- The EC2 vs. ECS vs. EKS vs. Lambda decision framework for a given workload's shape.
- The difference between an ECS task's execution role and task role.
Things you should be able to build
- A least-privilege IAM policy scoped to specific actions and resource ARNs.
- An ECS task definition with a correctly-scoped task role and secrets injected via Secrets Manager rather than plaintext environment variables.
Common mistakes
- Hardcoded AWS credentials instead of IAM roles.
- Choosing compute service by familiarity/prestige rather than workload shape.
- Overly broad wildcard IAM policies left unnarrowed after initial setup.
Recommended next chapter
04-aws-data-and-messaging.md