AWS CLI Commands Cheat Sheet
#The AWS CLI Is Your Incident Response Console, Not Just a Provisioning Tool#link
In July 2019, a misconfigured web application firewall exposed an IAM role that could list and read from S3 buckets. The attacker did not need a fancy exploit framework; they used the AWS CLI. Within hours, they enumerated buckets, found a bucket with overly permissive policies, and exfiltrated over 100 million records. The Capital One breach is a reminder that the same command-line interface you use to spin up infrastructure is also the fastest way to understand what an attacker did and to lock things down before they do it again.
TL;DR: This cheat sheet covers the AWS CLI commands that matter for security work: IAM policy simulation, S3 public access blocks, EC2 IMDSv2 enforcement, CloudTrail event lookup, and KMS key rotation. Every command is paired with a real example and a note on where it commonly goes wrong.
IAM: Who Can Do What, and How to Prove It
IAM is the control plane for everything else. The most underused security command in the AWS CLI is `simulate-principal-policy`. It answers the question, 'If this role tried to put an object in that bucket, would it succeed?' without actually granting the permission. That is invaluable during incident response, when you need to confirm whether a compromised role had the access it appears to have.
aws iam simulate-principal-policy \--policy-source-arn arn:aws:iam::123456789012:role/MyRole \--action-names s3:PutObject \--resource-arns arn:aws:s3:::my-bucket/*
The `--resource-arns` parameter accepts a comma-separated list. If you omit it, the simulation checks whether the action is allowed on any resource, which can produce a misleading `allowed` result for actions that are only supposed to work on specific ARNs.
For a broader view, the credential report is the fastest way to see every IAM user, their access keys, and when those keys were last rotated. The report is base64-encoded, so you need to decode it before it is readable.
S3: The Bucket That Should Not Be Public
S3 misconfigurations remain the most common cause of cloud data leaks. The `put-public-access-block` command is the single most effective guardrail you can apply at the bucket level. It overrides both ACLs and bucket policies, and it takes effect immediately. The Capital One breach exploited an S3 bucket that was readable because of a permissive bucket policy; this command would have blocked that policy from being applied in the first place.
aws s3api put-public-access-block \--bucket my-sensitive-bucket \--public-access-block-configuration "BlockPublicAcls=true,IgnorePublicAcls=true,BlockPublicPolicy=true,RestrictPublicBuckets=true"
Applying a public access block does not remove an existing public bucket policy; it prevents new public policies from being applied and ignores existing ones. If a bucket is already public, you still need to delete the offending policy statement or ACL. Run `aws s3api get-bucket-policy --bucket my-bucket` to inspect the current policy before you assume the block is enough.
For temporary sharing, presigned URLs are safer than making an object public. They expire, and they do not change the bucket policy. The default expiration is one hour, but you can set it up to seven days.
aws s3 presign s3://my-bucket/report.pdf --expires-in 3600
EC2: Metadata, Security Groups, and the IMDSv2 Mandate
The Capital One attacker used SSRF to reach the EC2 instance metadata service and retrieve IAM role credentials. That attack relied on IMDSv1, which accepts a simple GET request. IMDSv2 requires a session token obtained via a PUT request, which SSRF cannot easily perform. Enforcing IMDSv2 is one of the highest-leverage defensive changes you can make.
aws ec2 modify-instance-metadata-options \--instance-id i-1234567890abcdef0 \--http-tokens required \--http-endpoint enabled
You can enforce IMDSv2 at the account level using `aws ec2 modify-instance-metadata-defaults` in regions that support it. That prevents new instances from launching with IMDSv1, which is easier than remediating every instance after the fact.
Security groups are the other half of EC2 network security. The `describe-security-groups` command is verbose, but `--query` makes it usable. The following command lists every security group that allows SSH from anywhere on the internet.
aws ec2 describe-security-groups \--query 'SecurityGroups[?IpPermissions[?FromPort==`22` && IpRanges[?CidrIp==`0.0.0.0/0`]]].[GroupId,GroupName]' \--output table
| Output Format | Best For | Gotcha |
|---|---|---|
| json | Scripting and automation | Verbose; pipe to jq for filtering |
| yaml | Human-readable inspection | Not all AWS CLI versions support it |
| text | Piping to shell tools like cut | No structure; easy to misparse |
| table | Quick visual scanning in a terminal | Truncates long values; not for scripts |
CloudTrail: The Audit Log You Actually Read
CloudTrail records every API call, but the console is slow for investigation. The `lookup-events` command lets you filter by event name, user, or time range directly from the CLI. This is how you answer, 'Who deleted that S3 bucket policy last Tuesday at 3 a.m.?'
aws cloudtrail lookup-events \--lookup-attributes AttributeKey=EventName,AttributeValue=PutBucketPolicy \--start-time 2026-09-08T00:00:00Z \--end-time 2026-09-09T00:00:00Z \--max-results 20
Log file validation is the quiet feature that makes CloudTrail logs tamper-evident. Without it, an attacker with sufficient permissions could delete or modify log files without detection. Enable it once, and CloudTrail will include digest files that let you verify integrity later.
aws cloudtrail update-trail \--name my-trail \--enable-log-file-validation
KMS and Secrets Manager: Encryption and Rotation
KMS key rotation is not enabled by default for customer-managed keys. Turning it on is a single command, and it limits the blast radius if a key is ever compromised. The rotation happens annually, and old key material is retained so that previously encrypted data remains decryptable.
aws kms enable-key-rotation --key-id 1234abcd-12ab-34cd-56ef-1234567890ab
Scheduling a KMS key for deletion is irreversible after the waiting period (7 to 30 days, depending on the key). The command `aws kms schedule-key-deletion` will prompt for confirmation, but in scripts the `--no-cli-pager` flag can suppress that prompt. Never run it without a deliberate, documented reason.
Secrets Manager rotation is more involved, but the CLI gives you a way to inspect and trigger it. The `rotate-secret` command requires a Lambda function ARN, so this is not a one-liner. Still, checking the rotation status is a quick way to find secrets that have not been rotated in months.
aws secretsmanager describe-secret --secret-id prod/db/password --query 'RotationEnabled'
Common Mistakes That Break Scripts and Security
The AWS CLI defaults are designed for interactive use, not for automation. Two defaults cause the most trouble: pagination and output format. By default, commands like `aws s3api list-buckets` return only the first page of results. In a script, that means you might miss buckets, users, or log entries. The `--no-paginate` flag forces the CLI to retrieve all pages, but it also increases the risk of hitting API rate limits. For large accounts, you should paginate deliberately with `--max-items` and `--starting-token` rather than relying on `--no-paginate`.
Region misconfiguration is the other silent failure. If you do not set `AWS_DEFAULT_REGION` or pass `--region`, the CLI uses the region from your profile, which may not be where your resources live. A `describe-instances` call in the wrong region returns an empty list, which looks like 'no instances' rather than 'wrong region.' Always verify with `aws sts get-caller-identity` and `aws configure get region` before drawing conclusions from an empty result.
- ▪Set `AWS_DEFAULT_REGION` in your environment or pass `--region` explicitly in every script.
- ▪Use `--dry-run` on any command that modifies or deletes resources before running it for real.
- ▪Run `aws sts get-caller-identity` at the start of an investigation to confirm which principal you are actually using.
- ▪Pipe JSON output through `jq` rather than parsing it with `grep` or `cut`; the structure is not guaranteed to be stable across services.
- ▪Enable MFA for any IAM user that can call `iam:*` or `organizations:*`; the CLI will prompt for the token when the session requires it.
| Symptom | Likely Cause | Fix |
|---|---|---|
| Empty result from describe-instances | Wrong region in profile | Pass `--region us-east-1` or set `AWS_DEFAULT_REGION` |
| `AccessDenied` on a command you expect to work | Missing IAM permission or SCP restriction | Run `aws iam simulate-principal-policy` to confirm |
| Command hangs with no output | CLI pager waiting for input | Add `--no-cli-pager` or set `AWS_PAGER` to an empty string |
| Only 100 results returned | Default pagination limit | Use `--no-paginate` or iterate with `--starting-token` |
The AWS CLI is a double-edged sword: every command an attacker uses to enumerate your environment is a command you can use to detect and stop them. The difference is whether you already know the syntax when the alert fires at 2 a.m. Keep this cheat sheet close, and practice the simulate and describe commands before you need them in an incident.