Day 1 — Cloud, the AWS Account, and Identity
3h 30m · Phase 1 of 4 · Curriculum
Learning Objectives
By the end of today you can:
- Explain what cloud computing actually changes for a company that already owns servers, and where the responsibility boundary sits.
- Describe AWS's global infrastructure — Region, Availability Zone, edge location — and say what fails when each one fails.
- Secure a brand-new AWS account correctly: root user locked down, billing alarm set, an admin identity that is not root.
- Explain IAM's four nouns — principal, action, resource, condition — and read a policy document line by line.
- Describe, in order, everything that happens between your Java code calling
s3.putObject(...)and the object existing. - Reach AWS three ways — Console, CLI v2, SDK v2 — and explain why all three are the same thing.
The sentence you should be able to say tonight: "Every interaction with AWS is an authenticated, authorized HTTPS API call, and IAM decides whether it happens."
Prerequisites
- The baseline: backend development experience, HTTP, basic networking vocabulary.
- An AWS account you control. Create one before starting if you don't have one; it takes ~10 minutes and requires a payment card.
- Installed: AWS CLI v2, JDK 17+, Maven 3.9+, an IDE.
Verify your toolchain:
aws --version # expect: aws-cli/2.x.x ...
java -version # expect: openjdk version "17" or higher
mvn -version # expect: Apache Maven 3.9.xNo AWS knowledge is assumed. Nothing from any later day is used here.
Why Does This Exist?
You already know how to build a backend. So why does AWS exist at all?
Consider a company running its own servers. To add capacity they must: forecast demand months ahead, get budget approval, order hardware, wait for delivery, rack it, cable it, install an OS, patch it, and keep a team on call for the disk that fails at 3am. If the forecast was wrong high, they own idle metal for three years. If it was wrong low, they turn customers away.
Two things dominate that story, and neither is about writing software:
- Capacity is a guess made far in advance, and it is expensive in both directions.
- Most of the work is undifferentiated — every company patches operating systems, and no customer has ever chosen a product because of it.
Cloud computing attacks both. Capacity becomes an API call that completes in seconds, billed by the hour or the request. The undifferentiated work moves to the provider, in exchange for accepting their abstractions and their failure modes.
That trade — elasticity and operational offload, in exchange for a dependency you cannot inspect — is the whole deal. Everything in the next 14 days is a consequence of it.
Beginner Explanation
The cloud in one picture
Today, your mental model is probably this:
flowchart LR
L[My laptop] --> I((Internet)) --> S[A server somewhere] --> D[(A database)]AWS does not change that picture. It changes who owns the boxes and how fast you can get another one.
flowchart LR
L[My laptop] --> I((Internet)) --> A[AWS] --> App[My application<br/>running on AWS resources]For now, treat "AWS" as a very large company that will rent you compute, storage, databases, queues and about two hundred other things — through an API, priced per unit, available in seconds.
IaaS, PaaS, SaaS — a responsibility boundary, not a product tier
These three acronyms are usually taught as a hierarchy. They are better understood as a line drawn through a stack, marking where your responsibility stops.
flowchart TB
subgraph ON["On-premises"]
O1[Your app] --- O2[Runtime] --- O3[OS] --- O4[Virtualization] --- O5[Servers] --- O6[Data centre]
end
subgraph IAAS["IaaS · e.g. EC2"]
I1[Your app] --- I2[Runtime] --- I3[OS] --- I4["AWS: virtualization, servers, DC"]
end
subgraph PAAS["PaaS · e.g. Lambda"]
P1[Your app] --- P2["AWS: runtime, OS, virtualization, servers, DC"]
end
subgraph SAAS["SaaS · e.g. Gmail"]
S1["Someone else's app.<br/>You bring data and config."]
end| Model | You manage | AWS manages | Example |
|---|---|---|---|
| On-prem | Everything | Nothing | Your own rack |
| IaaS | App, runtime, OS, patching | Hardware, virtualization, network, facility | EC2 (Day 3) |
| PaaS | App, and the config of the platform | Everything below the app | Lambda (Day 5), RDS (Day 6) |
| SaaS | Your data and settings | The whole product | Not our subject |
Why a backend developer cares: every AWS service you pick moves this line. Choosing Lambda over EC2 is choosing to stop owning the OS — which is a gift right up until you need something the platform doesn't allow. That is the trade-off you will be asked to defend in interviews, repeatedly, starting on Day 4.
The shared responsibility model
AWS states it as: AWS is responsible for security of the cloud; you are responsible for security in the cloud.
Concretely:
| AWS's job | Your job |
|---|---|
| Physical data centre security | Who can call your APIs |
| Hypervisor, host OS patching | Guest OS patching (on EC2) |
| Network infrastructure | Your security groups and firewall rules |
| Service-level durability of S3 | Whether your bucket is public |
| Encryption capability (KMS) | Whether you turned encryption on |
| Availability of the AZ | Whether your app survives one AZ dying |
Notice how many of the right-hand entries are application design decisions. Almost every well-known "AWS breach" was a right-hand-column failure — most famously, buckets made public by their owners. AWS did not fail; a developer made a configuration choice.
Core Concepts
1. Global infrastructure: Region → Availability Zone → data centre
flowchart TB
subgraph GLOBAL["AWS Global Infrastructure"]
subgraph R1["Region · eu-west-1 (Ireland)"]
subgraph AZ1["AZ eu-west-1a"]
DC1[(one or more<br/>data centres)]
end
subgraph AZ2["AZ eu-west-1b"]
DC2[(data centres)]
end
subgraph AZ3["AZ eu-west-1c"]
DC3[(data centres)]
end
end
subgraph R2["Region · us-east-1 (N. Virginia)"]
AZ4[3+ AZs]
end
EDGE[Hundreds of edge locations<br/>worldwide · CloudFront, Day 10]
end
AZ1 <-->|low-latency private links| AZ2
AZ2 <-->|single-digit ms| AZ3Region — a named geographic area (us-east-1, eu-west-1, ap-south-1) containing multiple Availability Zones. Regions are isolated from each other by design: a Region is the largest unit of failure containment AWS offers, and data does not move between Regions unless you make it.
Availability Zone (AZ) — one or more physically separate data centres within a Region, with independent power, cooling and network, connected to the other AZs by high-bandwidth, low-latency private links (single-digit milliseconds). Most Regions have three or more. The whole point of an AZ is that one can fail without the others failing.
Edge location — a much larger number of small points of presence used for content delivery (CloudFront) and DNS (Route 53). Not where your application runs. Covered on Day 10.
The AZ identifier trap (worth knowing on Day 1)
eu-west-1a in your account is not necessarily the same physical AZ as eu-west-1a in someone else's — AWS randomizes the mapping per account to prevent everyone piling into "a". The stable identifier is the AZ ID (euw1-az1), which you'll see in the Console and in aws ec2 describe-availability-zones. This matters the moment you share resources across accounts.
aws ec2 describe-availability-zones --region eu-west-1 \
--query 'AvailabilityZones[].{Name:ZoneName,Id:ZoneId}' --output tableGlobal vs regional vs zonal services
This distinction predicts failure behaviour, so learn it now:
| Scope | Meaning | Examples |
|---|---|---|
| Zonal | The resource lives in exactly one AZ. If that AZ fails, it fails. | EC2 instance, EBS volume, RDS instance (single-AZ), subnet |
| Regional | AWS runs it across multiple AZs for you. Survives one AZ. | S3, SQS, DynamoDB, Lambda, ECS control plane |
| Global | One instance of the service for your whole account. | IAM, Route 53, CloudFront, Organizations, WAF (for CloudFront) |
⚠️ Common confusion: S3 is a regional service with a globally unique bucket namespace. Your bucket lives in one Region; nobody else on Earth can use its name. Those are two different facts that sound like one.
Choosing a Region
Four inputs, in roughly this order:
- Latency to your users — physics. A round trip London↔Virginia is ~75–90ms before your code runs.
- Data residency and legal requirements — often decides it outright.
- Service availability — not every service is in every Region, and new services land in
us-east-1first. - Price — the same instance can differ by 10–30% between Regions.
us-east-1is the oldest and largest Region. It has the most services and the most history of large-scale incidents, partly because several global service control planes are hosted there. This is a real architectural consideration, not folklore — we revisit it on Day 12.
2. The AWS account
An account is three things at once, and conflating them causes bad architecture:
| The account is… | Consequence |
|---|---|
| A billing boundary | One bill, one payment method |
| A security/blast-radius boundary | Resources in an account can be made to see each other easily; across accounts, never by accident |
| A quota boundary | Service limits are largely per-account, per-Region |
Large organizations therefore run many accounts (prod, staging, dev, per-team, per-workload) under AWS Organizations, governed by Service Control Policies. You do not need that today — but you need to know that "one account for everything" is a decision, not a default. We return to it on Day 11.
The root user
When you created the account, you created the root user — the email address and password you signed up with. It is not an IAM user and cannot be restricted by IAM policies. It can do everything, including close the account.
Your obligations on day one:
- Enable MFA on root. Non-negotiable.
- Delete any root access keys. If the signup flow created any, they should not exist.
- Create a separate admin identity and use that from now on.
- Do not use root again except for the handful of tasks that require it.
Tasks that genuinely require root (short list — these are the ones that come up):
- Closing the account, changing the account name/email/root password
- Changing the AWS Support plan
- Restoring an IAM user's permissions after someone has locked everyone out
- Enabling MFA Delete on an S3 bucket
- A few billing/tax configuration actions
Everything else — everything you will do in the next 14 days — is done as an IAM identity.
3. IAM — the four nouns
Every authorization decision in AWS answers one question:
Can this principal perform this action on this resource under these conditions?
flowchart LR
P["Principal<br/>who is asking"] --> A["Action<br/>s3:PutObject"]
A --> R["Resource<br/>arn:aws:s3:::my-bucket/*"]
R --> C["Condition<br/>optional constraints"]
C --> D{"Allow or Deny?"}Identities
| Entity | What it is | When you use it |
|---|---|---|
| IAM user | A long-lived identity with a password and/or access keys | Rarely, in modern practice. Humans should use IAM Identity Center; applications should use roles. |
| IAM group | A container of users, holding policies | Organizing human permissions |
| IAM role | A set of permissions with no credentials attached, that a trusted principal can temporarily assume | Almost always. EC2 instances, Lambda functions, ECS tasks, humans via SSO, other accounts. |
| IAM policy | A JSON document granting or denying actions on resources | Attached to any of the above |
The single most important idea in IAM:
A role is not an identity you log into. It is a costume. Something already trustworthy — an EC2 instance, a Lambda function, a federated human — temporarily wears it and receives short-lived credentials that expire automatically.
Why this matters: an access key is a secret that exists until someone deletes it. Role credentials expire in minutes to hours and are rotated for you. Every credential leak you read about involves the first kind.
The policy document
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "ReadWriteOrderDocuments",
"Effect": "Allow",
"Action": [
"s3:GetObject",
"s3:PutObject"
],
"Resource": "arn:aws:s3:::acme-orders-prod/documents/*",
"Condition": {
"StringEquals": { "s3:x-amz-server-side-encryption": "aws:kms" }
}
}
]
}Line by line:
| Field | Meaning |
|---|---|
Version | The policy language version. It is always 2012-10-17. It is not a date you choose; using anything else silently changes semantics (variables stop working). |
Sid | Optional human label. Use it — it shows up when debugging. |
Effect | Allow or Deny. An explicit Deny always wins, anywhere in any applicable policy. |
Action | Service-prefixed API operations. Wildcards allowed (s3:Get*). |
Resource | One or more ARNs. This is where least privilege actually happens. |
Condition | Extra constraints — source IP, MFA presence, encryption headers, tags, time. |
ARNs — Amazon Resource Names
Every resource in AWS has a globally unique name in a fixed format:
arn:partition:service:region:account-id:resource
arn:aws:s3:::acme-orders-prod/documents/inv-1.pdf S3: no region, no account (global namespace)
arn:aws:sqs:eu-west-1:123456789012:orders-queue SQS: regional, account-scoped
arn:aws:iam::123456789012:role/order-service-task IAM: global, so no region
arn:aws:dynamodb:eu-west-1:123456789012:table/ordersRead the empty fields — they tell you the service's scope. IAM ARNs have no region because IAM is global. S3 ARNs have neither region nor account because bucket names are globally unique.
The default is deny
If no policy allows an action, it is denied. There is no implicit permission anywhere in AWS. A new IAM user with no policies attached can do nothing — not even list what exists.
The full evaluation logic (SCPs, resource policies, permission boundaries, session policies) is Day 11. Today, two rules carry you:
- Implicit deny — not allowed means denied.
- Explicit deny wins — an explicit
Denybeats any number ofAllows.
Architecture
Where you are, after today
flowchart TB
subgraph YOU["You"]
DEV[Developer laptop]
CONSOLE[Browser · Console]
end
subgraph AWS["AWS · one account"]
subgraph GLOB["Global"]
IAM[IAM<br/>users · roles · policies]
end
subgraph REG["Region: eu-west-1"]
STS[STS endpoint]
S3[S3 bucket]
CW[CloudWatch<br/>billing alarm]
end
end
DEV -->|CLI v2 · signed HTTPS| STS
DEV -->|SDK v2 · signed HTTPS| S3
CONSOLE -->|same APIs, browser session| S3
IAM -.->|authorizes every call| S3
IAM -.-> STSNothing here is running your application yet. Today establishes the control plane: identity, access, and the three ways you drive it.
How It Works Internally
The signed AWS API call — the diagram that explains most access errors
This is the first of the eleven mandatory internal-working diagrams. Learn it properly now and Days 3, 5 and 11 get much easier.
sequenceDiagram
autonumber
participant APP as Your code (SDK v2)
participant CP as Credential provider chain
participant SIG as SigV4 signer
participant EP as Regional endpoint<br/>s3.eu-west-1.amazonaws.com
participant IAM as IAM authorization
participant SVC as S3 service
APP->>CP: need credentials
CP-->>APP: access key id + secret + session token
APP->>APP: build HTTPS request<br/>(method, path, headers, body hash)
APP->>SIG: sign canonical request
SIG-->>APP: Authorization: AWS4-HMAC-SHA256 ...
APP->>EP: HTTPS PUT /my-key
EP->>IAM: who is this principal,<br/>and may they do s3:PutObject on this ARN?
IAM-->>EP: Allow / Deny (+ reason)
alt Denied
EP-->>APP: 403 AccessDenied
else Allowed
EP->>SVC: execute
SVC-->>APP: 200 + ETag
endWhat each step means for you:
1–2. The credential provider chain. The SDK looks for credentials in a fixed order and uses the first source it finds. For AWS SDK for Java v2, DefaultCredentialsProvider checks:
1. Java system properties -Daws.accessKeyId, -Daws.secretAccessKey
2. Environment variables AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_SESSION_TOKEN
3. Web identity token AWS_WEB_IDENTITY_TOKEN_FILE (used by EKS/IRSA, OIDC federation)
4. Shared config/credentials ~/.aws/credentials and ~/.aws/config, honouring AWS_PROFILE
5. Container credentials AWS_CONTAINER_CREDENTIALS_RELATIVE_URI (ECS tasks — Day 4)
6. Instance profile via IMDS http://169.254.169.254/... (EC2 — Day 3)This chain is why "it works on my laptop but not in production" happens. Locally you hit step 4 (your profile, probably with broad permissions). In production you hit step 5 or 6 (a narrowly-scoped role). Same code, different principal, different answer from IAM.
3–5. SigV4 signing. The SDK builds a canonical form of the request (method, URI, sorted query string, sorted signed headers, SHA-256 of the body), derives a signing key from your secret plus the date, Region and service, and attaches an HMAC signature. Three consequences you will actually hit:
- Your clock matters. Signatures include a timestamp with a tolerance of about 5 minutes. A machine with badly skewed time gets
SignatureDoesNotMatchorRequestTimeTooSkewed. - The secret never crosses the wire. Only the signature does.
- Region and service are baked into the signing key — which is why you cannot point a client configured for
eu-west-1at aus-east-1endpoint and have it work.
- The endpoint is regional for regional services.
sqs.eu-west-1.amazonaws.comandsqs.us-east-1.amazonaws.comare different services holding different data. There is no "global SQS."
7–8. IAM authorization evaluates every applicable policy and returns allow or deny. On deny, the error message names the principal, the action and the resource — read it; it is usually telling you exactly what is wrong.
What the Console actually is
The AWS Console is a web application that makes these same signed API calls on your behalf, using temporary credentials from your browser session. There is nothing the Console can do that the API cannot.
flowchart LR
C[Console] --> API[AWS Service APIs]
CLI[AWS CLI v2] --> API
SDK[AWS SDK v2] --> API
API --> IAM{IAM authorization}
IAM --> SVC[Service]Why this matters from day one: if you learn AWS by clicking, you learn a UI that changes every few months. If you learn the API, you learn the thing that is stable, automatable, testable and the same in every interview. We use the Console to see, and the CLI/SDK to do.
Code
Setup: configure the CLI with a named profile
aws configure --profile aws-course
# AWS Access Key ID [None]: AKIA... (from Lab 0)
# AWS Secret Access Key [None]: ...
# Default region name [None]: eu-west-1 (pick one near you; be consistent)
# Default output format [None]: jsonThis writes two files:
~/.aws/credentials [aws-course] aws_access_key_id, aws_secret_access_key
~/.aws/config [profile aws-course] region, outputUse it either per-command or per-shell:
aws sts get-caller-identity --profile aws-course
export AWS_PROFILE=aws-course # then omit --profile"Who does AWS think I am?" — the first command you should ever run
aws sts get-caller-identity{
"UserId": "AIDA2EXAMPLEEXAMPLE",
"Account": "123456789012",
"Arn": "arn:aws:iam::123456789012:user/aws-course-admin"
}Read that ARN every time something is denied. Nine times out of ten the surprise is which identity you are, not which permission is missing.
The same question, from Java
pom.xml — note the BOM pattern, used in every Java example in this course:
<project xmlns="http://maven.apache.org/POM/4.0.0">
<modelVersion>4.0.0</modelVersion>
<groupId>com.acme</groupId>
<artifactId>aws-course-day1</artifactId>
<version>1.0.0</version>
<properties>
<maven.compiler.release>17</maven.compiler.release>
<aws.sdk.version>2.28.16</aws.sdk.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>software.amazon.awssdk</groupId>
<artifactId>bom</artifactId>
<version>${aws.sdk.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<!-- versions come from the BOM -->
<dependency>
<groupId>software.amazon.awssdk</groupId>
<artifactId>sts</artifactId>
</dependency>
<dependency>
<groupId>software.amazon.awssdk</groupId>
<artifactId>s3</artifactId>
</dependency>
</dependencies>
</project>The SDK version above is a concrete example. Check the current version before you start; the BOM pattern is what matters and does not change.
WhoAmI.java
package com.acme;
import software.amazon.awssdk.auth.credentials.DefaultCredentialsProvider;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.sts.StsClient;
import software.amazon.awssdk.services.sts.model.GetCallerIdentityResponse;
public class WhoAmI {
public static void main(String[] args) {
// Region is set explicitly. Relying on ambient region configuration is
// fine locally and a source of surprises in production.
try (StsClient sts = StsClient.builder()
.region(Region.EU_WEST_1)
// This is the default; naming it makes the credential chain visible
// to whoever reads this code next.
.credentialsProvider(DefaultCredentialsProvider.create())
.build()) {
GetCallerIdentityResponse id = sts.getCallerIdentity();
System.out.println("Account : " + id.account());
System.out.println("ARN : " + id.arn());
System.out.println("UserId : " + id.userId());
}
}
}mvn -q compile exec:java -Dexec.mainClass=com.acme.WhoAmITwo things to notice, both of which recur for the next 13 days:
- The client is
AutoCloseableand holds a connection pool. In a real application it is a singleton, created once. Creating one per request is a latency and file-descriptor bug (Day 5 shows what it does to a Lambda cold start). - No credentials appear in the code. They never will, in any example in this course.
S3 from Java — put and get one object
S3RoundTrip.java
package com.acme;
import software.amazon.awssdk.core.ResponseBytes;
import software.amazon.awssdk.core.sync.RequestBody;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.s3.S3Client;
import software.amazon.awssdk.services.s3.model.*;
import java.nio.charset.StandardCharsets;
public class S3RoundTrip {
public static void main(String[] args) {
String bucket = System.getenv("COURSE_BUCKET"); // never hardcode names
String key = "day1/hello-from-java.txt";
try (S3Client s3 = S3Client.builder().region(Region.EU_WEST_1).build()) {
PutObjectResponse put = s3.putObject(
PutObjectRequest.builder()
.bucket(bucket)
.key(key)
.contentType("text/plain")
.build(),
RequestBody.fromString("hello from the SDK", StandardCharsets.UTF_8));
System.out.println("PUT ok, etag=" + put.eTag());
ResponseBytes<GetObjectResponse> got = s3.getObjectAsBytes(
GetObjectRequest.builder().bucket(bucket).key(key).build());
System.out.println("GET ok, body=" + got.asUtf8String());
} catch (NoSuchBucketException e) {
// A *client* error: the bucket does not exist or is in another Region.
// Do not retry this.
System.err.println("Bucket not found: " + e.awsErrorDetails().errorMessage());
} catch (S3Exception e) {
// Typed error handling from day one. 403 here almost always means
// "the principal from get-caller-identity lacks this action on this ARN".
System.err.println("S3 error " + e.statusCode() + ": "
+ e.awsErrorDetails().errorMessage());
throw e;
}
}
}
catch (Exception e)does not appear anywhere in this course. AWS errors carry a status code and a typed exception, and the difference between "retry this" and "fix your policy" is the whole game. Day 5 and Day 8 build on this.
Hands-on
Lab 0 — Secure the account (20 min)
Objective: end with a root user nobody uses, an admin identity you do use, and an alarm that tells you before the bill surprises you.
Architecture:
flowchart LR
ROOT[Root user<br/>MFA on · keys deleted · unused] -.creates once.-> ADMIN[IAM admin user<br/>MFA · access keys]
ADMIN --> CLI[CLI profile: aws-course]
ROOT -.-> BUDGET[AWS Budget<br/>+ CloudWatch billing alarm]Steps:
- Sign in as root. Console → your account name (top right) → Security credentials.
- Enable MFA on the root user. Use an authenticator app or a hardware key. Do not skip this.
- Check for root access keys. If any exist, delete them. Root should have zero access keys, permanently.
- Create an admin IAM user: IAM → Users → Create user → name
aws-course-admin→ enable Console access → attach the AWS managed policyAdministratorAccess.AdministratorAccessis not least privilege. We are using it because the alternative on day one is spending an hour on policies before you can create anything. Day 11 narrows it, and you will do that exercise for real. - Enable MFA on the admin user too, and create access keys for it (CLI use case).
- Configure the CLI:
aws configure --profile aws-course. - Set a budget: Billing and Cost Management → Budgets → Create budget → Cost budget → monthly, e.g. $20 → alert at 80% and 100% to your email.
- Add a CloudWatch billing alarm as a second signal:
# NOTE: billing metrics are published only in us-east-1, regardless of where you work.
aws cloudwatch put-metric-alarm \
--alarm-name course-estimated-charges \
--namespace AWS/Billing \
--metric-name EstimatedCharges \
--dimensions Name=Currency,Value=USD \
--statistic Maximum --period 21600 --evaluation-periods 1 \
--threshold 20 --comparison-operator GreaterThanThreshold \
--region us-east-1Billing metrics must be enabled once in Billing preferences ("Receive Billing Alerts") before the alarm has data. Both the budget and the alarm are worth having: the budget forecasts, the alarm reacts.
- Sign out of root. Do not sign back in.
Verification:
aws sts get-caller-identity
# Arn must contain :user/aws-course-admin — NOT :rootCommon errors:
| Symptom | Cause | Fix |
|---|---|---|
Unable to locate credentials | No profile configured, or AWS_PROFILE unset | aws configure --profile aws-course, then export AWS_PROFILE=aws-course |
InvalidClientTokenId | Access key deleted or typo'd | Recreate the key pair in IAM |
| Alarm shows "Insufficient data" for hours | Billing alerts not enabled, or metrics publish slowly (up to several hours) | Enable in Billing preferences; wait |
Cleanup: nothing billable was created. Keep all of it.
Production relevance: this is, minus the IAM user, exactly what a real account baseline looks like — root locked and unused, an alarm on spend before anything exists to spend on. In a company, step 4 is replaced by IAM Identity Center and federated SSO; the principle is identical.
Lab 1 — One object, three ways (20 min)
Objective: prove to yourself that Console, CLI and SDK are one API.
Prerequisites: Lab 0. Permissions: s3:CreateBucket, s3:PutObject, s3:GetObject, s3:ListBucket, s3:DeleteObject, s3:DeleteBucket (covered by AdministratorAccess today).
Steps:
# 1. Create a bucket. The name must be globally unique — put your account id in it.
ACCOUNT=$(aws sts get-caller-identity --query Account --output text)
BUCKET="aws-course-${ACCOUNT}-day1"
REGION=eu-west-1
aws s3api create-bucket --bucket "$BUCKET" --region "$REGION" \
--create-bucket-configuration LocationConstraint="$REGION"
# us-east-1 is the exception: omit --create-bucket-configuration entirely.
# 2. Block public access explicitly (it is on by default for new buckets; be deliberate).
aws s3api put-public-access-block --bucket "$BUCKET" \
--public-access-block-configuration \
"BlockPublicAcls=true,IgnorePublicAcls=true,BlockPublicPolicy=true,RestrictPublicBuckets=true"
# 3. CLI upload
echo "hello from the CLI" > /tmp/cli.txt
aws s3 cp /tmp/cli.txt "s3://${BUCKET}/day1/hello-from-cli.txt"
# 4. List, with keys and sizes
aws s3api list-objects-v2 --bucket "$BUCKET" \
--query 'Contents[].{Key:Key,Size:Size}' --output table- Console: open S3, find the bucket, upload a file by drag-and-drop. Notice it appears alongside the CLI object — same bucket, same API.
- Java: run
S3RoundTripwithCOURSE_BUCKET=$BUCKET.
Verification — the point of the lab:
aws s3api list-objects-v2 --bucket "$BUCKET" --query 'Contents[].Key' --output text
# All three objects are listed. Nothing distinguishes the one the Console made.
# Now watch the API call itself:
aws s3api head-object --bucket "$BUCKET" --key day1/hello-from-cli.txt --debug 2>&1 \
| grep -i "Authorization: AWS4-HMAC-SHA256" | head -1That last line is the SigV4 signature from the internal-working diagram, on your own terminal.
Cleanup (do it now):
aws s3 rm "s3://${BUCKET}" --recursive
aws s3api delete-bucket --bucket "$BUCKET"Common errors:
| Symptom | Cause | Fix |
|---|---|---|
BucketAlreadyExists | Bucket names are globally unique across all AWS customers | Add your account id / a random suffix |
IllegalLocationConstraintException | LocationConstraint mismatched the --region, or you passed it for us-east-1 | Match them; omit for us-east-1 |
AccessDenied on list-objects-v2 | s3:ListBucket is on the bucket ARN, s3:GetObject on the object ARN — they are different resources | Grant both, on the right ARNs (Day 11 goes deep) |
BucketNotEmpty on delete | Objects (or versions) remain | aws s3 rm --recursive first |
Production relevance: the CLI is how you investigate production, the SDK is how your application works, and the Console is how you look. Teams that only know the Console cannot automate, cannot test, and cannot debug at 3am.
Production Considerations
| Concern | What a real account does |
|---|---|
| Human access | IAM Identity Center (SSO) federated to the corporate IdP. IAM users for humans are legacy; they mean long-lived keys, which means leaked keys. |
| Application access | Always roles. An access key in a deployment artifact is a finding in any security review. |
| Account structure | Multiple accounts under Organizations — at minimum prod separate from everything else. The account boundary is the strongest isolation AWS gives you, and it is free. |
| Region strategy | Pick one primary Region and be consistent. Resources scattered across Regions by accident are invisible, unbilled-to-anyone, and impossible to reason about. |
| Naming and tagging | Tag everything with owner, environment and cost centre from the first resource. Retrofitting tags across 400 resources is a quarter of someone's life. |
| Break-glass | One documented, MFA-protected root procedure, stored offline, tested annually. |
| Audit | CloudTrail on from day one (Day 11). It is the only record of who did what. |
Failure Scenarios
S1 — "What happens if that AZ dies?"
A startup runs a single EC2 instance serving 500 RPS in eu-west-1a.
| Question | Answer |
|---|---|
| What can fail? | The AZ — power, network, or the physical host |
| What happens? | Total outage. The instance is gone; its EBS volume is in that AZ too. |
| How does recovery happen? | Manually. Someone launches a new instance in another AZ from an AMI — if an AMI exists, and if the data was not only on that EBS volume. |
| How quickly? | Tens of minutes, optimistically. Hours if the runbook is imaginary. |
| Can the operation happen twice? | N/A today |
| Can data be lost? | Yes — anything on that instance's EBS volume without a snapshot or replica |
| Can data be duplicated? | N/A today |
The Day 1 lesson: AZs exist so that your architecture can survive one of them. Nothing about AWS gives you that automatically — placing an application in two AZs is a decision you make (Days 3, 4 and 6 make it).
S2 — Credentials in a public repository
A developer commits application.yml containing an access key. It is public for 11 minutes.
What actually happens: automated scanners find exposed AWS keys within seconds to minutes. Typical abuse is launching expensive GPU instances in every Region for cryptomining. Bills in the tens of thousands of dollars within hours are well documented.
| Question | Answer |
|---|---|
| What can fail? | The credential's confidentiality |
| What happens? | Anything the key's identity was permitted to do — which, for a convenience-created admin key, is everything |
| Recovery? | Deactivate the key immediately, rotate, review CloudTrail for the whole exposure window, contact AWS Support, delete everything created |
| How quickly? | Minutes to contain; days to be confident |
| Data lost / duplicated? | Possibly both, plus exfiltration |
The Day 1 lesson, stated as a rule: an application should never possess a credential that does not expire. Use roles. Every service in this course — EC2 (Day 3), ECS (Day 4), Lambda (Day 5) — has a mechanism for exactly this, and it is always the correct answer.
Detective controls that matter: enable IAM Access Analyzer, turn on GuardDuty (it flags anomalous credential use), and never create an access key for a workload.
Security
Today's security is entirely about identity, because on day one identity is the only attack surface you have.
The four questions the course asks of every architecture, applied to what you built:
| Question | Today's answer |
|---|---|
| Who can access this? | The aws-course-admin IAM user, and root (which you have locked and abandoned) |
| What credentials are used? | An access key on your laptop — acceptable for a personal learning account, not acceptable for a workload or a corporate human |
| Where is data encrypted? | S3 encrypts at rest by default (SSE-S3) and all API traffic is TLS. You made no explicit choice — Day 11 makes you make it |
| What happens if credentials leak? | Full account compromise. This is why the budget alarm from Lab 0 exists today and not later |
Least privilege, stated honestly: you attached AdministratorAccess today. That is a deliberate, temporary teaching choice. The course's position is that least privilege is derived from evidence (what did the service actually call?), not guessed up front — and Day 11's Lab 14 has you derive it from CloudTrail for a real service.
Performance
There is little to tune today, but two facts about latency will shape everything later:
- Every AWS API call is a network round trip — typically single-digit to low tens of milliseconds within a Region, and much more across Regions. Code that makes 50 sequential SDK calls in a request handler has a latency problem no instance size can fix. This is why batch APIs exist (Day 8) and why cross-Region chattiness is a design error (Day 12).
- Region choice is a latency floor. If your users are in London and your Region is
us-east-1, every request starts ~75ms in debt before your code runs. No amount of optimization recovers it.
Cost
What today cost: essentially nothing. S3 storage for a few text files is a fraction of a cent, and both labs end with deletion.
Prices used in this course (us-east-1, illustrative, verified at authoring time — always re-check current pricing):
| Item | Approx. price |
|---|---|
| S3 Standard storage | ~$0.023 per GB-month |
| S3 PUT/COPY/POST/LIST | ~$0.005 per 1,000 requests |
| S3 GET | ~$0.0004 per 1,000 requests |
| Data transfer out to Internet | First 100 GB/month free, then ~$0.09/GB (tiered down) |
| Data transfer in | Free |
| IAM, STS | Free |
| CloudWatch alarm | ~$0.10 per alarm-month (a small number of alarms are in the free tier) |
The one cost idea for Day 1: AWS bills for resources that exist, not resources you use. An idle EC2 instance, an unattached EBS volume, a NAT Gateway with no traffic, a provisioned database nobody queries — all bill by the hour, forever, silently. The single most common personal-AWS-bill story is a forgotten resource in a Region the person never looks at.
This is why every lab in this course ends with cleanup, and why you set the budget before you created anything.
Alternatives
| Instead of | You could | Why we use what we use |
|---|---|---|
| AWS | Azure, GCP, a rented rack | The concepts (identity, regions, managed services, failure domains) transfer almost entirely; the names do not. AWS has the largest share of backend job descriptions. |
| IAM user + access key | IAM Identity Center (SSO) with short-lived credentials | Correct for organizations; adds ~30 minutes of setup that teaches nothing on day one. Know that it is the real answer. |
| CLI v2 | CLI v1 | v1 is legacy; v2 has better auth/SSO support and is the current tool |
| SDK v2 for Java | SDK v1 | v1 is in maintenance. v2 has non-blocking clients, better pluggability, and is what new code uses. v1 appears in this course only when discussing legacy migration. |
| Console-first learning | API-first learning | The Console changes; the API does not. And you cannot automate a screenshot. |
Trade-offs
| Decision | Gain | Cost |
|---|---|---|
| Cloud over owned hardware | Elasticity, speed, operational offload, global reach | Variable spend, provider dependency, failure modes you cannot inspect, a new skill set |
| Higher on the IaaS→PaaS ladder | Less to operate, faster to ship | Less control, platform limits, more specific lock-in |
| One account | Simple, fast, everything can see everything | Everything can see everything. No blast-radius boundary. |
| Many accounts | Strong isolation, clean billing, clear ownership | Cross-account plumbing, more governance, a steeper start |
| Roles over long-lived keys | Automatic expiry and rotation; nothing to leak | Requires understanding trust policies — a real learning cost (Day 11) |
AdministratorAccess while learning | You are never blocked | If the key leaks, you lose everything. Acceptable only in a throwaway account with a budget alarm. |
Common Mistakes
- Using root for day-to-day work. It cannot be constrained and cannot be scoped. Lock it and walk away.
- Creating access keys for an application. Use a role. Every time. There is a role mechanism for every compute service in AWS.
- Assuming the Console is AWS. It is one client of the API.
- Forgetting which Region you are in. Half of all "my resource disappeared" reports are a Region selector. Set a default and be consistent.
- Ignoring the ARN in an error message.
AccessDeniedmessages usually name the principal, the action and the resource. That is the answer, not a mystery. - Thinking S3 is global because the bucket name is. The bucket lives in one Region.
- Believing an AZ letter is a place.
eu-west-1adiffers per account; the AZ ID is the stable identifier. - Treating
Version: "2012-10-17"as something to update. It is a language version, not a date. - Assuming permissions are inherited from "being in the account." The default is deny, always.
- Creating resources before setting a budget. Ten minutes of prevention.
Interview Questions
(2–3 YOE and 4–5 YOE level — see the full plan)
2–3 YOE
- What is the difference between a Region and an Availability Zone?
- What is an IAM role, and how does it differ from an IAM user?
- What happens if you attach no policies to a new IAM user?
- What is an ARN, and what can you tell from one that has no Region in it?
- Name three ways to interact with AWS and explain what they have in common.
- Why should you enable MFA on the root user and then stop using it?
4–5 YOE
- Your code works locally and gets
AccessDeniedafter deploying. Nothing about the policy changed. What is your first hypothesis, and what command confirms it? - Walk me through what the SDK does between your
putObjectcall and the HTTP request leaving the machine. - A colleague suggests storing an access key in the application's environment variables for production. Make the argument against it, and say what you would do instead.
- What is the difference between an "implicit deny" and an "explicit deny", and why does it matter when you combine policies?
- Why can't you point a client configured for
eu-west-1at aus-east-1endpoint?
Senior-Level Questions
(6–7 YOE and 8–10 YOE level)
6–7 YOE
- You are designing the account structure for a company with three products and four environments. How many AWS accounts, and why? What do you put in each?
- A service in account A needs to read objects from a bucket in account B. Describe the mechanisms available and which you would choose.
- How would you detect that an access key has been leaked, and what does your first hour look like?
- Your company must operate in the EU only. What does that constrain, beyond "pick an EU Region"?
- Explain why
us-east-1has a different risk profile from other Regions, and whether that should change your architecture.
8–10 YOE
- Argue both sides of "one AWS account per microservice." Then decide, and say what would change your mind.
- Your organization has 200 engineers and no IAM strategy. Describe the first 90 days, in the order you would do it, and what you would deliberately not do.
- Where exactly does the shared responsibility model leave ambiguity, and how have you seen that ambiguity cause an incident?
- A team proposes granting
AdministratorAccessto a CI pipeline "temporarily, to unblock delivery." What is your response, and what do you offer instead that does not slow them down?
Answering pattern reminder: state assumptions → name the dominating constraint → give 2–3 options → choose and say what you traded → say what would prove you wrong. See the five-move pattern.
Day-End Revision
The five sentences
- A Region is an isolated geographic area of 3+ Availability Zones; an AZ is a physically separate failure domain you can span.
- Every AWS interaction is a signed HTTPS API call — Console, CLI and SDK are three clients of the same API.
- IAM answers one question: may this principal perform this action on this resource under these conditions? Default deny; explicit deny wins.
- Roles give expiring credentials to things that are already trustworthy; access keys are long-lived secrets and are the wrong answer for workloads.
- AWS bills for resources that exist, not resources you use.
The diagram to redraw from memory: the signed API call sequence — credential chain → SigV4 → regional endpoint → IAM → service.
The numbers
Policy Version | always 2012-10-17 |
| SigV4 clock tolerance | ~5 minutes |
| AZs per Region | 3 or more, typically |
| Billing metrics Region | us-east-1, always |
| Credential chain steps (Java v2) | 6, in fixed order |
Today's trap: "S3 is a global service." It is regional, with a global bucket namespace. Two different facts.
Tomorrow needs: IAM (you'll write your first non-trivial policies), the idea that a Region contains AZs (subnets are per-AZ), and the CLI profile you configured.
Mini Assignment
Time: 30–40 minutes. This is the first commit of the repository that becomes your capstone.
- Create a Git repository named
aws-course. Add aday-01/directory. - Write a small Java program,
AccountInventory, that:- prints the caller identity (account, ARN);
- lists all S3 buckets in the account with their creation dates;
- lists all Regions enabled for the account (
Ec2Client.describeRegions()); - handles
AccessDeniedExceptiondistinctly from other errors, printing which action was denied.
- Run it with your
aws-courseprofile, then run it again withAWS_PROFILEunset and record what changes and why. - In
day-01/NOTES.md, answer in your own words:- Which step of the credential provider chain served each run?
- What would need to change for this code to run unmodified on an EC2 instance with no credentials file?
- Commit.
Success criterion: you can explain, without notes, why the same binary behaves differently in the two runs — and you have not typed a credential into a file the repository tracks.
AWS Documentation
Primary sources for today. AWS changes frequently — prefer these over any summary, including this one.
- AWS Global Infrastructure
- Regions and Availability Zones
- IAM User Guide
- IAM security best practices
- Signing AWS API requests (SigV4)
- AWS SDK for Java 2.x — credentials
- AWS CLI v2 configuration
- Shared Responsibility Model