# AWS CLI Cheat Sheet — Storage: S3, EBS & EFS

> **Tool:** AWS CLI v2
> **Category:** Cloud CLIs
> **Verified against:** aws-cli/2.33.6, flags verified via `aws <cmd> help`, 2026-08-21
> **Official docs:** https://docs.aws.amazon.com/cli/

S3 object and bucket operations, EBS volume/snapshot management for EC2, and EFS shared file systems.

## S3 — copying, syncing, and listing objects

```bash
aws s3 cp file.txt s3://my-bucket/path/file.txt
aws s3 cp s3://my-bucket/path/file.txt ./file.txt
aws s3 sync ./local-dir s3://my-bucket/path/ --delete    # mirror local -> bucket, remove extras in the destination
aws s3 sync s3://my-bucket/path/ ./local-dir
aws s3 ls s3://my-bucket/path/ --recursive --human-readable --summarize
```

The high-level `aws s3` commands (`cp`, `sync`, `ls`, `rm`, `mb`, `rb`) are convenience wrappers over the S3 API — they handle multipart upload/download automatically. `--dryrun` on `cp`/`sync`/`rm` previews what would change without doing it.

## S3 — removing objects and buckets

```bash
aws s3 rm s3://my-bucket/path/file.txt
aws s3 rm s3://my-bucket/path/ --recursive               # delete everything under a prefix
aws s3 rb s3://my-bucket --force                          # delete a bucket, --force empties it first
```

## S3 — bucket configuration (s3api)

```bash
aws s3api create-bucket --bucket my-new-bucket --create-bucket-configuration LocationConstraint=us-west-2
aws s3api put-bucket-versioning --bucket my-bucket --versioning-configuration Status=Enabled
aws s3api put-bucket-policy --bucket my-bucket --policy file://bucket-policy.json
aws s3api head-object --bucket my-bucket --key path/file.txt   # metadata only, no download
```

Anything below the `aws s3` convenience layer — bucket policies, versioning, lifecycle rules, ACLs, encryption config — lives under `aws s3api`, which maps 1:1 to the raw S3 API and needs `--bucket`/`--key` explicitly rather than an `s3://` URI.

## S3 — listing with s3api for scripting

```bash
aws s3api list-objects-v2 --bucket my-bucket --prefix path/ --query 'Contents[].Key' --output text
```

`s3api list-objects-v2` is the scriptable equivalent of `aws s3 ls` — prefer it when you need `--query` filtering or pagination control that the high-level `ls` doesn't expose.

## S3 — versioning and listing versions

```bash
aws s3api get-bucket-versioning --bucket my-bucket
aws s3api list-object-versions --bucket my-bucket --prefix path/file.txt
aws s3api get-object --bucket my-bucket --key path/file.txt --version-id <version-id> downloaded-old-version.txt
```

Once versioning is enabled on a bucket it **cannot be disabled**, only suspended (`Status=Suspended`) — plan for the storage cost before turning it on. A "deleted" object in a versioned bucket isn't gone; `list-object-versions` shows it with a delete-marker entry, and it's recoverable by fetching a specific `--version-id`.

## S3 — lifecycle rules

```bash
aws s3api put-bucket-lifecycle-configuration --bucket my-bucket --lifecycle-configuration file://lifecycle.json
aws s3api get-bucket-lifecycle-configuration --bucket my-bucket
```

A minimal `lifecycle.json` transitioning old objects to cheaper storage and expiring them later:

```json
{
  "Rules": [
    {
      "ID": "archive-and-expire",
      "Filter": { "Prefix": "logs/" },
      "Status": "Enabled",
      "Transitions": [{ "Days": 30, "StorageClass": "GLACIER" }],
      "Expiration": { "Days": 365 }
    }
  ]
}
```

## S3 — cross-region/same-region replication

```bash
aws s3api put-bucket-replication --bucket my-bucket --replication-configuration file://replication.json
```

Replication requires versioning enabled on **both** the source and destination buckets, and an IAM role (specified inside `replication.json`'s `Role` field) with permission to read the source and write the destination — it silently does nothing for objects that existed before the rule was created (it's forward-only, not a one-time backfill).

## S3 — presigned URLs

```bash
aws s3 presign s3://my-bucket/path/file.txt --expires-in 3600
```

A presigned URL grants temporary access (default 1 hour, max 7 days for IAM-user/role credentials) to a single object using the credentials of whoever ran the command — anyone holding the URL can perform that action without their own AWS credentials, so treat the URL itself as a secret. It only works for actions the signing principal is actually allowed to perform (`GetObject` by default).

## EBS — creating and attaching volumes

```bash
aws ec2 create-volume --availability-zone us-east-1a --size 100 --volume-type gp3
aws ec2 attach-volume --volume-id vol-0123456789abcdef0 --instance-id i-0123456789abcdef0 --device /dev/xvdf
aws ec2 describe-volumes --volume-ids vol-0123456789abcdef0
aws ec2 delete-volume --volume-id vol-0123456789abcdef0
```

A volume must be in the *same Availability Zone* as the instance it attaches to — a common source of "InvalidVolume.ZoneMismatch" errors when scripting instance + volume creation together.

## EBS — snapshots

```bash
aws ec2 create-snapshot --volume-id vol-0123456789abcdef0 --description "pre-migration backup"
aws ec2 describe-snapshots --owner-ids self
aws ec2 describe-snapshots --filters "Name=volume-id,Values=vol-0123456789abcdef0"
```

## EFS — file systems and mount targets

```bash
aws efs create-file-system --performance-mode generalPurpose --throughput-mode bursting --encrypted
aws efs describe-file-systems
aws efs create-mount-target --file-system-id fs-0123456789abcdef0 --subnet-id subnet-0123456789abcdef0 --security-groups sg-0123456789abcdef0
aws efs describe-mount-targets --file-system-id fs-0123456789abcdef0
```

EFS needs one mount target per Availability Zone you want to mount from — an EC2 instance in a subnet with no mount target for that file system will time out trying to mount it, not fail with a clear error.
