Skip to main content

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.

Published By Li Lei
#aws-cli #devops #cheatsheet #cloud #sre

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