AWS (Amazon Web Services) is the world's most widely adopted cloud platform, with over 200 fully featured services. This cheat sheet covers the AWS CLI, the most important services, and the patterns you use every day.
Quick reference
The AWS CLI commands you reach for most often.
| Command | What it does |
|---|---|
aws configure |
Set up credentials and region |
aws s3 ls |
List all S3 buckets |
aws s3 cp file.txt s3://bucket/ |
Upload file to S3 |
aws s3 sync ./dist s3://bucket/ |
Sync folder to S3 |
aws ec2 describe-instances |
List EC2 instances |
aws ec2 start-instances --instance-ids i-xxx |
Start an instance |
aws ec2 stop-instances --instance-ids i-xxx |
Stop an instance |
aws lambda invoke --function-name fn out.json |
Invoke a Lambda |
aws logs tail /aws/lambda/fn --follow |
Stream Lambda logs |
aws sts get-caller-identity |
Show current IAM identity |
aws cloudformation deploy --stack-name ... |
Deploy CloudFormation stack |
aws rds describe-db-instances |
List RDS databases |
aws iam list-users |
List IAM users |
aws ssm get-parameter --name /app/secret |
Fetch SSM parameter |
AWS CLI setup
Installation
# macOS (Homebrew)
brew install awscli
# Linux
curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o awscliv2.zip
unzip awscliv2.zip && sudo ./aws/install
# Windows (PowerShell)
msiexec.exe /i https://awscli.amazonaws.com/AWSCLIV2.msi
# Verify
aws --version
Configure credentials
# Interactive setup (writes ~/.aws/credentials and ~/.aws/config)
aws configure
# You will be prompted:
# AWS Access Key ID: AKIA...
# AWS Secret Access Key: ...
# Default region: eu-west-1
# Default output format: json
# Use a named profile
aws configure --profile prod
aws s3 ls --profile prod
# Temporary credentials via STS (MFA)
aws sts get-session-token \
--serial-number arn:aws:iam::123456789:mfa/user \
--token-code 123456
# Export env vars (overrides config)
export AWS_ACCESS_KEY_ID=AKIA...
export AWS_SECRET_ACCESS_KEY=...
export AWS_DEFAULT_REGION=eu-west-1
Useful CLI flags
--region eu-west-1 # Override region for one command
--profile staging # Use named profile
--output json|text|table|yaml
--query 'Instances[*].InstanceId' # JMESPath filter
--no-sign-request # Public buckets without creds
--dry-run # Validate without executing (EC2)
S3 (Simple Storage Service)
Object storage — the backbone of almost every AWS workload.
Bucket operations
# List buckets
aws s3 ls
# Create bucket (us-east-1 has no LocationConstraint)
aws s3 mb s3://my-bucket
aws s3 mb s3://my-bucket --region eu-west-1
# Delete empty bucket
aws s3 rb s3://my-bucket
# Force-delete bucket with all objects
aws s3 rb s3://my-bucket --force
Object operations
# List objects
aws s3 ls s3://my-bucket/
aws s3 ls s3://my-bucket/prefix/ --recursive --human-readable
# Upload
aws s3 cp file.txt s3://my-bucket/
aws s3 cp file.txt s3://my-bucket/folder/file.txt
aws s3 cp ./dist/ s3://my-bucket/ --recursive
# Download
aws s3 cp s3://my-bucket/file.txt .
aws s3 cp s3://my-bucket/ ./local/ --recursive
# Sync (only changed files, great for deploys)
aws s3 sync ./build s3://my-bucket/ --delete
aws s3 sync s3://my-bucket/ ./backup/
# Delete object
aws s3 rm s3://my-bucket/file.txt
aws s3 rm s3://my-bucket/folder/ --recursive
# Move/rename
aws s3 mv s3://my-bucket/old.txt s3://my-bucket/new.txt
# Presigned URL (time-limited access)
aws s3 presign s3://my-bucket/private.pdf --expires-in 3600
S3 static website hosting
# Enable website hosting
aws s3 website s3://my-bucket \
--index-document index.html \
--error-document error.html
# Make bucket public (website hosting)
aws s3api put-bucket-policy --bucket my-bucket --policy '{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Principal": "*",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::my-bucket/*"
}]
}'
EC2 (Elastic Compute Cloud)
Virtual machines in the cloud.
Instance management
# List instances
aws ec2 describe-instances \
--query 'Reservations[*].Instances[*].[InstanceId,State.Name,PublicIpAddress,Tags[?Key==`Name`].Value|[0]]' \
--output table
# Start / stop / reboot / terminate
aws ec2 start-instances --instance-ids i-0abc123
aws ec2 stop-instances --instance-ids i-0abc123
aws ec2 reboot-instances --instance-ids i-0abc123
aws ec2 terminate-instances --instance-ids i-0abc123
# Launch instance
aws ec2 run-instances \
--image-id ami-0c101f26f147fa7fd \
--instance-type t3.micro \
--key-name my-key \
--security-group-ids sg-0abc123 \
--subnet-id subnet-0abc123 \
--count 1 \
--tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=web-server}]'
# SSH into instance
aws ec2-instance-connect send-ssh-public-key \
--instance-id i-0abc123 \
--instance-os-user ec2-user \
--ssh-public-key file://~/.ssh/id_rsa.pub
ssh ec2-user@<public-ip>
Key pairs and security groups
# Create key pair
aws ec2 create-key-pair \
--key-name my-key \
--query 'KeyMaterial' \
--output text > my-key.pem
chmod 400 my-key.pem
# Create security group
aws ec2 create-security-group \
--group-name web-sg \
--description "Web server SG" \
--vpc-id vpc-0abc123
# Allow inbound HTTP/HTTPS/SSH
aws ec2 authorize-security-group-ingress \
--group-id sg-0abc123 \
--protocol tcp --port 80 --cidr 0.0.0.0/0
aws ec2 authorize-security-group-ingress \
--group-id sg-0abc123 \
--protocol tcp --port 443 --cidr 0.0.0.0/0
aws ec2 authorize-security-group-ingress \
--group-id sg-0abc123 \
--protocol tcp --port 22 --cidr MY.IP.ADD.RESS/32
AMIs and snapshots
# List available AMIs (Amazon Linux 2023)
aws ec2 describe-images \
--owners amazon \
--filters "Name=name,Values=al2023-ami-*" \
--query 'sort_by(Images, &CreationDate)[-1].ImageId'
# Create AMI from instance
aws ec2 create-image \
--instance-id i-0abc123 \
--name "my-server-backup-$(date +%Y-%m-%d)" \
--no-reboot
# Create snapshot
aws ec2 create-snapshot \
--volume-id vol-0abc123 \
--description "Weekly backup"
IAM (Identity and Access Management)
Control who can do what in your AWS account.
Users and groups
# List users
aws iam list-users --query 'Users[*].[UserName,CreateDate]' --output table
# Create user
aws iam create-user --user-name alice
# Add user to group
aws iam add-user-to-group --user-name alice --group-name developers
# Create access keys
aws iam create-access-key --user-name alice
# Delete access key
aws iam delete-access-key --user-name alice --access-key-id AKIA...
# Attach managed policy
aws iam attach-user-policy \
--user-name alice \
--policy-arn arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess
Roles and policies
# Create role (for EC2 to access S3)
aws iam create-role \
--role-name ec2-s3-role \
--assume-role-policy-document '{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Principal": { "Service": "ec2.amazonaws.com" },
"Action": "sts:AssumeRole"
}]
}'
# Attach policy to role
aws iam attach-role-policy \
--role-name ec2-s3-role \
--policy-arn arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess
# Create instance profile (attach role to EC2)
aws iam create-instance-profile --instance-profile-name ec2-s3-profile
aws iam add-role-to-instance-profile \
--instance-profile-name ec2-s3-profile \
--role-name ec2-s3-role
# Who am I?
aws sts get-caller-identity
Inline policy example
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": ["s3:GetObject", "s3:PutObject"],
"Resource": "arn:aws:s3:::my-bucket/*"
},
{
"Effect": "Allow",
"Action": "s3:ListBucket",
"Resource": "arn:aws:s3:::my-bucket"
}
]
}
Lambda
Serverless functions — run code without managing servers.
# List functions
aws lambda list-functions \
--query 'Functions[*].[FunctionName,Runtime,LastModified]' \
--output table
# Create function (from zip)
zip function.zip index.js
aws lambda create-function \
--function-name my-function \
--runtime nodejs20.x \
--role arn:aws:iam::123456789:role/lambda-role \
--handler index.handler \
--zip-file fileb://function.zip
# Update function code
zip function.zip index.js
aws lambda update-function-code \
--function-name my-function \
--zip-file fileb://function.zip
# Invoke function
aws lambda invoke \
--function-name my-function \
--payload '{"key":"value"}' \
--cli-binary-format raw-in-base64-out \
output.json
cat output.json
# Invoke asynchronously
aws lambda invoke \
--function-name my-function \
--invocation-type Event \
--payload '{}' \
--cli-binary-format raw-in-base64-out \
/dev/null
# View logs
aws logs tail /aws/lambda/my-function --follow
# Set environment variables
aws lambda update-function-configuration \
--function-name my-function \
--environment 'Variables={DB_URL=postgres://...,API_KEY=abc}'
# Delete function
aws lambda delete-function --function-name my-function
Lambda handler (Node.js)
// index.js
export const handler = async (event, context) => {
console.log('Event:', JSON.stringify(event));
return {
statusCode: 200,
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ message: 'Hello from Lambda!' }),
};
};
Lambda handler (Python)
import json
def handler(event, context):
print(f"Event: {json.dumps(event)}")
return {
"statusCode": 200,
"headers": {"Content-Type": "application/json"},
"body": json.dumps({"message": "Hello from Lambda!"}),
}
RDS (Relational Database Service)
Managed databases — PostgreSQL, MySQL, MariaDB, Oracle, SQL Server.
# List DB instances
aws rds describe-db-instances \
--query 'DBInstances[*].[DBInstanceIdentifier,DBInstanceStatus,Endpoint.Address]' \
--output table
# Create PostgreSQL instance
aws rds create-db-instance \
--db-instance-identifier my-postgres \
--db-instance-class db.t3.micro \
--engine postgres \
--engine-version 16.1 \
--master-username admin \
--master-user-password MySecurePass123! \
--allocated-storage 20 \
--no-publicly-accessible \
--db-subnet-group-name my-subnet-group
# Create DB snapshot
aws rds create-db-snapshot \
--db-instance-identifier my-postgres \
--db-snapshot-identifier my-postgres-snapshot-2024
# Start / stop instance (save costs in dev)
aws rds start-db-instance --db-instance-identifier my-postgres
aws rds stop-db-instance --db-instance-identifier my-postgres
# Delete instance (no final snapshot for dev)
aws rds delete-db-instance \
--db-instance-identifier my-postgres \
--skip-final-snapshot
CloudFormation
Infrastructure as Code — define all your AWS resources in YAML or JSON.
# Validate template
aws cloudformation validate-template \
--template-body file://template.yaml
# Create stack
aws cloudformation create-stack \
--stack-name my-stack \
--template-body file://template.yaml \
--parameters ParameterKey=Env,ParameterValue=production \
--capabilities CAPABILITY_IAM
# Update stack
aws cloudformation update-stack \
--stack-name my-stack \
--template-body file://template.yaml \
--capabilities CAPABILITY_IAM
# Deploy (create or update, recommended)
aws cloudformation deploy \
--stack-name my-stack \
--template-file template.yaml \
--parameter-overrides Env=production \
--capabilities CAPABILITY_IAM CAPABILITY_NAMED_IAM
# Watch stack events
aws cloudformation describe-stack-events \
--stack-name my-stack \
--query 'StackEvents[*].[Timestamp,ResourceStatus,ResourceType,LogicalResourceId]' \
--output table
# List stacks
aws cloudformation list-stacks \
--stack-status-filter CREATE_COMPLETE UPDATE_COMPLETE
# Delete stack
aws cloudformation delete-stack --stack-name my-stack
Minimal CloudFormation template (S3 + Lambda)
AWSTemplateFormatVersion: "2010-09-09"
Description: S3 bucket + Lambda function
Parameters:
Env:
Type: String
Default: dev
AllowedValues: [dev, staging, production]
Resources:
AssetsBucket:
Type: AWS::S3::Bucket
Properties:
BucketName: !Sub "my-assets-${Env}-${AWS::AccountId}"
VersioningConfiguration:
Status: Enabled
LambdaRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal:
Service: lambda.amazonaws.com
Action: sts:AssumeRole
ManagedPolicyArns:
- arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
MyFunction:
Type: AWS::Lambda::Function
Properties:
FunctionName: !Sub "my-function-${Env}"
Runtime: nodejs20.x
Handler: index.handler
Role: !GetAtt LambdaRole.Arn
Code:
ZipFile: |
exports.handler = async (event) => ({
statusCode: 200,
body: JSON.stringify({ env: process.env.ENV }),
});
Environment:
Variables:
ENV: !Ref Env
Outputs:
BucketName:
Value: !Ref AssetsBucket
FunctionArn:
Value: !GetAtt MyFunction.Arn
SSM Parameter Store
Secure storage for configuration values and secrets.
# Store a plain text parameter
aws ssm put-parameter \
--name "/app/db-host" \
--value "mydb.cluster.eu-west-1.rds.amazonaws.com" \
--type String
# Store a secret (encrypted with KMS)
aws ssm put-parameter \
--name "/app/db-password" \
--value "MySecurePass123!" \
--type SecureString
# Get a parameter
aws ssm get-parameter --name "/app/db-host" --query 'Parameter.Value' --output text
# Get a secret (decrypt it)
aws ssm get-parameter --name "/app/db-password" --with-decryption --query 'Parameter.Value' --output text
# Get all parameters by path
aws ssm get-parameters-by-path \
--path "/app/" \
--recursive \
--with-decryption \
--query 'Parameters[*].[Name,Value]' \
--output table
# Delete parameter
aws ssm delete-parameter --name "/app/db-host"
Read SSM in Node.js
import { SSMClient, GetParameterCommand } from "@aws-sdk/client-ssm";
const ssm = new SSMClient({ region: "eu-west-1" });
async function getSecret(name) {
const result = await ssm.send(
new GetParameterCommand({ Name: name, WithDecryption: true })
);
return result.Parameter.Value;
}
const dbPassword = await getSecret("/app/db-password");
CloudWatch
Monitoring, logs, and alarms.
# List log groups
aws logs describe-log-groups --query 'logGroups[*].logGroupName' --output table
# Stream logs in real time
aws logs tail /aws/lambda/my-function --follow
aws logs tail /ecs/my-service --follow --since 1h
# Get log events
aws logs get-log-events \
--log-group-name /aws/lambda/my-function \
--log-stream-name "2024/01/15/[$LATEST]abc123" \
--start-time $(date -d '1 hour ago' +%s000)
# Create metric alarm (CPU > 80% for 5 min)
aws cloudwatch put-metric-alarm \
--alarm-name high-cpu \
--metric-name CPUUtilization \
--namespace AWS/EC2 \
--period 300 \
--evaluation-periods 1 \
--threshold 80 \
--comparison-operator GreaterThanThreshold \
--statistic Average \
--dimensions Name=InstanceId,Value=i-0abc123 \
--alarm-actions arn:aws:sns:eu-west-1:123:alerts
ECS (Elastic Container Service)
Run Docker containers at scale.
# List clusters
aws ecs list-clusters
# List services
aws ecs list-services --cluster my-cluster
# Describe service
aws ecs describe-services \
--cluster my-cluster \
--services my-service \
--query 'services[0].[serviceName,status,runningCount,desiredCount]'
# Force new deployment (rolling update)
aws ecs update-service \
--cluster my-cluster \
--service my-service \
--force-new-deployment
# Scale service
aws ecs update-service \
--cluster my-cluster \
--service my-service \
--desired-count 3
# Run one-off task
aws ecs run-task \
--cluster my-cluster \
--task-definition my-task:5 \
--launch-type FARGATE \
--network-configuration 'awsvpcConfiguration={subnets=[subnet-xxx],securityGroups=[sg-xxx],assignPublicIp=ENABLED}'
VPC (Virtual Private Cloud)
Your isolated network in AWS.
# List VPCs
aws ec2 describe-vpcs --query 'Vpcs[*].[VpcId,CidrBlock,Tags[?Key==`Name`].Value|[0]]' --output table
# List subnets
aws ec2 describe-subnets \
--query 'Subnets[*].[SubnetId,CidrBlock,AvailabilityZone,MapPublicIpOnLaunch]' \
--output table
# Create VPC
aws ec2 create-vpc --cidr-block 10.0.0.0/16
# Create subnet
aws ec2 create-subnet \
--vpc-id vpc-0abc123 \
--cidr-block 10.0.1.0/24 \
--availability-zone eu-west-1a
# Create internet gateway and attach
aws ec2 create-internet-gateway
aws ec2 attach-internet-gateway \
--internet-gateway-id igw-0abc123 \
--vpc-id vpc-0abc123
AWS service comparison table
| Service | Category | Use case |
|---|---|---|
| EC2 | Compute | VMs — full OS control |
| ECS / Fargate | Compute | Docker containers |
| Lambda | Compute | Serverless functions, event-driven |
| Elastic Beanstalk | Compute | PaaS — deploy web apps without IaC |
| S3 | Storage | Object storage — files, backups, static sites |
| EBS | Storage | Block storage attached to EC2 |
| EFS | Storage | Shared NFS for multiple EC2 |
| RDS | Database | Managed relational DB (Postgres, MySQL…) |
| Aurora | Database | AWS-optimised Postgres/MySQL — 5× faster |
| DynamoDB | Database | Serverless NoSQL — key-value/document |
| ElastiCache | Database | Managed Redis / Memcached cache |
| CloudFront | Networking | CDN — edge caching for S3 / API |
| Route 53 | Networking | DNS + health checks |
| ALB / NLB | Networking | Load balancers |
| API Gateway | Networking | HTTP/WebSocket API front door |
| VPC | Networking | Isolated private network |
| IAM | Security | Identity, roles, permissions |
| KMS | Security | Encryption key management |
| Secrets Manager | Security | Rotate & store secrets |
| SSM | Security | Parameter store, run commands on EC2 |
| CloudFormation | IaC | Deploy all AWS resources via YAML |
| CDK | IaC | CloudFormation via TypeScript/Python |
| CloudWatch | Observability | Logs, metrics, alarms, dashboards |
| X-Ray | Observability | Distributed tracing |
| SQS | Messaging | Queue — decoupled async messaging |
| SNS | Messaging | Pub/sub — fan-out notifications |
| EventBridge | Messaging | Event bus — route events between services |
| SES | Send transactional email at scale | |
| Cognito | Auth | Managed user pools / social login |
| CodePipeline | CI/CD | Automate build → test → deploy |
Common architecture patterns
Static website with CDN
Route 53 → CloudFront → S3 (static files)
→ API Gateway → Lambda → DynamoDB
# Deploy Next.js static export to S3 + CloudFront
next build && next export
aws s3 sync ./out s3://my-site-bucket --delete
aws cloudfront create-invalidation \
--distribution-id E1XXXXX \
--paths "/*"
Three-tier web application
Internet → ALB → EC2 Auto Scaling Group (App)
↓
RDS (Multi-AZ) in private subnets
↓
ElastiCache (Redis) for sessions
Serverless API
Client → API Gateway → Lambda → DynamoDB
→ Lambda → RDS Proxy → Aurora Serverless
Event-driven microservices
Service A → SQS → Lambda → DynamoDB
→ SNS → SQS (B)
→ SQS (C)
→ Email (SES)
AWS regions and availability zones
# List all regions
aws ec2 describe-regions --query 'Regions[*].RegionName' --output table
# List AZs in current region
aws ec2 describe-availability-zones \
--query 'AvailabilityZones[*].[ZoneName,State]' \
--output table
Most popular regions:
| Region code | Location |
|---|---|
us-east-1 |
N. Virginia (cheapest, most services) |
us-west-2 |
Oregon |
eu-west-1 |
Ireland |
eu-central-1 |
Frankfurt |
ap-southeast-1 |
Singapore |
ap-northeast-1 |
Tokyo |
Cost-saving tips
| Tip | Savings |
|---|---|
| Use Spot Instances for batch workloads | Up to 90% vs On-Demand |
| Stop dev EC2/RDS instances overnight | ~65% (8h off / 24h) |
| Use S3 Intelligent-Tiering for infrequent files | 40–68% on storage |
| Use Lambda instead of always-on EC2 for low traffic | Pay per invocation |
| Set S3 lifecycle rules — move old data to Glacier | 80% cheaper storage |
| Use Reserved Instances for steady 24/7 workloads | Up to 72% discount |
| Enable AWS Budgets alerts | Avoid surprise bills |
# Set up billing alarm ($50 threshold)
aws cloudwatch put-metric-alarm \
--alarm-name billing-alarm-50 \
--metric-name EstimatedCharges \
--namespace AWS/Billing \
--period 86400 \
--evaluation-periods 1 \
--threshold 50 \
--comparison-operator GreaterThanThreshold \
--statistic Maximum \
--alarm-actions arn:aws:sns:us-east-1:123:billing-alerts \
--dimensions Name=Currency,Value=USD \
--region us-east-1
Common mistakes
| Mistake | Fix |
|---|---|
| Storing secrets in env vars or code | Use SSM Parameter Store or Secrets Manager |
| Using root account for daily work | Create IAM users with least-privilege roles |
| Public S3 bucket for sensitive data | Block Public Access + use presigned URLs |
| Single AZ deployment | Use Multi-AZ RDS + Auto Scaling across 2+ AZs |
| No CloudWatch alarms | Set CPU, error rate, and billing alarms |
| Hardcoded region in code | Use AWS_DEFAULT_REGION env var or SDK defaults |
Forgetting to set --region |
Add AWS_DEFAULT_REGION to your shell profile |
aws s3 rm without testing |
List first: aws s3 ls s3://bucket/prefix/ |
FAQ
Q: What's the difference between IAM roles and IAM users?
Users are for humans (long-lived credentials). Roles are for services and applications (temporary credentials via STS). An EC2 instance, Lambda function, or ECS task assumes a role — never embed user credentials in code. Always prefer roles over users for programmatic access.
Q: How do I avoid AWS billing surprises?
Enable billing alerts in CloudWatch (see example above), set an AWS Budget in the console, use the Cost Explorer, and tag all resources with a project/environment tag. Also: always terminate test resources — stop is not enough, a stopped EC2 still charges for the EBS volume.
Q: EC2 vs Lambda vs Fargate — when to use which?
Use Lambda for event-driven, short-lived tasks (API routes, processing triggers) — pay per millisecond, zero idle cost. Use Fargate for long-running containers where you need persistent processes, WebSockets, or tasks over 15 minutes. Use EC2 when you need full OS control, custom GPU instances, or steady heavy traffic where Reserved Instances save money.
Q: What's the difference between SQS and SNS?
SQS is a queue — one consumer reads and deletes each message (point-to-point). SNS is pub/sub — one message fans out to multiple subscribers (email, SQS, Lambda, HTTP). For fan-out to multiple services, publish to SNS and subscribe multiple SQS queues to it.
Q: How do I run commands on EC2 without SSH?
Use AWS Systems Manager Session Manager — no inbound ports needed, no SSH key required:
aws ssm start-session --target i-0abc123
Or use EC2 Instance Connect for browser-based terminal access from the console.
Q: What's the fastest way to look up an AWS CLI command?
Use aws <service> help (e.g., aws s3 help, aws lambda help) or add --help to any command. The AWS CLI Command Reference has every command, and the --query flag with JMESPath is essential for filtering JSON output.