HomeCloud & OSAWS
Day 4 · 5 · 12 · 13 · 43 — the cloud platform this entire course deploys onto

AWS

Every chapter after Virtual Machines needs somewhere to actually run — AWS is that somewhere for this course. This chapter rebuilds Days 4, 5, 12, 13, and 43 into a workshop: first the service model and the handful of services that cover most real work, then hands-on launching and exposing a real application, then the interview questions the instructor flags, and finally the senior scenarios where a leaked key, a public bucket, or a surprise bill is yours to catch.

① Theory② Lab③ Interview Q&A④ Scenario drills
Day 4·5 · The AWS service model, IaaS/PaaS/SaaS, and the top services Day 12·43 · Deploying and exposing a real application on EC2 006 · Connecting to EC2 — SSH and key permissions Day 13 · Automating AWS — CLI, CloudFormation, CDK, and beyond
⚡ The whole of AWS in one mental model

AWS rents you the building blocks of a data centre by the hour, so you never have to own the hardware. Everything on it is one of three service models — IaaS (you get raw compute/storage/network and manage the OS up, like EC2), PaaS (AWS manages the runtime too, you just bring code or config, like Elastic Beanstalk or RDS), or SaaS (a finished product you just use). AWS itself has around 200 services, but a working DevOps engineer lives in a small, repeating set of about fifteen of them — compute, storage, networking, database, and security — and everything else is a variation on those same five categories.

IaaSEC2, VPC, EBSyou manage the OS up PaaSRDS, Elastic BeanstalkAWS manages the runtime SaaSa finished productyou just use it Same spectrum every cloud service sits on — how much AWS manages for you, versus you
One spectrum, three points on it. Every AWS service is really a choice of how much operational responsibility you keep versus hand to AWS — the further right, the less you manage, and the less control you have.

Three ideas carry this chapter: (1) IaaS/PaaS/SaaS is a spectrum of who manages what, not a ranking — the right point on it depends on the workload; (2) a small set of core services (EC2, S3, VPC, IAM, RDS, and a handful more) covers most real work, and the other ~185 services are specialised tools for specific needs; and (3) you rarely click through the console by hand at any real scale — the CLI, CloudFormation, CDK, or Terraform automate what the console does, and knowing which one fits which job is itself a genuine skill.

01
Phase 1 · Theory

The service model, the core services, and how to automate them

First IaaS/PaaS/SaaS as the spectrum every AWS service sits on, then the small set of services that cover most real work, then the tools that automate them at scale.

T1

IaaS, PaaS, and SaaS — the service model

The single most useful lens for understanding any AWS service is asking: how much of the stack does AWS manage, and how much is still mine?

With Infrastructure as a Service (IaaS) — EC2, EBS, VPC — AWS gives you the raw compute, storage, and network, and you manage everything from the operating system upward: patching, scaling, configuration. With Platform as a Service (PaaS) — RDS, Elastic Beanstalk, Lambda — AWS also manages the runtime or the database engine itself; you bring your code or your data and AWS handles the operational layer beneath it. With Software as a Service (SaaS) you don't manage infrastructure at all — you consume a finished product (think a fully managed monitoring SaaS, or AWS's own end-user products). Moving right along this spectrum trades control for reduced operational burden — there's no universally "best" point, only the point that fits a given workload's need for control versus its tolerance for operational overhead.

T2

The ~15 services a DevOps engineer actually lives in

AWS has roughly 200 services; a working DevOps engineer's daily vocabulary is a small, repeating fraction of that.

CategoryCore services
ComputeEC2 (virtual machines), Lambda (serverless functions), ECS/EKS/Fargate (containers)
StorageS3 (object storage), EBS (block storage attached to EC2), EFS (shared file storage)
NetworkingVPC (your private network), Route 53 (DNS), CloudFront (CDN), ELB/ALB (load balancing)
DatabaseRDS/Aurora (relational), DynamoDB (NoSQL), ElastiCache (caching)
Security & opsIAM (identity and access), KMS (encryption keys), CloudTrail (audit logging), CloudWatch (metrics/logs/alarms)

Everything else in AWS's catalogue tends to be a specialised tool solving one narrow problem within these same five categories (a niche database, a specific analytics service, a machine-learning product) — the categories themselves, and the handful of services in each, are what's worth being genuinely fluent in first.

T3

Automating AWS — CLI, CloudFormation, CDK, and Terraform

Clicking through the console is how you learn a service; it's never how you operate it at real scale.

ToolWhat it actually is
AWS CLIDirect, imperative commands against the AWS API — good for scripting, one-off tasks, and anything you'd otherwise click in the console.
CloudFormation (CFT)AWS's own declarative infrastructure-as-code, written in JSON/YAML — AWS-native, but locked to AWS only.
AWS CDKDefine infrastructure in a real programming language (Python, TypeScript), which compiles down to CloudFormation underneath — for teams who want CFT's AWS-native depth with a real language's tooling.
TerraformThe multi-cloud declarative tool from the Terraform chapter — the default when a team wants one tool that also works outside AWS.

The API underlies all of them — the CLI, CloudFormation, CDK, and Terraform are all, ultimately, just different ergonomics wrapped around the same underlying AWS API calls. Which one a team picks is usually a question of how AWS-native versus multi-cloud they want to be, and how much they value a real programming language over a declarative config format.

02
Phase 2 · Lab

Hands-on — from a first EC2 instance to a deployed, exposed app

Launch an EC2 instance, connect to it over SSH (and hit the key-permission error everyone hits once), use the CLI and S3 for the first time, then deploy a real application and expose it to the internet through security-group rules.

L1

Launch your first EC2 instance

