Amazon Web Services is usually taught as a catalog, which is why it feels endless. It is smaller as a set of rules every service obeys: work runs in one geographic place and does not leave it by itself, identity decides what a request may do, the defaults favor reachability over safety, you are billed for what you reserve as much as for what you use, and anything you click into existence stops matching the file that claims to describe it. Learn those and a new service is mostly a new noun.
Each chapter opens with the short version. Tap one to read the detail.
Where your system runs, and how far a request travels
~2 min
Regions are isolated and nothing crosses between them unless you configure it. Two habits cause most of the confusion: reading "multi-AZ" as if it meant multi-Region, and assuming your us-east-1a is the same building as someone else's.
A Region is a separate geographic area, and the documentation is blunt: Regions are isolated, resources are not automatically replicated across them, and you see only the resources tied to the Region you selected. That last clause explains the commonest beginner panic: a bucket that "disappeared" is almost always in the Region the console is no longer pointed at.
Inside a Region are availability zones (AZ): multiple isolated locations. Spreading instances across them protects you from the failure of a single location within the Region, and does nothing about losing the Region — a different and far larger design.
The naming is a trap of its own. AWS maps physical zones randomly to zone names per account, so us-east-1a in your account may be different hardware from us-east-1a in a colleague's. Only the zone ID is stable. For example, one account reports this mapping:
us-west-2a -> usw2-az2
us-west-2b -> usw2-az1
In front of all this sits an edge network: a distribution answers from the nearest edge location and reaches your origin only on a miss. Those are points of presence caching copies, not Regions you can launch into.
Distance is billed too. Traffic that stays inside one zone can be free while the same traffic across zones or Regions is charged, and the edge network bills the data it sends out to viewers plus the requests. Compute is what people watch; movement is what surprises them.
Rule: decide which failure you are insuring against — a data center or a Region — before calling a design redundant.
Who is responsible for what
~2 min
AWS secures the cloud; you secure what you put in it — and the line moves service by service. The account, not a tag, is the hard boundary, and an organization guardrail grants nothing.
The split is written per service, and reading two teaches more than any diagram. On instances, AWS protects the infrastructure that runs the services, while your side explicitly includes network access, connection credentials, the roles attached to the instance, and the guest operating system with its updates and security patches. On a managed relational database that list moves: installing and patching the operating system and the database engine, backups, high availability and scaling all become the provider's. What stays yours is named just as plainly — query tuning.
So "managed" never means nothing is yours; it means the list changed. For example, a missing index is yours either way.
Accounts are described as natural boundaries for permission, security, costs and workloads, which is why production and development get separate accounts rather than separate tags. Accounts group into organizational units, and a service control policy attached above them sets a ceiling.
That ceiling is not a grant, and this is where people go wrong. No permissions are granted by such a policy; a user with no permissions policy still has no access; and these policies do not affect the management account at all, so testing a guardrail from there proves nothing.
Rule: name the specific task before arguing about responsibility; the answer changes with the service.
Identity is the perimeter
~2 min
Access is decided about a principal, not about where a request came from, so the identity design is the security design. The habit that breaks it is the long-lived access key.
AWS Identity and Access Management controls who is authenticated — signed in — and who is authorized, meaning who has permissions. Those are separate steps, and most baffling errors are the second failing after the first succeeded.
A user is a long-lived identity carrying static credentials. A role has none of its own; assuming it produces temporary ones — short-term, generated dynamically rather than stored with the user, unusable once expired. The stated advantage is the whole argument for roles: you do not have to distribute or embed long-term credentials with an application.
Say you run an application on an instance. Give the instance a role and those credentials are available to everything running there, so no long-term secret is ever written to that machine. The alternative — a key pasted into a config file — must then be rotated, audited and kept out of version control forever.
Two things surprise newcomers. The account root user sits outside the normal rules: all requests are implicitly denied by default except the root user's, which has full access. And identity changes are eventually consistent, so a role you just created may not work the instant you use it.
Rule: if a credential has no expiry, treat it as a liability and find the role that replaces it.
How a request is decided, and why keys have their own policy
~2 min
Everything is denied until something allows it, and one explicit deny anywhere ends the argument. Boundaries and organization policies intersect rather than add — and an encryption key can refuse you anyway.
The evaluation logic is short enough to memorize. By default all requests are implicitly denied, with the root user as the exception. A request must be explicitly allowed by some policy to proceed. And an explicit deny overrides an explicit allow, whichever policy it came from.
What people get wrong is the arithmetic in the middle. Permissions feel additive, so attaching an administrator policy feels decisive. Suppose a permissions boundary is also in force: the result is the intersection of the two, and where an organization policy applies, the action must be allowed by every applicable type. Adding an allow can therefore change nothing, which reads as a bug and is not one.
Encryption is where this bites hardest. Every key in the Key Management Service (KMS) has exactly one key policy, and the rule is explicit: unless that key policy allows it, you cannot use an identity policy to allow access to the key. Key policies are also Regional. So "access denied on an action I obviously have" is often a key in another Region, or a key whose own policy never mentioned you.
Rule: when a permission fails, find the layer that is not saying yes instead of adding another allow.
The network, and the defaults that are not safe defaults
~2 min
A subnet is public only because its route table sends everything to an internet gateway; nothing else marks it. Security groups are stateful and cannot deny; network access control lists are stateless, and the default one allows everything.
A virtual private cloud (VPC) is a network you define; a subnet is a range of addresses inside one availability zone. Five addresses per subnet are unavailable — the first four and the last — so, for example, the smallest allowed subnet, a /28, yields eleven usable addresses rather than sixteen.
Nothing labels a subnet public. Its route table does, and where several routes match the most specific wins:
10.0.0.0/16 -> Local # always present
0.0.0.0/0 -> igw-... # this line is what makes the subnet public
A Network Address Translation gateway is the mirror image: instances behind it reach out, but connections must always start inside.
Two filters exist and behave oppositely — the trap that catches most people here. Security groups are stateful: the response to a request you allowed is permitted regardless of the rules in the other direction. They take allow rules but not deny rules, and a new one starts with no inbound rules and one outbound rule permitting everything. Network access control lists are stateless, so return traffic needs its own rule; they are numbered and the first match decides. The default list allows everything both ways; one you create denies whatever it does not match.
Rule: debug a connection in order — route first, then the stateful filter, then the stateless one.
Instances, and the disks underneath them
~2 min
Stopping and terminating are not two strengths of one action. A stop erases instance-store data and keeps billing you for volumes; a terminate deletes the root volume by default.
An instance moves through pending, running, stopping, stopped, shutting-down and terminated, and billing follows those states literally. A running instance is billed per second with a one-minute minimum even if it sits idle and you never connect. Say you stop it overnight: usage billing halts, but charges are still incurred for the storage of any attached volumes. That is where "I turned everything off, why is there a bill" comes from. Terminating is final — permanently deleted, and you cannot connect to or recover it.
The storage rules are where data actually gets lost. Instance-store volumes hold temporary data deleted when you stop, hibernate or terminate, so stopping and starting loses whatever was on the store attached to the previous host. Elastic Block Store volumes survive, but on termination the default is to delete the root volume and preserve the others. A snapshot persists independently of its volume, and is how you move data across zones, Regions or accounts.
Billing style differs by service: for block storage you pay for what you provision, filled or not. On-Demand instances bill by the second with no commitment, while Savings Plans and Reserved Instances are commitments to spend. Reserving capacity in a zone is a separate option, so committing to spend is not the same as holding hardware.
Rule: before stopping or terminating anything, ask which volume the data is on and what that volume's default is.
Scaling reacts to a signal, so check the signal is real
~2 min
An Auto Scaling group replaces instances that fail a health check — but by default it uses only the built-in status checks and ignores the load balancer's. An instance that boots fine and serves errors stays in service.
A group holds a desired capacity between a minimum and a maximum and replaces instances it considers unhealthy. Everything rests on what "unhealthy" is allowed to mean.
The built-in instance and system status checks are the default type; they need no configuration and are always enabled. They notice an instance that has stopped running or become impaired. What they cannot notice is your application, because they never speak to it. Load balancer health checks can — and here is the trap: by default the group ignores the results of those checks, and you have to turn them on. Suppose your process crashes inside a perfectly healthy virtual machine; nothing replaces it, and it keeps receiving traffic.
Replacement is deliberately throttled — once instances are warmed up, the group replaces only up to ten percent of desired capacity at a time — so a misconfigured health check degrades a fleet rather than deleting it.
Metrics have the same shape of gap. Many services report metrics automatically, but the monitoring agent is what collects processes, memory, disk usage and network performance from inside an instance. Memory is not a default metric, so a plan to scale on memory needs the agent installed first.
Rule: for every automated reaction, name the signal it reads and confirm that signal can see the failure you care about.
Managed compute: what you stop owning, and what you start
~2 min
A function's execution environment handles one request at a time, so concurrency means more environments, never threads inside one. Code outside the handler runs once per environment, so a request-scoped variable there leaks between callers.
Serverless functions run code without provisioning servers; the platform creates and destroys execution environments in response to traffic. The concurrency model is the part to internalize: one request per execution environment at a time, so ten simultaneous callers means ten environments, each with its own memory.
Environments can be reused — a warm start — but state might not persist across invocations. Both halves are load-bearing: you may not rely on a cache surviving, and you may not assume it is gone. Say you cache the current user outside the handler; the next caller inherits it.
Memory is the one performance dial, and it is not only memory: processing power is allocated in proportion to it, and the 128-megabyte default is the lowest possible setting. Since billing is per request plus memory multiplied by duration, raising memory can lower the total when work finishes sooner.
Containers make the same trade at a different granularity: a task definition is the blueprint, a task is one running instance of it, a service keeps a long-running application alive. Instance-backed capacity means you pick and manage instance types; the serverless engine means no capacity planning and less control.
The misconception underneath: "serverless means no operations". Patching and capacity stopped being yours; cold starts, concurrency limits and per-invocation cost started.
Rule: when you adopt a managed runtime, write down what you stopped owning and what you started — the second list is never empty.
Object storage is not a filesystem
~2 min
Objects are stored under keys in a flat namespace; the slashes are ordinary characters the console draws as folders. Reads are strongly consistent, but two overlapping writes to one key are not arbitrated — the later timestamp wins.
A bucket holds objects, each with a key that identifies it uniquely. For example, in photos/puppy.jpg the whole string is the key — the slash is just a character. The proof it is flat: a different bucket type exists precisely to offer real hierarchical directories instead of that structure. A bucket's name and Region are fixed at creation.
Consistency is strong for writes, overwrites and deletes, but two traps hide behind that. There is no locking for concurrent writers, so if two writes to one key overlap the later timestamp wins and the other is gone. And bucket configuration is only eventually consistent — after first enabling versioning, wait about fifteen minutes before writing objects.
Access defaults have improved and old advice has not kept up. Buckets and objects are private, public-access block settings are on by default at bucket level, and object ownership defaults to bucket-owner-enforced with access control lists disabled outright — so advice about fixing a permission with an object access control list describes a mechanism your bucket has switched off.
Versioning changes what deletion means: a delete without a version identifier writes a delete marker, the object is not actually deleted, and a later read returns 404 while the data — and its storage charge — remains.
Rule: treat a key as an opaque name and a delete as a new version, not as a path and a removal.
Managed databases: what the provider runs, and what stays yours
~2 min
A standby in another zone exists for failover, not for read capacity — you cannot read from it. And filtering a scan does not make it cheaper: the filter runs after the read, so the capacity is consumed anyway.
For a managed relational instance the provider takes installation and patching of the operating system and the database engine, backups, high availability and scaling. You keep application design and, explicitly, query tuning.
The availability feature is widely misread. A multi-zone deployment maintains a synchronous standby replica in a different availability zone, and the documentation states plainly that this replica does not serve read traffic — it is redundancy, not throughput. The wrinkle: the same phrase also covers a cluster deployment whose readers do serve reads.
Backups carry a matching trap. Suppose you delete an instance without explicitly choosing to retain: every automated backup goes with it, and only a manual snapshot survives. So "we have backups" has a hole exactly where you need it most.
The key-value alternative rewards a different instinct. An item's partition key is hashed to choose its partition; adding a sort key stores items sharing a partition key together in sorted order, so a range read is cheap. A scan reads every item in the table or index — and its filter expression is applied after the scan finishes, so identical read capacity is consumed whether or not you filtered. Filters tidy the response; they do not reduce the work.
Rule: for any read path, name the key that finds the rows — if the answer is "we filter", you are paying for the whole table.
Decoupling, and designing for the failure you will actually get
~2 min
Receiving a message does not remove it: the queue hides it for the visibility timeout, and your delete call is the acknowledgment. Build on that and a consumer crash costs nothing; assume otherwise and you lose work.
The lifecycle is three steps and the middle one is the whole design. A producer sends. A consumer receives, at which point the message remains in the queue and is not returned to other receive requests for the duration of the visibility timeout. The consumer then deletes it, which stops redelivery once that timeout expires.
Everything useful follows. Suppose a consumer dies mid-work: it never deleted anything, so the message reappears and someone else does the job. A consumer that is merely slow gets its work redelivered too, so the handler must be safe to run twice. That is a property of your code, not a setting: standard queues are documented as at-least-once delivery, and only first-in-first-out queues offer exactly-once processing. Messages do not wait forever either; retention defaults to four days and is configurable between sixty seconds and fourteen days. A repeatedly failing message needs somewhere to go, which is what a dead-letter queue is for.
This is one instance of a general habit. The framework's design principles read well as prompts: stop guessing at capacity, test at full production scale, and hold game days — scheduled rehearsals of failure.
Rule: assume every message is delivered at least once, and design the handler so a second delivery is boring.
If it is not in a template, it is not real
~2 min
A stack manages its resources as one unit, so a console edit puts the running system out of step with the file that claims to describe it. Drift detection finds those — only for properties the template set.
A template describes resources; a stack creates and configures them and lets you manage the collection as a single unit. Deleting the stack deletes the resources in it, which is exactly what you want until the collection includes a production database. That is not hypothetical: a resource carrying no deletion policy is deleted by default. Setting the policy to retain keeps it — and a retained resource keeps incurring charges until you delete it yourself.
Before applying an update, a change set previews the effect on running resources, including whether a change will delete or replace any. Replacement is the word to slow down on: a routine-looking property edit can throw away a resource and its contents.
Then drift. People change resources outside the tool, and those changes complicate later updates and deletions. Drift detection reports where actual configuration differs from what the template expects. Its limitation is the part almost nobody reads, and it is the whole lesson: drift is determined only for property values you explicitly set, and defaults do not count. For example, a property your template never mentioned can be edited by hand and drift will report nothing wrong.
Rule: change things in the template, and set the properties you care about explicitly, even when the value is the default.
Written by Keentune. We are not affiliated with or endorsed by the organizations whose documentation informs this guide, and any linked sources belong to their respective owners.
All exam, test, and product names and trademarks are the property of their respective owners and are used here for identification and reference only. Keentune is independent study practice — not affiliated with, authorized, or endorsed by any of these organizations.