AWS CLI Cheat Sheet: The Commands You Actually Run on S3, EC2, and IAM
A practical AWS CLI cheat sheet for S3, EC2, and IAM — profiles, regions, the --query JMESPath flag, output formats, and a copy-ready quick reference.
AWS CLI Cheat Sheet: The Commands You Actually Run on S3, EC2, and IAM
The official aws help lists roughly 14,000 subcommands. On a real account you use maybe eighty of them, and the same dozen every single day. This post is a working cheat sheet for that dozen — the S3, EC2, and IAM commands that show up in real runbooks — plus the three flags that decide whether the output is usable: --profile, --region, and --query. If you want the full searchable list with copy buttons, keep the AWS CLI cheat sheet open in another tab while you read.
Profiles and regions: the two flags that bite first
Every wrong-account incident I have seen started the same way: someone ran a write command without --profile, hit the default profile, and created a security group in staging while thinking they were in prod. On any org with more than one account, treat --profile as mandatory on every write.
# Confirm where you are BEFORE anything destructive
aws sts get-caller-identity --profile prod
# {"Account": "417900000000", "Arn": "arn:aws:iam::417900000000:user/lilei"}
# Pin both the profile and the region explicitly
aws --profile dev --region us-east-1 ec2 describe-instances
The --region flag matters because, when it is unset, the CLI falls back to AWS_DEFAULT_REGION — which is often empty and gives you a confusing "Could not connect to the endpoint URL" error instead of a clear message. Set it per command, or export AWS_PROFILE and AWS_DEFAULT_REGION once for the whole shell session:
export AWS_PROFILE=staging
export AWS_DEFAULT_REGION=ap-southeast-1
If you are on IAM Identity Center, aws configure sso walks you through the named profiles and aws sso login --profile <name> refreshes the token automatically. Either way, aws sts get-caller-identity is the thirty-second check that stops a misfire.
S3: cp, sync, and the one that wipes the bucket
S3 is where you spend most of your CLI time, and where the worst footgun lives. cp copies a single file or, with --recursive, a prefix. sync does an rsync-style diff and only transfers what changed.
# Upload one artifact
aws s3 cp build/app.tar.gz s3://acme-artifacts/releases/app-1.4.2.tar.gz
# Deploy a static site, removing files that no longer exist locally
aws s3 sync ./dist s3://acme-www --delete
# Generate a 1-hour signed link for a private object
aws s3 presign s3://acme-private/report.pdf --expires-in 3600
That --delete flag is exactly right for a site deploy and catastrophic if you fumble the source path. aws s3 sync . s3://acme-www --delete from the wrong directory will, on first run, delete everything in the bucket that is not in your current folder. Always pin the source to a real subfolder and add --dryrun the first time you run a sync with --delete:
aws s3 sync ./dist s3://acme-www --delete --dryrun
When the high-level s3 commands cannot express what you need — say you want object metadata or a specific storage class transition — drop down to s3api, which maps directly to the underlying API.
Filtering output with --query: a worked example
This is the flag that turns the CLI from a JSON firehose into a useful tool, and the one people misuse most. --query is JMESPath, not jq. The leading dot is gone: .Reservations[0] becomes Reservations[0], and the pipe is JMESPath's own |, not a shell pipe into jq.
Here is a real scenario. A deploy script needs the instance IDs and private IPs of every running web-tier box, as a clean tab-separated list a shell loop can read. Run describe-instances raw and you get hundreds of lines of nested JSON. Add --query and --output text and you get exactly the two columns you want:
aws ec2 describe-instances \
--filters "Name=tag:Role,Values=web" "Name=instance-state-name,Values=running" \
--query 'Reservations[].Instances[].[InstanceId, PrivateIpAddress]' \
--output text \
--profile prod --region us-east-1
i-0a1b2c3d4e5f6a7b8 10.0.3.41
i-09f8e7d6c5b4a3210 10.0.3.88
Now a loop can consume it directly:
aws ec2 describe-instances \
--filters "Name=tag:Role,Values=web" \
--query 'Reservations[].Instances[].InstanceId' --output text \
| tr '\t' '\n' | while read id; do
echo "Draining $id"
done
The rule I follow: use --output json when something downstream parses JSON, and --output text --query whenever a shell loop consumes the result. The text output is tab-separated and stable, which is far safer than slicing JSON with cut or awk. The default --output table is for human eyes only — it truncates wide columns to fit your terminal, so never script against it.
IAM: who can do what
IAM commands are read-heavy: most of the time you are auditing, not creating. The pattern is list, inspect, then simulate before you grant.
# Who exists and what is attached
aws iam list-users --query 'Users[].UserName' --output text
aws iam list-attached-user-policies --user-name lilei
# What roles can be assumed, and by whom
aws iam list-roles --query 'Roles[?contains(RoleName, `deploy`)].RoleName' --output text
# Will this principal actually be allowed to do the thing?
aws iam simulate-principal-policy \
--policy-source-arn arn:aws:iam::417900000000:role/ci-deploy \
--action-names s3:PutObject \
--resource-arns arn:aws:s3:::acme-www/* \
--query 'EvaluationResults[].EvalDecision' --output text
For cross-account work, aws sts assume-role --role-arn ... --role-session-name ... returns temporary credentials you export into the shell — or define source_profile and role_arn in ~/.aws/config so the CLI assumes the role transparently and you never copy keys by hand. If an access key ever lands in a git commit, rotate it immediately; the CLI makes credentials easy to leak and AWS will not stop you.
A quick reference to copy
# Identity / context
aws sts get-caller-identity --profile prod
aws configure list
# S3
aws s3 ls s3://acme-www/
aws s3 cp file.txt s3://acme-www/file.txt
aws s3 sync ./dist s3://acme-www --delete --dryrun
aws s3 presign s3://acme-private/key --expires-in 3600
# EC2
aws ec2 describe-instances --query 'Reservations[].Instances[].InstanceId' --output text
aws ec2 start-instances --instance-ids i-0abc --profile prod
aws ec2 stop-instances --instance-ids i-0abc --profile prod
# IAM
aws iam list-users --query 'Users[].UserName' --output text
aws iam attach-user-policy --user-name dev --policy-arn arn:aws:iam::aws:policy/ReadOnlyAccess
Bookmark the full AWS CLI cheat sheet for the searchable EC2/S3/IAM/Lambda/RDS/EKS list with every command's syntax, its real-world trap, and copy-ready examples. If your day also lives in clusters, the kubectl cheat sheet pairs with it for the post-update-kubeconfig half of the workflow. Everything runs in your browser — no account connected, no credentials touched.
Made by Toolora · Updated 2026-06-13