AWS · lesson 1 of 10
Introduction to AWS
What AWS regions and availability zones really are, then configure the CLI and confirm your identity against LocalStack with no AWS account required.
By SkillByExample · Published
AWS is around 200 services built on top of compute, storage, and networking that Amazon rents out by the second. The service catalogue looks overwhelming until you notice that almost everything sits on the same three primitives: a place to run code, a place to store bytes, and a network to move them — and that every one of those is scoped to a region.
Regions and availability zones
| Region code | Location | Availability zones |
|---|---|---|
us-east-1 | N. Virginia | 6 (a-f) |
us-west-2 | Oregon | 4 (a-d) |
eu-west-1 | Ireland | 3 (a-c) |
ap-south-1 | Mumbai | 3 (a-c) |
A region is a self-contained geographic area. Nothing replicates across regions unless
you explicitly configure it to — an S3 bucket in eu-west-1 is invisible from us-east-1.
Inside a region, AWS operates multiple availability zones (AZs): physically separate
data centers, each with independent power and networking, connected to the others by
low-latency private links. Spreading a database across two AZs in the same region survives
a data center failure; nothing survives a whole region going down except a second region.
This lesson uses LocalStack (https://localstack.cloud), an open-source AWS emulator
that runs the real AWS CLI against a local server on port 4566. It costs nothing, needs no
account, and every command is identical to real AWS except for one added flag
(--endpoint-url). Where a later lesson needs behaviour LocalStack cannot emulate
faithfully, it says so and switches to a real free-tier account.
Installing and starting LocalStack
pip install localstack awscli-local
localstack start -d
$ localstack start -d
__ _______ __ __
/ / ____ _________ _/ / ___// /_____ ______/ /__
/ / / __ \/ ___/ __ `/ /\__ \/ __/ __ `/ ___/ //_/
/ /___/ /_/ / /__/ /_/ / /___/ / /_/ /_/ / /__/ ,<
/_____/\____/\___/\__,_/_/____/\__/\__,_/\___/_/|_|
LocalStack CLI 3.4.0
[16:02:11] starting LocalStack in Docker mode 🐳
[16:02:14] Waiting for all LocalStack services to be ready
[16:02:19] Ready.
localstack status services
$ localstack status services
┏━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━┓
┃ Service ┃ Status ┃
┡━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━┩
│ acm │ ✔ available │
│ cloudformation │ ✔ available │
│ cloudwatch │ ✔ available │
│ ec2 │ ✔ available │
│ iam │ ✔ available │
│ lambda │ ✔ available │
│ s3 │ ✔ running │
│ sts │ ✔ running │
└──────────────────┴───────────────┘
Configuring the CLI
LocalStack does not validate credentials, so any non-empty values work. Real AWS needs a
real access key pair from IAM instead — the iam lesson covers issuing one properly.
aws configure --profile localstack
$ aws configure --profile localstack
AWS Access Key ID [None]: test
AWS Secret Access Key [None]: test
Default region name [None]: us-east-1
Default output format [None]: json
Confirming identity
aws sts get-caller-identity is the smallest possible AWS call — it asks “who am I?” and
proves credentials, network access, and region are all wired up correctly.
aws --endpoint-url=http://localhost:4566 --profile localstack sts get-caller-identity
$ aws --endpoint-url=http://localhost:4566 --profile localstack sts get-caller-identity
{
"UserId": "AKIAIOSFODNN7EXAMPLE",
"Account": "000000000000",
"Arn": "arn:aws:iam::000000000000:root"
}
000000000000 is LocalStack’s fixed test account — every request returns it regardless of
which access key was supplied. Against a real AWS account, the same command (dropping
--endpoint-url and pointing the profile at real credentials) returns your genuine account
ID and the ARN of the actual IAM identity:
$ aws --profile personal sts get-caller-identity
{
"UserId": "AIDAJQABLZS4A3QDU576Q",
"Account": "482719503661",
"Arn": "arn:aws:iam::482719503661:user/abhishek"
}
Listing regions and zones for real
describe-regions and describe-availability-zones are read-only EC2 metadata calls — no
resources are created, so they are safe to run against a real account too.
aws --endpoint-url=http://localhost:4566 --profile localstack \
ec2 describe-regions --query 'Regions[].RegionName' --output table
$ aws --endpoint-url=http://localhost:4566 --profile localstack ec2 describe-regions --query 'Regions[].RegionName' --output table
-------------------------
| DescribeRegions |
+-------------------------+
| us-east-1 |
| us-east-2 |
| us-west-1 |
| us-west-2 |
| eu-west-1 |
| ap-south-1 |
+-------------------------+
aws --endpoint-url=http://localhost:4566 --profile localstack \
ec2 describe-availability-zones --region us-east-1 \
--query 'AvailabilityZones[].ZoneName' --output text
$ aws --endpoint-url=http://localhost:4566 --profile localstack ec2 describe-availability-zones --region us-east-1 --query 'AvailabilityZones[].ZoneName' --output text
us-east-1a us-east-1b us-east-1c us-east-1d us-east-1e us-east-1f
Six AZs for one region is exactly the table above — this is the same API a real account hits, just served locally.
awslocal: the shortcut
Typing --endpoint-url=http://localhost:4566 --profile localstack on every command gets
old fast. awslocal, installed alongside awscli-local, is a thin wrapper that adds both
automatically — the rest of this track uses it wherever a lesson runs on LocalStack.
awslocal sts get-caller-identity
$ awslocal sts get-caller-identity
{
"UserId": "AKIAIOSFODNN7EXAMPLE",
"Account": "000000000000",
"Arn": "arn:aws:iam::000000000000:root"
}
Identical output to the long form above, because it is the long form — awslocal just
saves the typing.
Practice
1. Start LocalStack and confirm the S3 and Lambda services are running.
localstack status services | grep -E 's3|lambda'
│ lambda │ ✔ available │
│ s3 │ ✔ running │
running means the service has handled at least one request this session; available
means it is registered but has not been called yet — both are healthy states.
2. Run aws sts get-caller-identity against LocalStack twice with different (fake) access keys.
aws --endpoint-url=http://localhost:4566 configure set aws_access_key_id fake-key-1
aws --endpoint-url=http://localhost:4566 sts get-caller-identity
aws --endpoint-url=http://localhost:4566 configure set aws_access_key_id fake-key-2
aws --endpoint-url=http://localhost:4566 sts get-caller-identity
{
"UserId": "AKIAIOSFODNN7EXAMPLE",
"Account": "000000000000",
"Arn": "arn:aws:iam::000000000000:root"
}
{
"UserId": "AKIAIOSFODNN7EXAMPLE",
"Account": "000000000000",
"Arn": "arn:aws:iam::000000000000:root"
}
Same output both times — proof that LocalStack Community does not check the key at all.
This is exactly why the iam lesson, which needs AccessDenied to be real, uses an actual
AWS account instead.
3. List availability zones for eu-west-1 and count them.
awslocal ec2 describe-availability-zones --region eu-west-1 \
--query 'length(AvailabilityZones)'
3
Matches the table at the top of the lesson — Ireland has three AZs against Virginia’s six,
which is why some architectures pin multi-AZ RDS specifically to us-east-1 or eu-west-1
rather than smaller regions.
4. Find the region name for zone ap-south-1b without hardcoding it.
awslocal ec2 describe-availability-zones \
--filters Name=zone-name,Values=ap-south-1b \
--query 'AvailabilityZones[0].RegionName' --output text
ap-south-1
--filters on Name=zone-name is the general pattern for turning a partial fact into a
full record — the same shape reappears filtering EC2 instances by tag in the ec2 lesson.
5. Configure a second profile pointed at a real AWS account and compare its get-caller-identity output to LocalStack's.
aws configure --profile real
aws --profile real sts get-caller-identity
{
"UserId": "AIDAJQABLZS4A3QDU576Q",
"Account": "482719503661",
"Arn": "arn:aws:iam::482719503661:user/abhishek"
}
A 12-digit account ID that is not 000000000000, and an ARN naming your actual IAM user —
this is the tell for “am I accidentally about to create real, billable resources?” Get in
the habit of checking it before any command with a create-* or run-* verb.
Next: IAM — users, roles, and the least-privilege policy that turns into an AccessDenied
you have to debug.