AWS Core Services and Architecture Questions
Amazon Web Services' core service catalog and how the pieces compose into a working system: EC2, Lambda, S3, VPC, IAM, RDS, and the managed-service ecosystem. Covers service selection within AWS, common reference architectures, the AWS Well-Architected Framework pillars, and operational patterns specific to the platform. For provider-agnostic compute or storage trade-offs, see the cross-cloud entries.
Design a highly available, secure 3-tier web application on AWS for an e-commerce platform expecting 100k daily active users. Cover VPC layout, compute choice, load balancing, caching, database topology, and for each layer (edge, compute, storage, database) the security controls you'd deploy.
Sample Answer
Direct answer
For a secure, highly available e-commerce platform at 100k daily active users, the shape is a classic three-tier layout spread across three Availability Zones (AZs) inside one VPC: a public-facing edge (CloudFront plus WAF, AWS Web Application Firewall, then an internet-facing ALB) in public subnets, a stateless application tier in private subnets behind that ALB, and a stateful database tier (a managed relational database, replicated across AZs) in its own private, more restricted subnets. High availability comes from multiplying every tier across three AZs and never putting a single point of failure in the request path; security comes from each layer only trusting the layer immediately in front of it, never being reachable directly from the internet except through the edge.
Structured elaboration
flowchart TB
USER[User traffic] --> CF[CloudFront + WAF]
CF --> ALB[Application Load Balancer]
ALB --> APP[App tier: containers across 3 AZs]
APP --> CACHE[ElastiCache Redis Multi-AZ]
APP --> AURORA[Aurora cluster: writer + read replicas]
APP --> SQS[SQS order queue]
SQS --> WORKER[Async order worker]
WORKER --> AURORA
WORKER --> S3R[S3 receipts]
APP --> SM[Secrets Manager]
VPC layout: three AZs, each with a public subnet (holding the ALB and a NAT gateway) and at least two private subnet tiers, one for the application layer and one, more locked-down, for the database, using a dedicated DB subnet group. Nothing in either private tier has a public IP or a route directly to the internet gateway; outbound access, for patching or calling external APIs, goes through the NAT gateway.
Compute choice: containers on ECS Fargate (or EKS) running behind the ALB, spread evenly across the three AZs with autoscaling on request latency and CPU/memory. This isn't the only valid answer, plain EC2 in an Auto Scaling group behind the same ALB gets you the same HA property with more operational ownership of the instances, and it's worth naming that trade-off explicitly rather than treating "containers" as the only correct choice.
Load balancing: an internet-facing Application Load Balancer with cross-zone load balancing enabled and health checks against the app tier, so a failed AZ or a failed set of tasks is routed around automatically rather than needing a human to intervene.
Caching: ElastiCache Redis, Multi-AZ with automatic failover, for session data and frequently-read, rarely-changed data (product catalog fragments, for example). This is also the natural place to keep session state, so the app tier itself stays stateless and any instance can serve any request.
Database topology: Amazon Aurora (MySQL- or PostgreSQL-compatible) with a writer and read replicas spread across the three AZs. Aurora's storage layer is itself replicated across AZs independent of the instance layer, which is what gives it fast failover, the promotion of a replica to writer on a primary failure doesn't require re-copying data. Automated backups and point-in-time recovery should be enabled by default for an e-commerce platform where losing order data is not acceptable.
Security controls by layer
| Layer | Controls |
|---|---|
| Edge | AWS WAF managed rule sets plus rate-based rules on CloudFront; TLS termination at the edge |
| Load balancer | Security group allowing inbound only from CloudFront's IP range or via a shared secret header, not open to the whole internet |
| Compute | Security group allowing inbound only from the ALB's security group; IAM role scoped to exactly what the app needs (read/write specific S3 prefixes, invoke specific queues, no wildcard permissions); secrets pulled from Secrets Manager, never hardcoded |
| Storage/database | DB subnet group in the most restricted private subnets; security group allowing inbound only from the app tier's security group on the database port; encryption at rest via a KMS key; credentials rotated through Secrets Manager rather than static connection strings |
Asynchronous work: order processing that doesn't need to block the user's request (payment confirmation follow-up, receipt generation, inventory reconciliation) goes through SQS to a separate worker, decoupling checkout latency from downstream processing and giving you a natural retry/redrive mechanism if a downstream step fails.
Worked example
Contrast this against a much simpler baseline to make clear where the extra layers earn their cost: a single-region, stateless REST API backed directly by RDS, with no cache tier, no queue, and a single AZ, would be enough for a low-traffic internal tool. It is not enough here, because at 100k daily active users with an availability and security bar, a single AZ is a single point of failure for the database, no cache tier means every read hits the database directly, and no queue means a slow downstream dependency (say, a payment provider) directly extends checkout latency for every user. Each additional piece in this design, the second and third AZ, the cache tier, the queue, corresponds to a specific failure mode or load pattern that the simpler baseline doesn't survive.
Trade-offs & pitfalls
A frequent design mistake is putting the database in the same subnets as the app tier "to keep it simple," which removes a real security boundary for no operational benefit, the DB subnet group should be its own tier with its own, tighter security group. Another is choosing EC2 or containers without naming why: at very high request volumes, for example if this platform needed to scale to something on the order of 100k requests per second spread across three regions rather than 100k daily active users in one region, the calculus shifts, Lambda's automatic scaling can remove capacity-planning overhead for spiky, high-fan-out request patterns, while a container fleet gives more control over cost at sustained high, steady throughput and over long-lived connections a request-response Lambda model handles less naturally. Finally, security groups that are too permissive between tiers (allowing the app tier's whole subnet CIDR into the database instead of just the app tier's security group) are a common source of lateral-movement risk that a security review will catch, referencing security groups by ID rather than by CIDR range keeps the rule accurate even as the app tier scales in and out.
How do S3 Versioning and MFA Delete help protect against accidental deletes or overwrites? What operational and cost implications should you be aware of when enabling versioning on a large bucket?
Sample Answer
Direct answer
S3 Versioning keeps every write as a distinct object version instead of overwriting data in place, so an accidental overwrite just creates a new version alongside the old one, and an accidental delete adds a lightweight delete marker, which only hides the object from a normal listing or GET, rather than destroying it. Both are recoverable by fetching or restoring a prior version ID. MFA Delete adds a second, harder-to-automate layer on top: permanently deleting an object version, or changing the bucket's versioning state, requires a valid multi-factor authentication code from the account's root user, which stops both accidental scripted deletes and a compromised non-root credential from erasing history.
Structured elaboration
- Versioning mechanics: enabling versioning is effectively one-way at the bucket level; you can enable or suspend it, but you can never make it as if the bucket was never versioned, since versions created while it was enabled persist.
- MFA Delete specifics: it must be configured using the bucket owner's root account credentials, not just any AWS Identity and Access Management (IAM) admin, and once required, permanently deleting a version or disabling versioning/MFA Delete itself requires that MFA code at request time. That's why it's usually reserved for a bucket's highest-value data rather than applied everywhere.
- Operational cost of versioning: every write, including a rewrite of unchanged content, is billed as new, full-sized storage. A bucket with frequent overwrites can see storage cost grow substantially if nothing prunes old versions. The standard mitigation is a lifecycle rule that transitions or expires noncurrent versions after a chosen window, for example keeping noncurrent versions restorable for roughly 30 days and transitioning anything not needed sooner to a cheaper storage class before final expiry around 90 days, tuned to how much recovery time the team actually needs against storage spend.
- API and tooling overhead: version-aware operations, listing versions, deleting a specific version ID, restoring by copying an old version back as current, need version-ID-aware code. Naive scripts that assume "one object equals one thing" can behave unexpectedly on a versioned bucket, since a plain DELETE just adds a marker and doesn't free any storage.
Worked example
A bucket stores user-uploaded documents, and a bad deploy runs a cleanup script that deletes keys matching a stale prefix pattern, hitting 4,000 live objects by mistake. With versioning on, none of that data is actually gone: the 4,000 objects now show delete markers. Recovery is ListObjectVersions filtered to those keys, then removing the delete marker (or copying the prior version back as current) for each, which S3 Batch Operations can drive at scale from a manifest instead of a one-by-one script. Without versioning, the same script would have caused permanent, unrecoverable data loss.
Trade-offs & pitfalls
- Enabling versioning without a lifecycle rule to prune noncurrent versions is the most common mistake: storage cost creeps up silently, especially on buckets with high overwrite churn, until someone notices the bill.
- MFA Delete is a real operational tax: it must go through the root account, which is incompatible with fully automated, script-driven bulk cleanup unless that workflow can supply an MFA code. That's why teams usually restrict it to a small number of critical buckets.
- Versioning and MFA Delete protect against accidental overwrite, accidental delete, and many scripted mistakes, but neither is a substitute for cross-region or cross-account replication if the actual risk is bucket-level deletion or a compromised account with root-level access; that scenario needs mass-deletion controls beyond versioning alone.
- A delete marker still counts as an object version and, depending on lifecycle configuration, can itself accumulate cost or clutter if never cleaned up.
Walk through the S3 storage classes (Standard, Intelligent-Tiering, Standard-IA, One Zone-IA, Glacier Instant/Flexible Retrieval, Deep Archive). How would you design a lifecycle policy for data whose access pattern you don't know upfront, versus compliance logs you rarely touch but occasionally need to retrieve quickly?
Sample Answer
Separate two questions: how often is the data accessed (which decides storage cost tier), and how fast must it come back the rare times you do need it (which decides retrieval latency tier). For genuinely unknown or changing access patterns, let S3 automate the first question with Intelligent-Tiering; for known-rare-but-must-retrieve-fast data like compliance logs, pick a class by its retrieval latency, not just by how "cold" the data is.
The storage classes
| Class | Resilience | Retrieval latency | Notable cost mechanics | Use when |
|---|---|---|---|---|
| S3 Standard | Multi-AZ | Milliseconds | Highest storage $/GB among these, no retrieval fee | Actively accessed, hot data |
| S3 Intelligent-Tiering | Multi-AZ | Milliseconds (frequent/infrequent tiers); minutes-to-hours if optional archive tiers are enabled | Small per-object monitoring fee; no retrieval fee on the frequent/infrequent tiers | Access pattern is unknown or changes over time |
| S3 Standard-IA | Multi-AZ | Milliseconds | Lower storage cost than Standard, but per-GB retrieval fee and a 30-day minimum storage duration charge | Known infrequent access that still needs multi-AZ resilience |
| S3 One Zone-IA | Single-AZ | Milliseconds | Cheaper than Standard-IA; same retrieval fee model | Infrequent access where losing the AZ is acceptable, e.g. easily-regenerated derivative data or a secondary copy |
| S3 Glacier Instant Retrieval | Multi-AZ | Milliseconds | Priced for archive-level access frequency; retrieval fee; 90-day minimum storage duration | Rarely touched, but needs to come back instantly when it is |
| S3 Glacier Flexible Retrieval | Multi-AZ | Minutes (expedited) to hours (standard/bulk) | Very low storage cost; 90-day minimum storage duration | Long-term archive where a multi-hour wait is acceptable |
| S3 Glacier Deep Archive | Multi-AZ | Hours (standard) to up to 48 hours (bulk) | Lowest storage cost; 180-day minimum storage duration | Long-term retention/compliance archives rarely, if ever, retrieved |
Designing the two lifecycle policies
Data whose access pattern you don't know upfront: land it in S3 Intelligent-Tiering from the start. It automatically moves objects between the frequent and infrequent access tiers based on observed access with no retrieval fee on those tiers, which removes the guesswork of picking transition ages up front. If the workload is dominated by very large object counts of small objects, weigh Intelligent-Tiering's per-object monitoring fee against the savings, since it can erode the benefit at very small object sizes.
Compliance logs you rarely touch but occasionally need to retrieve quickly: the "quickly" requirement rules out Glacier Flexible Retrieval and Deep Archive, since a multi-hour-to-48-hour wait can breach an audit or legal-hold response SLA even though the data is genuinely cold. The right fit is S3 Glacier Instant Retrieval for the bulk of the archive after an initial active window, layered with S3 Object Lock (Compliance or Governance mode) and Legal Hold so the retention requirement is enforced independently of the storage class, and a lifecycle expiration rule for the end of the retention period (e.g. after a fixed multi-year window):
- Ingest to S3 Standard or Standard-IA.
- Transition to S3 Glacier Instant Retrieval after 90 days (respecting its minimum storage duration).
- Apply Object Lock + Legal Hold at ingest so retention doesn't depend on remembering to re-apply it later.
- Expire objects via a lifecycle rule once the retention window (e.g. 7 years) has passed, unless a legal hold is still active.
Worked example: a lifecycle transition schedule
For data with a genuinely cooling access pattern where transitions are staged rather than automated by Intelligent-Tiering:
- Days 0-30: S3 Standard (still likely to be read).
- Day 30: transition to S3 Standard-IA (exactly at its 30-day minimum storage duration, avoiding an early-transition charge).
- Day 90: transition to S3 Glacier Flexible Retrieval (its minimum storage duration is also 90 days, so this transition lands right at the boundary rather than before it).
- Day 365: transition to S3 Glacier Deep Archive.
- Day 365 + 180 (its own minimum) or later: eligible for expiration per the retention policy.
Each transition age is chosen to land on or after the destination class's minimum storage duration, which is what avoids triggering an early-deletion/transition charge on the object.
Trade-offs and pitfalls
- Transitioning an object before its destination class's minimum storage duration (30/90/180 days depending on class) triggers an early-deletion-style charge; the transition schedule has to respect those minimums, not just "when the data looks cold."
- Intelligent-Tiering's per-object monitoring fee makes it a poor fit for very large counts of very small objects; it's a better fit for larger objects or aggregate object sizes.
- One Zone-IA trades AZ-level resilience for a lower price; never use it for the only copy of data that can't be regenerated.
- Equating "cold" with "compliance-safe" is the core mistake in the compliance-log scenario: Glacier Flexible Retrieval and Deep Archive are cheaper per GB, but their multi-hour retrieval time can itself be a compliance failure if the requirement is fast retrieval, not just cheap long-term storage.
Summarize the AWS Shared Responsibility Model: what does AWS secure versus what does the customer secure? How does that split change between EC2, RDS, and a fully managed service like Lambda?
Sample Answer
Direct answer
The AWS Shared Responsibility Model splits security into two zones. AWS is responsible for security of the cloud: the physical data centers, hardware, global network, and the virtualization layer that isolates customers from each other. The customer is responsible for security in the cloud: how they configure, access, and protect whatever they run on top of that foundation. Exactly where the line falls shifts as you move up the stack from EC2 (you manage more) toward a fully managed service like Lambda (AWS manages more), but "AWS manages more" never means "the customer manages nothing."
Structured elaboration
| Layer | AWS's job | Customer's job |
|---|---|---|
| EC2 (unmanaged compute, IaaS) | Physical hosts, hypervisor, facility security, network hardware | Guest OS patching, AMI (Amazon Machine Image) hardening, security group and network ACL rules, IAM permissions, encryption choices, application-level patching |
| RDS (managed database) | Underlying host OS patching, database engine patching (within a maintenance window the customer schedules), backup infrastructure, storage-layer durability | Schema design and data, database user credentials, VPC network access (security groups, subnet placement), KMS (Key Management Service) key choice for encryption at rest, parameter/option group configuration |
| Lambda (fully managed, serverless compute) | OS and runtime patching, the entire compute fleet, scaling infrastructure | Function code and its dependencies, the IAM execution role (least-privilege permissions the function runs with), secrets/environment variables, any VPC configuration if the function reaches private resources |
Reading across the table, the pattern is: AWS's slice of "of the cloud" grows as the service gets more managed, but IAM, data, and access configuration stay the customer's job at every layer, because AWS has no way to know what access is supposed to look like for your workload.
This is also exactly what the Security pillar of the AWS Well-Architected Framework asks you to check during a review: for every service in the workload, confirm someone has explicitly taken ownership of each control on the customer side, rather than assuming a "managed" label means it is covered.
Worked example
Take a single concern, an unpatched operating-system vulnerability, and trace it across the three services:
- On EC2, the OS is yours: if you don't patch it, it stays vulnerable. AWS never touches your guest OS.
- On RDS, AWS patches the underlying OS and database engine, but the customer chooses and approves the maintenance window, and is still responsible for the network path to the instance and the credentials that can reach it.
- On Lambda, there is no guest OS for the customer to reason about at all, AWS owns and patches the entire execution environment. The vulnerability surface that remains is the customer's own code and third-party dependencies packaged into the function.
Same underlying risk, three different owners, because the abstraction level changes what the customer even has visibility into.
Trade-offs & pitfalls
The most common senior-level failure is treating "managed service" as "AWS's problem now." Public S3 buckets and permissive IAM roles are consistently the cause of real breaches, not gaps in AWS's own infrastructure, and both of those are 100% customer-side regardless of how "managed" the surrounding service is. A second pitfall is assuming encryption at rest via KMS means data is automatically protected: AWS manages the KMS service, but the customer still owns key policy, key rotation decisions, and who is allowed to decrypt. Finally, teams sometimes assume the model is static per service; it isn't, the split for a self-managed database on EC2 is materially different from RDS's managed engine, so the honest answer always names the specific service, not "the database."
How does an EC2 Auto Scaling Group work as a mechanism? Walk through launch templates, health checks, lifecycle hooks, and how the scaling-policy types (target-tracking, step, scheduled) fit together to maintain desired capacity.
Sample Answer
Direct answer
An Auto Scaling Group (ASG) continuously compares actual healthy capacity against a desired capacity number and launches or terminates EC2 instances from a Launch Template to close the gap. Three subsystems make this work: the Launch Template defines what to launch, health checks decide which instances actually count toward capacity, and scaling policies decide what the desired capacity should be right now.
Structured elaboration
- Launch Template as source of truth: the ASG references a specific Launch Template and version (or
$Latest) for the Amazon Machine Image (AMI), instance type(s), network, an AWS Identity and Access Management (IAM) role, and user data. AMixedInstancesPolicylets one group launch several instance types or purchase options from the same template. - Health checks: EC2 status checks confirm the instance itself is healthy; if the group is attached to a load balancer or target group, Elastic Load Balancing (ELB)/target-group health checks confirm the application is actually serving traffic, which EC2-only checks can't see. A configurable health check grace period stops the group from replacing an instance that's still booting.
- Lifecycle hooks: pause an instance in a
Pending:WaitorTerminating:Waitstate so custom code (Lambda, AWS Systems Manager (SSM) Automation, or a script polling the group) can run before the instance is marked in service (register with a service mesh, warm a cache) or before it's terminated (drain connections, flush in-flight work), then explicitly signalCONTINUEorABANDON. - Warm pools: keep a stopped or pre-initialized pool of instances outside the desired-capacity count, so a scale-out event can resume a warm instance instead of paying full cold-boot latency.
- Scaling policy types: target tracking keeps a metric such as average CPU or an Application Load Balancer's request count per target at a chosen value, similar to a thermostat; step scaling makes discrete capacity changes sized to how far a CloudWatch alarm has been breached; scheduled scaling changes capacity at known times for predictable patterns.
flowchart LR
CW[CloudWatch metric] --> SP[Scaling policy]
SCHED[Schedule] --> SP
SP --> DC[Desired capacity]
DC --> ASG[Auto Scaling Group]
LT[Launch template] --> ASG
ASG --> LH1[Lifecycle hook launch]
LH1 --> INS[Instance in service]
INS --> HC{Health check}
HC -->|healthy| DC
HC -->|unhealthy| LH2[Lifecycle hook terminate]
LH2 --> TERM[Instance terminated]
TERM --> ASG
Worked example
A web service sits behind an Application Load Balancer. A scheduled scaling action sets desired capacity to 4 instances at 07:00, before the known daily traffic ramp, and back to 2 at 20:00. On top of that, a target-tracking policy on the ALB's request-count-per-target metric, targeted at 500 requests per target, absorbs variance the schedule doesn't predict: if a spike pushes the per-target rate above 500, the group adds instances until it settles back down. When a new instance launches, a lifecycle hook holds it in Pending:Wait while an SSM Automation document installs the current config bundle and confirms a local health endpoint returns 200, then calls complete-lifecycle-action with CONTINUE; only then does the ALB health check start counting it toward capacity.
Trade-offs & pitfalls
- Scheduled scaling is cheap and precise for known patterns but blind to anything unplanned. Target tracking is the safety net but reacts on the lag of its metric window; for a sharp step-function spike, step scaling's fixed-size jumps per alarm breach can respond faster than target tracking's smoother, proportional adjustments.
- A health check grace period that's too short causes flapping, killing instances mid-boot before the app ever gets a chance to pass a check; too long, and a genuinely broken instance stays in service longer than it should.
- A lifecycle hook whose custom code never reports back (a Lambda crashes, a heartbeat isn't sent) holds the instance in its wait state until the hook's timeout, then the configured default action fires; forgetting to set that default action explicitly is a common gap.
- Combining ELB health checks with EC2 status checks is usually right for anything behind a load balancer, since EC2-only checks won't catch an instance that's up but whose application has hung.
Unlock Full Question Bank
Get access to all 33 AWS Core Services and Architecture interview questions and detailed answers.
Sign in to ContinueJoin thousands of developers preparing for their dream job.