From the EC2 console: choose an AMI (Amazon Machine Image — the OS template, e.g. Ubuntu or Amazon Linux), choose an instance type (t2.micro is free-tier eligible), create or select a key pair (you'll need the private half to connect), and configure a security group — for now, allow SSH (port 22) from your own IP only, not 0.0.0.0/0. Launch it, and wait for its status checks to pass before trying to connect.

L2

Connect over SSH — and the key-permission gotcha

Connect using the private key you downloaded when creating the key pair:

bashchmod 400 my-key.pem                              # SSH refuses to use a key that's too open — this is mandatory, not optional
ssh -i my-key.pem ubuntu@<instance-public-ip>      # username depends on the AMI: ubuntu, ec2-user, etc.

The single most common first-timer error here is skipping chmod 400: SSH deliberately refuses to use a private key with overly permissive file permissions, and the resulting error message rarely makes the actual cause obvious on the first read.

L3

The AWS CLI, and your first S3 bucket

Install the CLI and authenticate once with aws configure (the same credentials Terraform relies on, from the Terraform chapter), then use it to create and use an S3 bucket directly from the terminal instead of the console:

bashaws configure                          # access key, secret key, region, output format
aws s3 mb s3://my-unique-bucket-name   # bucket names are globally unique across all of AWS
aws s3 cp ./report.txt s3://my-unique-bucket-name/
aws s3 ls s3://my-unique-bucket-name/
L4

Deploy and expose an app via security-group rules

Install and start a simple app on the instance (a Node.js app on Ubuntu via apt, or a static site on Apache on RHEL via yum — both are the same underlying lesson), then the part people miss: the app running and listening on a port doesn't mean the outside world can reach it. A security group is a stateful virtual firewall attached to the instance, and by default it allows nothing inbound except what you explicitly add — you have to add an inbound rule for the app's port (say, 80 or 3000) from the source you intend (0.0.0.0/0 for the whole internet, or a narrower range):

bash# in the EC2 console: select the instance's security group → Inbound rules → Edit
# add: Custom TCP, port 3000 (or 80), source 0.0.0.0/0 (or your office IP range)
curl http://<instance-public-ip>:3000    # confirm it's actually reachable from outside

This is the exact mechanism behind a huge fraction of "my app won't load" confusion for anyone new to AWS: the application is fine, the security group simply hasn't been told to let anyone in.

03
Phase 3 · Interview Q&A

The AWS questions you'll be asked

These are the questions the instructor explicitly flags across Days 4, 5, 12, 13, and 43, with answers built from his explanations. Answer each out loud, then reveal.

Q1What's the difference between IaaS, PaaS, and SaaS?reveal ▸
Answer

They're points on a spectrum of who manages what. IaaS (EC2, VPC) gives you raw infrastructure and you manage the OS up. PaaS (RDS, Elastic Beanstalk) has AWS also manage the runtime or database engine. SaaS is a finished product you just consume, with no infrastructure management at all. Moving toward SaaS trades control for less operational burden.

Q2What is a security group, and how is it different from a network ACL?reveal ▸
Answer

A security group is a stateful virtual firewall attached to an instance (or ENI) — it allows nothing inbound by default and you add explicit allow rules; because it's stateful, a response to an allowed outbound request is automatically allowed back in. A network ACL operates at the subnet level and is stateless, meaning you need explicit rules for both directions of traffic. Security groups are the more commonly used, finer-grained control for most day-to-day work.

Q3What is IAM, and what's the principle you should always follow with it?reveal ▸
Answer

IAM (Identity and Access Management) controls who — users, roles, services — can do what, on which resources. The governing principle is least privilege: grant only the specific permissions a task genuinely needs, never broad access "to be safe," since over-broad permissions are the single most common root cause of a security incident's blast radius being larger than it needed to be.

Q4What's the difference between an EC2 instance and a Lambda function?reveal ▸
Answer

An EC2 instance is a persistent virtual machine you manage — it's running (and billing) continuously whether or not it's handling a request. A Lambda function is serverless — it runs only in response to a trigger, for a bounded duration, and you're billed per invocation and execution time rather than for idle capacity. EC2 fits long-running or stateful workloads; Lambda fits event-driven, intermittent work.

Q5What is a VPC?reveal ▸
Answer

A Virtual Private Cloud is your own logically isolated network within AWS — you define its IP address range, subnets (public and private), route tables, and gateways. It's the networking foundation everything else (EC2 instances, RDS databases, load balancers) is placed inside, and it's what lets you control exactly what can reach what.

Q6What's the difference between S3 and EBS?reveal ▸
Answer

S3 is object storage — accessed over HTTP(S), not attached to any one instance, and built for durability and massive scale (storing files, backups, static assets). EBS is block storage — attached to a single EC2 instance like a virtual hard drive, used for the instance's own filesystem and anything needing low-latency disk access. You don't choose one as generally "better"; you choose based on whether you need instance-attached disk or independently accessible object storage.

Q7Why would you automate AWS with the CLI, CloudFormation, CDK, or Terraform instead of the console?reveal ▸
Answer

The console doesn't scale, isn't repeatable, and leaves no reviewable record of what changed — clicking the same sequence of buttons for the tenth environment is slow and error-prone. Automation makes infrastructure scriptable, version-controlled, and reviewable, the same argument as infrastructure as code generally. Which specific tool depends on how AWS-native versus multi-cloud you want to be.

Q8Why did your SSH connection fail with a permissions-related error?reveal ▸
Answer

Almost always a missing chmod 400 on the private key file — SSH deliberately refuses to use a key with overly open file permissions, since an overly permissive key file could be read by other users on the same machine. Fixing the file permissions on the key, not the SSH command itself, is the actual fix.

Q9Your application is running on the instance but you can't reach it from a browser — why?reveal ▸
Answer

Almost certainly the security group — by default it allows no inbound traffic on any port, so unless an explicit inbound rule was added for the application's port and the intended source, the request never reaches the instance at all, regardless of whether the application itself is working correctly.

Q10What's the difference between RDS Multi-AZ and a read replica?reveal ▸
Answer

Multi-AZ is a synchronous standby for high availability — if the primary fails, RDS automatically fails over to the standby, typically within a minute or two. A read replica is an asynchronous copy used for scaling read traffic, not for automatic failover. They solve different problems and are frequently confused with each other.

Q11What is the shared responsibility model?reveal ▸
Answer

AWS is responsible for the security of the cloud — the physical data centres, the hardware, the underlying infrastructure. You're responsible for security in the cloud — your IAM configuration, your security groups, your data encryption, your patching of anything above the layer AWS manages for that service. Misunderstanding this split is a common source of real security incidents, where a team assumes AWS handles something that was actually their responsibility.

Q12Name a handful of core AWS services a DevOps engineer should know well.reveal ▸
Answer

EC2 and Lambda for compute; S3 and EBS for storage; VPC, Route 53, and ELB for networking; RDS and DynamoDB for databases; IAM, KMS, CloudTrail, and CloudWatch for security and operations. This handful covers the large majority of real day-to-day work, even though AWS's full catalogue runs to roughly 200 services.

04
Phase 4 · Scenario drills

The senior layer — AWS where a leaked key or a public bucket is the incident

You can now launch, connect to, and expose a real application. These 16 scenarios are the senior version: leaked credentials, public buckets, IAM policy confusion, surprise bills, and the architecture decisions that separate a hobby setup from a production-grade one. Answer out loud, reveal, and mark yourself.

☁️

AWS

16 scenarios · 1–16

The complete AWS scenario set, worked through in depth. This is the thinking and the story behind each one, not answers to memorise. The goal is that a leaked key or a confusing "Access Denied" is a puzzle you already know how to open, not a page you have to look up.

⚡ The universal AWS-incident reflex

Before touching a resource or a policy, ask these in order:

  1. Is a credential or a resource exposed right now? Rotate/lock down first, investigate root cause second — exposure time is the thing that actually compounds damage.
  2. Is this an identity problem or a resource problem? IAM policies, resource policies, and SCPs are separate authorization layers that all have to agree — "Access Denied" can come from any of them.
  3. What's the blast radius, and does the account boundary already contain it? A well-designed multi-account setup limits how far one mistake can spread.
  4. Would this show up in CloudTrail? If you can't answer "who did this and when," that's itself a finding.

Almost every scenario below is one of these four questions applied to a specific situation. Get the reflex automatic and the rest is detail.

---

📋 The full scenario inventory (distinct — no padding)

A. Credentials & access

  1. Leaked IAM access keys in a public repo
  2. An overly broad IAM policy
  3. A confusing "Access Denied" despite the policy looking correct

B. Exposure & network

  1. A security group open to the world on a sensitive port
  2. An S3 bucket exposed publicly
  3. VPC design — public/private subnets and a NAT gateway

C. Cost

  1. A surprise AWS bill — data transfer and orphaned resources

D. Reliability & scale

  1. Designing for high availability — Multi-AZ, ASG, ALB, RDS
  2. An Auto Scaling Group flapping under load
  3. Choosing the right compute service for a new workload

E. Database & serverless

  1. RDS is slow, or a failover became an outage
  2. Serverless at scale — the gotchas
  3. Choosing the right database service

F. Organization & governance

  1. Multi-account strategy / AWS Organizations
  2. Encryption & KMS
  3. Well-Architected review / inheriting a messy environment
1Leaked IAM access keys in a public repoScenario

A developer accidentally committed a long-lived IAM access key and secret into a public GitHub repository. It was live for six hours before anyone noticed.

What is actually happening (the mental model).

A leaked long-lived key is a live credential in an attacker's hands the moment it's public — bots scan GitHub for exactly this pattern continuously, so "six hours" isn't a small window, it's ample time for automated abuse (crypto-mining instances, data exfiltration) to already be underway. The response has to start with containment, not investigation.

How to reason about it.

  1. Deactivate or delete the key immediately in IAM — this is the single highest-priority action, before anything else.
  2. Check CloudTrail for activity from that key during the exposure window, to understand what, if anything, was actually done with it.
  3. Rewrite the Git history to remove it (the same discipline as the Git chapter's leaked-secret scenario) and force-push, coordinating with the team.
  4. Prevent recurrence structurally: move to short-lived credentials via IAM roles (for EC2/Lambda) instead of long-lived access keys wherever possible, and add automated secret-scanning on commits/pushes.

The root causes and trade-offs.

The root cause is simply having long-lived IAM access keys at all in a place where a role would do the job instead, combined with no automated secret-scanning in place to catch the commit before it was pushed. The deeper fix, eliminating long-lived keys in favour of roles, is more work upfront, but it removes this entire category of incident going forward.

The trap that less-experienced engineers fall into.

Spending the first hour investigating what happened before deactivating the key — every minute the key stays live is additional exposure, and containment always comes before root-cause analysis.

🎯 Interviewer follow-up questions you should expect.

  • "A key was leaked — what's your very first action?" You deactivate or delete the key in IAM immediately, before investigating anything.
  • "How do you prevent this structurally?" You use IAM roles with short-lived credentials instead of long-lived access keys wherever possible, plus automated secret-scanning on commits.
Say it like this"The first action is deactivating the key in IAM immediately — investigation comes after containment, not before, because every minute it stays live is more exposure to a bot that's almost certainly already scanning for it. Then I check CloudTrail for what actually happened during the window, and clean the key out of Git history properly. The structural fix is moving off long-lived access keys entirely in favour of IAM roles wherever possible, plus secret-scanning on every push so this doesn't depend on someone noticing by chance."

---

Mark:
2An overly broad IAM policyScenario

A security review finds a role attached to dozens of EC2 instances with an IAM policy granting "Action": "*", "Resource": "*" — full admin access — because "it was easier to get things working."

What is actually happening (the mental model).

A wildcard policy is the IAM equivalent of leaving every door in the building unlocked because it was faster than cutting individual keys — it works, until any one of the many things that role touches is compromised, at which point the blast radius is the entire account instead of one narrow, intended capability.

How to reason about it.

  1. Use IAM Access Analyzer or CloudTrail's actual usage history to see what the role has genuinely called in practice, rather than guessing at what it might need.
  2. Write a scoped policy matching only that observed, real usage — specific actions on specific resources — rather than a broad category "to be safe."
  3. Roll it out gradually, monitoring for access-denied errors that reveal a genuinely missed permission, and widen only that specific gap rather than reverting to the wildcard.
  4. Make least privilege the default going forward — start every new role narrow and add permissions as concretely justified, rather than starting broad and meaning to narrow later (which rarely actually happens).

The root causes and trade-offs.

The root cause is usually time pressure during initial setup, which leads someone to grant everything as a shortcut, combined with no process ever circling back afterward to actually tighten it. Tightening the policy from real, observed usage, using a tool like Access Analyzer or CloudTrail, is far safer than tightening it by guessing at what "should" be needed.

The trap that less-experienced engineers fall into.

Treating "it was easier to get working" as a permanent justification rather than a temporary shortcut that was always meant to be revisited — broad IAM policies granted under time pressure very rarely get tightened unless something forces the issue.

🎯 Interviewer follow-up questions you should expect.

  • "How do you tighten an overly broad IAM policy safely?" You use Access Analyzer or CloudTrail to see actual usage, write a scoped policy matching it, and roll out gradually while watching for access-denied errors revealing a real gap.
  • "Why is a wildcard policy dangerous even if nothing's gone wrong yet?" The blast radius of any single compromise becomes the entire account instead of one narrow capability.
Say it like this"A wildcard policy works right up until something touching that role is compromised, and then the blast radius is the whole account instead of one narrow thing. I'd tighten it from real evidence — Access Analyzer or CloudTrail's actual usage history — rather than guessing what 'should' be needed, roll the scoped policy out gradually, and watch for access-denied errors that reveal a genuinely missed permission rather than reverting back to the wildcard. Going forward, every new role starts narrow, because broad policies granted under time pressure almost never get revisited unless something forces it."

---

Mark:
3A confusing "Access Denied" despite the policy looking correctScenario

An IAM user's policy clearly grants s3:GetObject on a bucket, and yet every request comes back "Access Denied" with no further detail.

What is actually happening (the mental model).

AWS authorization is an intersection of every applicable policy layer, not just the one you happened to check — the identity-based policy (attached to the user/role), the resource-based policy (attached to the bucket itself), any Service Control Policy at the Organization level, and any permissions boundary all have to agree; and critically, an explicit deny in any one layer always wins, regardless of what any other layer allows.

How to reason about it.

  1. Check the resource-based policy (the S3 bucket policy) — a common cause is the identity policy allowing it while the bucket policy has no matching allow, or an explicit deny for a different condition (like requiring a specific VPC endpoint).
  2. Check for an SCP at the Organization/OU level that might be denying the action account-wide, invisible from looking at the IAM user's own policy alone.
  3. Check for a permissions boundary on the user or role, which caps the maximum permissions it can ever have regardless of what its attached policies say.
  4. Remember explicit-deny-always-wins — if any layer has an explicit deny matching this action, no other layer's allow can override it, so hunt specifically for a deny, not just for a missing allow.

The root causes and trade-offs.

The root cause is checking only the identity-based policy and assuming that's the whole picture, when AWS authorization is genuinely the intersection of several independent layers. The actual deny is often an explicit one sitting somewhere in an SCP or a resource policy, and it simply isn't visible unless you specifically go looking for it there.

The trap that less-experienced engineers fall into.

Staring at the IAM identity policy that clearly says "allow" and concluding AWS must be malfunctioning, instead of checking the other authorization layers — the resource policy, SCPs, permissions boundaries — where the actual deny is very often sitting.

🎯 Interviewer follow-up questions you should expect.

  • "The IAM policy clearly allows it but access is still denied — why?" AWS authorization is an intersection of multiple layers — identity policy, resource policy, SCPs, permissions boundaries — and an explicit deny in any one of them wins regardless of what the others allow.
  • "Where do you look beyond the IAM policy itself?" You look at the resource-based policy on the object or bucket, any Organization-level SCP, and any permissions boundary on the role.
Say it like this"Access Denied' when the IAM policy clearly allows it means I need to stop looking at just that one layer — AWS authorization is an intersection of the identity policy, the resource policy, any SCP, and any permissions boundary, and an explicit deny in any single one of them wins no matter what the others say. So I check the bucket policy next, then any Organization-level SCP, then a permissions boundary — the actual deny is almost always sitting in one of those, not in the identity policy I already confirmed looks fine."

---

Mark:
4A security group open to the world on a sensitive portScenario

A routine audit finds a database's security group allows inbound traffic on port 5432 from 0.0.0.0/0 — the entire internet — and it's been that way for months.

What is actually happening (the mental model).

A security group open to the world on a database port means anyone on the internet can attempt to connect — the only thing standing between that and a breach is the strength of the database credentials themselves, which is a single point of failure nobody should be relying on when a network-level restriction is nearly free to add.

How to reason about it.

  1. Check for evidence of actual exploitation first — review database logs and CloudTrail for unfamiliar connection attempts before assuming it's merely a latent risk versus an active breach.
  2. Narrow the security group immediately to only the specific sources that genuinely need access — the application servers' security group as the source, not an IP range, wherever possible, so the rule stays correct even as IPs change.
  3. Rotate database credentials regardless of whether exploitation is confirmed, given the exposure window is unknown and credentials can't be un-exposed after the fact.
  4. Add guardrails to prevent recurrence — AWS Config rules or Security Hub checks that flag any security group with a sensitive port open to 0.0.0.0/0 automatically, rather than relying on periodic manual audits to catch it.

The root causes and trade-offs.

The root cause is usually a security group rule that was created broadly during initial setup just to make things work, and then never narrowed afterward, combined with no automated detection in place for this specific, well-known misconfiguration pattern. Setting up an automated Config or Security Hub rule costs a small amount of effort, in exchange for continuous protection against this exact class of mistake happening again.

The trap that less-experienced engineers fall into.

Treating a security-group-only fix as sufficient without also rotating credentials, on the assumption that if nothing's been proven exploited yet, the existing credentials are still safe — but an unknown exposure window means you can't actually make that assumption with confidence.

🎯 Interviewer follow-up questions you should expect.

  • "A database port is open to the whole internet — what do you do?" You check logs for evidence of exploitation, narrow the security group immediately to specific sources, rotate credentials regardless, and add automated detection to prevent recurrence.
  • "Why rotate credentials if nothing's confirmed exploited?" The exposure window and its actual usage can't be fully verified, so treating the credentials as potentially compromised is the safer default.
Say it like this"A database port open to the whole internet means the credentials are the only thing standing between anyone and a breach, so I don't wait for confirmed exploitation — I check the logs and CloudTrail for anything unfamiliar, narrow the security group immediately to the specific application servers that need it, and rotate the database credentials regardless, since the exposure window can't be fully verified. Then I'd add an automated Config or Security Hub rule that flags this exact misconfiguration pattern going forward, rather than relying on the next periodic audit to catch it again."

---

Mark:
5An S3 bucket exposed publiclyScenario

A security scanner flags an S3 bucket containing customer data as publicly readable — anyone with the URL can download the objects inside.

What is actually happening (the mental model).

S3 buckets are private by default, so public exposure is always the result of an explicit choice somewhere — a bucket policy, an object-level ACL, or (historically) a "Block Public Access" setting that was deliberately disabled — which means the fix always starts with finding exactly which of those three actually granted the access, not assuming which one it was.

How to reason about it.

  1. Confirm the actual exposure first — check whether it's the bucket policy, an object ACL, or the account/bucket-level "Block Public Access" setting, since the fix differs depending on which one it is.
  2. Re-enable Block Public Access at the bucket (and ideally account) level as the fastest, broadest containment — this overrides bucket policies and ACLs that would otherwise allow public access.
  3. Check access logs for evidence of actual downloads during the exposure window, to scope the incident's real impact rather than assuming worst-case with no evidence either way.
  4. Prevent recurrence with automated detection (AWS Config, Security Hub, or Macie for sensitive-data-specific scanning) that continuously checks for public buckets, rather than relying on the next scheduled scan to catch the next one.

The root causes and trade-offs.

The root cause is usually a bucket policy or object ACL that was set too broadly, often just for convenience during initial setup. It's also common for Block Public Access to have been deliberately disabled for a specific static-website use case, and then never re-scoped afterward once that same bucket started holding sensitive data too.

The trap that less-experienced engineers fall into.

Fixing the specific policy or ACL that was found without also checking and re-enabling Block Public Access as a broader safety net — leaving the bucket one future misconfiguration away from being exposed again the same way.

🎯 Interviewer follow-up questions you should expect.

  • "An S3 bucket with customer data is publicly readable — what's your first move?" You re-enable Block Public Access immediately as the fastest broad containment, then determine exactly which mechanism — the policy, the ACL, or the setting itself — caused the exposure.
  • "How do you prevent this recurring across the whole account?" Automated detection through Config, Security Hub, or Macie continuously flags public buckets, rather than relying on periodic manual scans.
Say it like this"S3 buckets are private by default, so public exposure is always some explicit choice — a bucket policy, an object ACL, or Block Public Access being disabled — and the fastest containment is re-enabling Block Public Access immediately, since it overrides the other two. Then I'd figure out exactly which mechanism actually caused it, check access logs for evidence of real downloads during the exposure window, and add automated detection so the next public bucket gets caught continuously instead of at the next scheduled scan."

---

Mark:
6VPC design — public/private subnets and a NAT gatewayConcept

"Design a VPC for a typical three-tier web application" — testing whether you understand why not everything belongs in a public subnet.

What is actually happening (the mental model).

The core VPC design principle is that only what genuinely needs to be reachable from the internet should be able to be reached from the internet — a public subnet (with a route to an Internet Gateway) holds load balancers and bastion hosts; a private subnet (no direct route in) holds application servers and databases, which never need a direct inbound connection from the public internet at all.

How to reason about it.

  1. Public subnet: load balancers (ALB/NLB), and a bastion host if you need direct SSH access for debugging — these are the only things that genuinely need to be internet-facing.
  2. Private subnet: application servers and databases — they receive traffic routed through the load balancer, never directly from the internet.
  3. A NAT Gateway in the public subnet lets resources in the private subnet reach out to the internet (for package updates, external API calls) without allowing anything to initiate a connection in — outbound-only by design.
  4. Spread subnets across multiple Availability Zones for the high-availability design covered in scenario 8, rather than putting everything in a single AZ's subnets.

The root causes and trade-offs.

Putting application servers or databases directly in a public subnet "because it's simpler" trades away a whole layer of network-level protection for no real benefit, since a load balancer in front already handles the actual internet-facing traffic. NAT Gateways cost money per hour plus data processed — a real, deliberate trade-off against the security benefit of private-subnet isolation.

The trap that less-experienced engineers fall into.

Putting everything in public subnets to avoid the extra NAT Gateway complexity and cost, which removes a meaningful layer of defence for no real operational upside, since the load balancer was always going to be the actual entry point regardless.

🎯 Interviewer follow-up questions you should expect.

  • "What goes in a public subnet versus a private one?" Load balancers and bastion hosts go in the public subnet; application servers and databases go in the private one, since they never need a direct inbound connection from the internet.
  • "What does a NAT Gateway actually do?" It lets private-subnet resources reach out to the internet, for updates or external API calls, without allowing anything to connect in — it's outbound-only.
Say it like this"The principle is that only what genuinely needs to be internet-facing should be reachable from the internet, so load balancers and maybe a bastion host go in a public subnet, while application servers and databases go in private subnets that receive traffic only through the load balancer. A NAT Gateway in the public subnet lets those private resources reach out for updates or external API calls without allowing anything to connect in — it's outbound-only by design. And I'd spread all of this across multiple Availability Zones rather than concentrating it in one."

---

Mark:
7A surprise AWS bill — data transfer and orphaned resourcesScenario

This month's AWS bill is 40% higher than last month's, with no obvious new feature or traffic spike to explain it.

What is actually happening (the mental model).

AWS cost surprises hide in two places people don't naturally look first: data transfer (moving data between AZs, regions, or out to the internet is billed and easy to overlook, since it's not an obvious "resource" like an instance) and orphaned resources — an EBS volume left behind after its instance was terminated, an unused Elastic IP, an old load balancer nobody deleted — each individually small but adding up unnoticed over time.

How to reason about it.

  1. Use Cost Explorer to break down the increase by service first, rather than guessing — this immediately narrows whether it's compute, storage, data transfer, or something else entirely.
  2. Check specifically for cross-AZ or cross-region data transfer if the breakdown points that way — a recent architecture change (like spreading instances across AZs for HA) can introduce data-transfer costs that weren't there before, as a direct trade-off for the reliability gained.
  3. Audit for orphaned resources — unattached EBS volumes, unused Elastic IPs, idle load balancers — using AWS's own cost-optimization recommendations or Trusted Advisor as a starting point rather than manually hunting.
  4. Set up billing alerts and regular cost reviews going forward, so a 40% jump is caught within days, not discovered a month later when the bill arrives.

The root causes and trade-offs.

One common cause is an architecture change — like spreading instances across AZs for high availability — that added cross-AZ or cross-region data transfer as a side effect of a different, deliberate goal. The other is resources simply left behind after a migration, or a decommissioned service that nobody explicitly cleaned up. Both are common, and the fix — billing alerts and regular cost reviews — is cheap relative to catching this kind of drift only after it's already large.

The trap that less-experienced engineers fall into.

Assuming a cost jump must mean a traffic spike or a new feature, and looking only at compute costs, when the actual driver is very often data transfer or an accumulation of small orphaned resources that don't show up as an obvious single culprit.

🎯 Interviewer follow-up questions you should expect.

  • "The bill jumped 40% with no obvious cause — where do you look?" You check Cost Explorer's service-level breakdown first, then specifically data transfer and orphaned resources, which are the two most commonly overlooked cost drivers.
  • "How do you prevent being surprised by this again?" You set up billing alerts and a regular, monthly or more frequent, cost review, so a jump is caught within days rather than discovered when the bill arrives.
Say it like this"Cost surprises hide in two places people don't naturally check first — data transfer between AZs, regions, or out to the internet, and orphaned resources like an unattached EBS volume or an unused Elastic IP that nobody explicitly cleaned up. I'd start with Cost Explorer's service-level breakdown rather than guessing, then check specifically for data transfer if a recent architecture change like spreading across AZs for HA introduced it as a side effect, and audit for orphaned resources using Trusted Advisor. Going forward I'd set up billing alerts so a jump like this is caught within days, not discovered a month later."

---

Mark:
8Designing for high availability — Multi-AZ, ASG, ALB, RDSConcept

"Design this application so a single failure — an instance, or an entire Availability Zone — doesn't take it down." Testing whether you actually understand redundancy, not just naming services.

What is actually happening (the mental model).

High availability means eliminating single points of failure at every layer: at the compute layer, an Auto Scaling Group spread across multiple AZs replaces a failed instance automatically; at the traffic layer, an Application Load Balancer spread across those same AZs stops routing to any instance or AZ that's unhealthy; at the database layer, RDS Multi-AZ gives an automatic failover to a synchronous standby. No single layer's redundancy compensates for a gap in another — all three have to be addressed together.

How to reason about it.

  1. Spread the Auto Scaling Group across at least two (ideally three) AZs, so losing one AZ still leaves healthy instances running elsewhere, and the ASG launches replacements automatically.
  2. Put an ALB in front, also spanning multiple AZs, with health checks that stop routing traffic to any instance (or entire AZ) that's failing.
  3. Use RDS Multi-AZ for automatic database failover to a synchronous standby, typically within a minute or two — and make sure the application handles the writer-endpoint change on failover (from scenario 11) rather than assuming failover alone is sufficient.
  4. Size for N+1 at minimum — enough capacity that losing one AZ's worth of instances still leaves enough to handle the load, not just enough for the happy-path average.

The root causes and trade-offs.

Designing redundancy at one layer, say the Auto Scaling Group, while leaving another layer as a single point of failure, like a single-AZ RDS instance, gives a false sense of high availability, because the whole design is only ever as resilient as its weakest layer. Making everything Multi-AZ does cost more than staying single-AZ, and that's a deliberate trade of cost for availability that should be sized to the actual availability requirement, not applied blindly everywhere.

The trap that less-experienced engineers fall into.

Making the compute layer highly available with an ASG across AZs while leaving a single-AZ RDS instance underneath it — the database becomes the single point of failure the rest of the design was supposed to eliminate.

🎯 Interviewer follow-up questions you should expect.

  • "How do you design for HA at every layer, not just one?" You use an ASG spanning multiple AZs, an ALB spanning the same AZs with health checks, and RDS Multi-AZ for the database, all three together, since the design is only as strong as its weakest layer.
  • "What's the trade-off of Multi-AZ everywhere?" It's cost — Multi-AZ should be sized to the actual availability requirement, not applied uniformly regardless of need.
Say it like this"HA means no single layer can take the whole thing down, so I address compute, traffic, and database together, not just one. An Auto Scaling Group spread across multiple AZs replaces a failed instance automatically; an ALB spanning the same AZs stops routing to anything unhealthy; and RDS Multi-AZ gives automatic database failover, as long as the application actually handles the writer-endpoint change when it happens. The trap is making one layer highly available — say, the compute layer — while leaving a single-AZ database underneath as the thing that actually takes the whole system down."

---

Mark:
9An Auto Scaling Group flapping under loadScenario

An Auto Scaling Group keeps rapidly adding and then immediately removing instances during a sustained traffic period, instead of settling at a stable size.

What is actually happening (the mental model).

Flapping almost always means the scaling policy's thresholds and the actual metric behaviour are mismatched — a scale-out threshold and a scale-in threshold set too close together (or a cooldown period too short) means the ASG reacts to normal, brief fluctuations in the metric as if they were a sustained trend in either direction, repeatedly overcorrecting.

How to reason about it.

  1. Check the gap between scale-out and scale-in thresholds — if they're too close (say, scale out above 70% CPU, scale in below 65%), ordinary metric noise crosses both repeatedly; widen the gap so there's real separation between the two triggers.
  2. Check the cooldown period — too short, and the ASG evaluates and acts again before the previous scaling action has had time to actually affect the metric; lengthen it so each action gets a chance to take effect before the next evaluation.
  3. Consider a step-scaling or target-tracking policy instead of simple threshold-based scaling, since these are specifically designed to respond proportionally rather than as a binary in-or-out trigger.
  4. Verify new instances are actually ready before being counted as healthy — if health checks mark an instance healthy before its application has actually finished starting up (tying back to the VM boot-time scenario), the ASG can misjudge capacity and scale again prematurely.

The root causes and trade-offs.

The root cause is usually scale-out and scale-in thresholds that are set too close together, often combined with a cooldown period too short for the metric to actually reflect a completed scaling action, and sometimes with health checks marking an instance ready before it's genuinely ready. Widening the thresholds and the cooldown trades a little responsiveness for real stability, and that's almost always the right trade for anything that's actually flapping.

The trap that less-experienced engineers fall into.

Reacting to flapping by disabling autoscaling or setting a large fixed instance count instead — which solves the symptom by giving up the actual benefit of autoscaling, rather than fixing the threshold/cooldown mismatch causing it.

🎯 Interviewer follow-up questions you should expect.

  • "An ASG keeps flapping — what do you check first?" You check the gap between the scale-out and scale-in thresholds, and the cooldown period — flapping almost always means these are too tight relative to the metric's normal noise.
  • "Why not just disable autoscaling if it's causing problems?" That gives up the actual benefit entirely — the real fix is widening the thresholds and cooldowns or switching to target-tracking, not abandoning the mechanism.
Say it like this"Flapping almost always means the scale-out and scale-in thresholds are too close together, or the cooldown is too short for a scaling action to actually take effect before the next evaluation — so the ASG is reacting to normal metric noise as if it were a sustained trend. I'd widen the gap between the thresholds, lengthen the cooldown, and consider target-tracking instead of a simple threshold trigger. I wouldn't just disable autoscaling to make the symptom go away — that gives up the actual benefit instead of fixing the real mismatch."

---

Mark:
10Choosing the right compute service for a new workloadConcept

"Would you run this on EC2, ECS/Fargate, or Lambda?" — testing whether you match the compute model to the workload instead of defaulting to what you know best.

What is actually happening (the mental model).

This mirrors the Virtual Machines chapter's VM-vs-container-vs-serverless decision, one layer up inside AWS specifically: EC2 for full OS control or workloads that don't fit a container model cleanly; ECS/Fargate (containers, with Fargate removing the underlying server management) for modern, containerised applications wanting fast, consistent deployment without managing EC2 instances directly; Lambda for event-driven, bursty, or intermittent work where paying only for actual execution time beats any idle capacity cost at all.

How to reason about it.

  1. EC2: full control needed, a legacy app, or specific licensing/OS requirements Fargate/Lambda can't accommodate.
  2. ECS on EC2: containerised, but you want to manage the underlying instances yourself (for cost control at scale, or specific instance types).
  3. Fargate: containerised, and you'd rather not manage servers at all — AWS handles the underlying compute.
  4. Lambda: genuinely event-driven or intermittent (a webhook handler, a scheduled job, a lightweight API) where idle cost matters and execution time is well within Lambda's limits (from the serverless-gotchas scenario).

The root causes and trade-offs.

One common cause is defaulting to EC2 purely out of familiarity, even for a workload that would genuinely fit Fargate or Lambda better, which is usually a cost and agility issue rather than a correctness one. The opposite mistake is pushing everything into Lambda regardless of fit, which is what runs straight into the cold-start, concurrency, and downstream-overwhelm problems covered in the serverless-gotchas scenario.

The trap that less-experienced engineers fall into.

Treating this as a trendiness contest ("serverless is the future, use Lambda for everything") rather than genuinely matching the workload's actual shape — control needs, execution duration, and traffic pattern — to the right compute model.

🎯 Interviewer follow-up questions you should expect.

  • "When would you still choose EC2 over Fargate or Lambda?" You'd still choose EC2 for full OS control, a legacy application, or specific licensing or OS requirements the other two can't accommodate.
  • "Fargate vs ECS on EC2 — what's the actual difference?" Both run containers through ECS; Fargate removes the underlying server management entirely, at a cost premium over managing your own EC2 instances for the containers.
Say it like this"This is the same VM-vs-container-vs-serverless decision from the Virtual Machines chapter, one layer up. EC2 for full control or a legacy app that doesn't fit a container model; Fargate for a modern containerised app where I'd rather not manage the underlying servers at all; Lambda for genuinely event-driven or intermittent work where paying only for actual execution time beats any idle cost. The mistake is picking based on what's trendy rather than actually matching the workload's control needs, execution duration, and traffic pattern to the right model."

---

Mark:
11RDS is slow, or a failover became an outageScenario

Either the production database has been running noticeably slower under normal load for the past week, or a Multi-AZ failover just happened and the application still went fully down even though RDS itself recovered within about ninety seconds.

What is actually happening (the mental model).

These are two faces of the same underlying skill — knowing where to look on a managed database, and knowing that Multi-AZ solving the database's availability doesn't automatically solve the application's availability, since the application still has to notice and adapt to the writer endpoint changing.

How to work through it.

  1. Performance Insights — top SQL and wait events (missing index? lock waits?). CloudWatch for CPU/connections/IOPS/replica lag.
  2. Connection storms — each app instance's pool multiplies connections; put RDS Proxy in front to pool/multiplex.
  3. Multi-AZ vs read replicas — Multi-AZ = synchronous HA standby, auto-failover; read replicas = asynchronous read scaling. Don't confuse them.
  4. App-side failover handling — the writer endpoint changes on failover; connections need retry-with-backoff or the failover is an outage.
  5. Right-size instance/IOPS; add read replicas for read scaling.

Root causes, and how to tell them apart.

Missing index / bad query (Performance Insights); connection exhaustion (RDS Proxy fixes); undersized instance/IOPS; a failover the app didn't handle. Performance Insights + CloudWatch distinguish them.

How to fix it, verify it, and prevent it.

Resize the machines in a change window, watch the performance for a cycle, and then lock it in. To prevent the problem coming back, use a tagging and ownership policy, review right-sizing recommendations every month, and use autoscaling so that capacity tracks actual demand instead of a static guess.

The trap juniors fall into.

Confusing Multi-AZ (HA) with read replicas (read scaling), not using RDS Proxy for connection storms, and not handling failover in the app (so a recovered database still means an outage).

🎯 Interviewer follow-ups you should expect.

  • "RDS is slow — how do you diagnose?" You use Performance Insights for the top SQL and waits, and CloudWatch for connections and IOPS — it's likely a missing index or a connection storm.
  • "Multi-AZ vs read replica?" Multi-AZ is a synchronous HA standby with automatic failover; a read replica is asynchronous and built for read scaling.
  • "A failover happened and it was an outage even though RDS recovered — why?" The application didn't handle the writer-endpoint change, so it needs connection retry logic.
Say it like this"Performance Insights shows me the top SQL and wait events immediately — usually a missing index or a connection storm. Connection exhaustion is common because each app instance opens a pool, so I put RDS Proxy in front to multiplex. I'm clear on Multi-AZ versus read replicas: Multi-AZ is synchronous standby for HA with automatic failover in a minute or two, replicas are asynchronous for scaling reads — different problems. And the app must handle failover: the writer endpoint changes, so connections need retry-with-backoff, or a failover becomes an outage even though the database recovered in 90 seconds."

---

Mark:
12Serverless at scale — the gotchasScenario

A Lambda-based system hits limits — cold-start latency, throttling, or it flattened the database it calls.

What is actually happening (the mental model).

Serverless is great until you hit its edges, and a senior engineer can name them. There are cold starts, for which you use provisioned concurrency on latency-sensitive paths. There are account-level concurrency limits, where one runaway function throttles everything else — a nasty coupling. And then there is the big one: Lambda scales faster than your database can. Lambda can go to thousands of concurrent executions almost instantly, which flattens a relational database that cannot take that many connections; you put SQS or RDS Proxy between them to smooth the load. On top of that, there are the 15-minute and payload limits, and a cost crossover at high steady volume.

How to reason about it.

  1. Cold starts: use provisioned concurrency for latency-sensitive paths, which adds cost; and note that a VPC-attached Lambda has extra cold-start and ENI considerations.
  2. Concurrency limits: these are account-wide, so a runaway function can throttle everything else, which is a coupling you have to manage.
  3. Downstream overwhelm: Lambda scales to thousands of concurrent executions instantly and flattens a relational database, so putting SQS or RDS Proxy between them smooths the load.
  4. Limits: a 15-minute maximum runtime and payload size limits.
  5. Cost crossover: at high steady volume, Lambda can cost more than containers, so you need to know when serverless stops fitting.

The root causes and trade-offs.

Serverless removes operational work at the cost of these edges. The senior skill is knowing where the model stops fitting.

The trap that less-experienced engineers fall into.

Pointing thousands of concurrent Lambdas straight at a relational database, which causes connection exhaustion and an outage; and not knowing that the account-wide concurrency limit couples all the functions together.

🎯 Interviewer follow-up questions you should expect.

  • "Lambda flattened your database — why and fix?" Lambda scales faster than the database can accept connections, so you put SQS or RDS Proxy between them.
  • "What's the concurrency-limit gotcha?" It's account-wide, so one runaway function throttles everything else.
  • "When does serverless stop being right?" It stops being right at high steady volume, where the cost crossover kicks in, for long-running work that hits the 15-minute limit, or for latency-critical paths affected by cold starts.
Say it like this"Serverless is great until you hit its edges. Cold starts hurt latency-sensitive paths — provisioned concurrency helps but adds cost. Concurrency limits are account-wide, so one runaway function can throttle everything else, a nasty coupling. The big one is downstream overwhelm: Lambda scales to thousands of concurrent executions almost instantly, which will flatten a relational database that can't take that many connections — so I put SQS or RDS Proxy between them to smooth it. And at very high steady volume, Lambda can cost more than just running containers. Senior is knowing where the serverless model stops fitting and being willing to say so."

---

Mark:
13Choosing the right database serviceScenario

"SQL vs NoSQL — which AWS database?" — testing whether you choose from access patterns or from hype.

What is actually happening (the mental model).

Choose from the access pattern and the consistency needs, not from fashion. Use RDS or Aurora for relational, transactional work with flexible queries. Use DynamoDB for massive scale with known, fixed access patterns, where you model the table around your queries in what is called single-table design. Use ElastiCache for caching, OpenSearch for search, and Redshift or Athena for analytics. The senior mistake to avoid is picking DynamoDB "for scale" and then fighting it because the access patterns were not nailed down first, since it is painful for ad-hoc or flexible querying.

How to reason about it.

  • RDS or Aurora is for relational, transactional work with flexible or ad-hoc queries.
  • DynamoDB is for massive scale with known access patterns, where you model the table around the queries (single-table design); it is the wrong choice for flexible or ad-hoc querying.
  • ElastiCache is for caching, OpenSearch is for search, Redshift and Athena are for analytics, and services like DocumentDB cover more niche needs.
  • Decide based on the access patterns, consistency, scale, cost, and operational model.

The root causes and trade-offs.

DynamoDB is cheap and scales almost infinitely, but only if the access patterns are modelled up front; it is painful if you later need flexible queries. Aurora gives you flexible queries, but at more operational cost at extreme scale.

The trap that less-experienced engineers fall into.

Choosing DynamoDB "for scale" without modelling the access patterns first, and then fighting it when they need the flexible queries it was never designed for.

🎯 Interviewer follow-up questions you should expect.

  • "How do you pick a database?" You pick it from the access pattern and the consistency needs, not from hype.
  • "When is DynamoDB the wrong choice?" It's the wrong choice when you need flexible or ad-hoc queries, because it needs the access patterns modelled up front.
  • "What's single-table design?" It's modelling one DynamoDB table around your specific query patterns.
Say it like this"I choose from the access pattern and consistency needs, not hype. Relational and transactional work with flexible queries goes to Aurora or RDS. DynamoDB is fantastic for massive scale with known, fixed access patterns — you model the table around your queries — but it's the wrong choice if you need ad-hoc flexible querying, where people get burned. Caching is ElastiCache, search is OpenSearch, analytics is Redshift or Athena. The senior mistake to avoid is picking DynamoDB for 'scale' and then fighting it because the access patterns weren't nailed down first."

---

Mark:
14Multi-account strategy / AWS OrganizationsScenario

"How do you structure accounts for a growing org?" — testing whether you use accounts as isolation boundaries and enforce guardrails top-down.

What is actually happening (the mental model).

The account boundary is the strongest isolation that AWS offers — for limiting the security blast radius, for attributing cost, and for keeping a mistake contained to one environment. So you use separate accounts for each environment or team under Organizations, group them into organizational units (OUs), and enforce guardrails with SCPs from the top down (for example, "no one can disable CloudTrail" or "only approved regions"). You centralise the things that should be central, such as logging and security tooling in dedicated accounts and networking through a Transit Gateway, and you bootstrap new accounts consistently with a landing zone or Control Tower.

How to reason about it.

  1. Use separate accounts for each environment, team, or blast-radius boundary under Organizations, since this is the strongest isolation.
  2. Use OUs together with SCPs for top-down guardrails, such as denying anyone the ability to disable CloudTrail, or restricting which regions can be used.
  3. Centralise logging and security tooling in dedicated accounts, connect networking through a Transit Gateway, and use consolidated billing.
  4. Use a landing zone or Control Tower to bootstrap new accounts consistently.

The root causes and trade-offs.

One giant account gives no isolation, so everything is within the blast radius; a multi-account structure is isolated but takes more setup. SCPs enforce the guardrails without your having to police each account individually.

The trap that less-experienced engineers fall into.

Running everything in one account, so that a single mistake or breach affects everything and there is no cost attribution; and having no SCP guardrails at all.

🎯 Interviewer follow-up questions you should expect.

  • "How do you structure accounts?" You use separate accounts for each environment and team under Organizations, with OUs and SCP guardrails, plus centralised logging and security accounts.
  • "What's an SCP?" It's a top-down guardrail that limits what any account can do, for example preventing anyone from disabling CloudTrail or restricting it to approved regions.
  • "Why multi-account over one big account?" Because the account is the strongest isolation boundary, for security, cost, and blast radius.
Say it like this"I use a multi-account structure under Organizations because account boundaries are the strongest isolation AWS offers — for security blast radius, for cost attribution, and for limiting a mistake to one environment. OUs group accounts and SCPs enforce guardrails top-down, like 'no one can disable CloudTrail' or 'only approved regions.' I centralise the things that should be central — logging and security tooling in dedicated accounts, networking through a Transit Gateway — while keeping workloads isolated per account. Control Tower or a landing zone bootstraps this consistently. It's more setup up front but it's what lets an org scale safely."

---

Mark:
15Encryption & KMSScenario

A security/compliance question, or a review: "how do you handle encryption?" — and an "Access Denied" on an encrypted resource that turns out to be a KMS key-policy issue.

What is actually happening (the mental model).

You encrypt at rest (backed by KMS on S3, EBS, RDS, and so on) and in transit (with TLS), and the piece that people underuse is the KMS key policy. A KMS key policy is a separate authorisation layer, which means that accessing an encrypted resource requires access to both the resource and its KMS key. KMS uses envelope encryption, where a data key encrypts the data and the KMS key in turn encrypts the data key. And you choose between AWS-managed and customer-managed keys (CMKs) based on your control and audit needs, because CMKs give you control over key rotation, key policies, and CloudTrail auditing of how the key is used.

How to reason about it.

  1. At rest: enable default encryption everywhere (S3, EBS, RDS, and so on), backed by KMS.
  2. In transit: use TLS, and enforce it (for example, with an S3 bucket policy that denies non-TLS requests).
  3. KMS key policies: these are a separate authorisation layer, so access to an encrypted resource needs key access as well (this is the hidden "Access Denied" from scenario 3).
  4. CMK versus AWS-managed: use CMKs when you need control over rotation, the key policy, and CloudTrail auditing of key usage, or cross-account sharing through the key policy.
  5. Envelope encryption: KMS encrypts the data keys rather than the data directly, which scales well and keeps the master key inside KMS.

The root causes and trade-offs.

The classic issue is missing key access on an encrypted resource, which is the hidden denial. AWS-managed keys are simple but give less control, whereas CMKs give control and audit at the cost of a little management overhead.

The trap that less-experienced engineers fall into.

Forgetting that the KMS key policy is a separate authorisation layer, and so granting access to the bucket or resource but not to the key, which causes a confusing "Access Denied."

🎯 Interviewer follow-up questions you should expect.

  • "Access to an encrypted resource is denied but the resource policy allows it — why?" The KMS key policy doesn't grant access — it's a separate layer.
  • "AWS-managed vs customer-managed keys?" CMKs give you control over rotation, the key policy, and CloudTrail auditing of key usage, plus cross-account sharing.
  • "What's envelope encryption?" It's KMS encrypting a data key that in turn encrypts the data, so the master key stays inside KMS.
Say it like this"I encrypt at rest with KMS on S3, EBS, and RDS, and in transit with TLS, often enforced by a bucket policy denying non-TLS requests. The piece people underuse is the KMS key policy — it's a separate authorisation layer, so accessing an encrypted resource needs access to both the resource and its key, which is a common hidden 'Access Denied.' I use customer-managed keys when I need control over rotation, the key policy, and CloudTrail auditing of key usage, or cross-account sharing. And KMS uses envelope encryption — it encrypts data keys rather than the data directly — so the master key never leaves KMS."

---

Mark:
16Well-Architected review / inheriting a messy environmentScenario

You inherit a messy AWS environment and must assess and improve it — without boiling the ocean.

What is actually happening (the mental model).

Run a structured Well-Architected review across all the pillars (operational excellence, security, reliability, performance, cost, and sustainability), rather than reacting to whatever is loudest, so that you have a complete picture. Tooling does much of the discovery for you, including Security Hub, Config, Trusted Advisor, and Cost Explorer. Then prioritise by risk multiplied by effort: security holes and reliability single points of failure come first, cost comes next (since it is usually high-ROI), and performance comes after that. Present the findings in business terms — meaning risk and dollars — and sequence the remediation as a realistic roadmap.

How to reason about it.

  1. Do a structured review across all the pillars, to get a complete picture rather than a reactive one.
  2. Use tooling for discovery: Security Hub, Config, Trusted Advisor, and Cost Explorer surface the issues for you.
  3. Prioritise by risk multiplied by effort: security exposures and single points of failure first, cost next (which is high-ROI), and performance after.
  4. Communicate in business terms — risk and dollars — rather than presenting a wall of technical findings.
  5. Sequence the remediation: fix the scariest things first and show progress, rather than trying to boil the ocean.

The root causes and trade-offs.

The choice is between reactive firefighting and a structured review, and between prioritising by risk times effort and trying to fix everything at once, which fails.

The trap that less-experienced engineers fall into.

Reacting to whatever is loudest, so that you never get a complete picture; presenting a wall of technical findings that leadership cannot act on; and trying to fix everything at the same time.

🎯 Interviewer follow-up questions you should expect.

  • "You inherit a messy AWS account — where do you start?" You start with a Well-Architected review across the pillars, using tooling for discovery, and then prioritise by risk times effort.
  • "How do you prioritise?" Security holes and single points of failure come first, cost comes next since it's high-ROI, and performance comes after.
  • "How do you get leadership buy-in?" You present it in business terms — risk and dollars — as a sequenced roadmap.
Say it like this"I'd run a structured Well-Architected review rather than react to whatever's loudest, so I have a complete picture across security, reliability, cost, and performance. Tooling does a lot of the discovery — Security Hub, Config, Trusted Advisor, Cost Explorer surface the issues. Then I prioritise by risk times effort: security exposures and single points of failure that threaten availability come first, cost optimisation next since it's usually high-ROI, performance after. I present it to leadership in terms of business risk and dollars, not a wall of technical findings, and I sequence the remediation as a realistic roadmap — you earn trust by fixing the scariest things first and showing progress, not by boiling the ocean."

---

Mark:
🧠 How to turn this into muscle memory
  • Drill the principles first (Well-Architected pillars; least privilege / no long-lived keys / account = isolation; multi-AZ + DR sized to RTO/RPO; IAM is an intersection with explicit-deny-wins; cost hides in data transfer and orphaned resources; right service = least operational overhead that fits) — nearly every question is an application of one.
  • Build and break, in a real account. Leak a fake key into a repo and trace it in CloudTrail; write an over-broad IAM policy then tighten it with Access Analyzer; break access to an encrypted object and discover the KMS key policy; stand up a multi-AZ ASG + ALB + RDS and fail over the database. Doing beats reading.
  • One scenario, three passes. Reason it out, then say the spoken version without looking, then explain it to a teammate — especially the IAM-intersection, cost-driver, RDS-failover, and HA-design stories, which come up constantly.
  • Keep an "AWS decision + why" log — the architecture and trade-off decisions you make become the concrete stories you tell in the behavioural round.

Next (numbered order): 10 — Python (for DevOps), at this same depth.

No scenarios match that search. Clear search
AWS — DevOps Zero → Hero. Theory + Lab + Interview rebuilt from Days 4, 5, 12, 13 & 43; the 16 scenario drills are stitched in verbatim from the Scenario Playbook (AWS domain). Free-tier only — verify billing before you leave instances running. ← All chapters