---
title: "AWS CLI"
description: "An expert reference for the AWS CLI covering configuration precedence, EC2 lifecycle control, recursive S3 operations, JMESPath querying, output formatting, and secure SSM sessions."
author: "Mohammad Abu Mattar"
canonical: https://mkabumattar.com/cheatsheets/aws-cli
---

# AWS CLI

The AWS Command Line Interface (CLI) lets you orchestrate infrastructure, move cloud data, and configure security entirely from the terminal. This reference covers configuring identity profiles, managing EC2 instances, synchronizing S3 buckets, creating IAM roles, querying nested JSON with JMESPath, formatting output, and securing access with Systems Manager.

Browse the sections below to explore command syntax, operational insights, and advanced configuration mechanics.

## Configuration & Profiles

Set up credentials, manage named profiles, and understand how the CLI resolves settings so automation runs against the account you expect.

### Initial Setup & Profiles

Configuring the CLI sets the default security context and region for every API call, resolved through a strict precedence hierarchy.

**Keywords:** AWS CLI, profile, automation, configuration

#### Interactive configuration of the default profile

```bash
# Prompt for keys, region, and output format, then save them locally
aws configure
```

_input_
```text
AWS Access Key ID [None]: AKIAIOSFODNN7EXAMPLE
AWS Secret Access Key [None]: wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
Default region name [None]: us-east-1
Default output format [None]: json
```

Prompts for standard access credentials and defaults, then persists them to the ~/.aws directory.

- Writes two files: ~/.aws/credentials (access keys) and ~/.aws/config (region and output format) on Unix-like systems.
- Commands run without a profile fall back to this default configuration block.

#### Configuring a named profile for isolated environments

```bash
# Create an independent credential set under a profile name
aws configure --profile production-admin
```

_input_
```text
AWS Access Key ID [None]: AKIAI44QH8DHBEXAMPLE
AWS Secret Access Key [None]: je7MtGbClwBF/2Zp9Utk/h3yCoEXAMPLEKEY
Default region name [None]: us-west-2
Default output format [None]: yaml
```

Establishes a separate credential set tied to a profile name, which makes safe multi-account work possible.

- Append --profile production-admin to a command to use this credential block explicitly.
- Or switch the active shell context with export AWS_PROFILE=production-admin.

#### Verify the active configuration and its sources

```bash
# Show current values and where each one came from
aws configure list
```

_output_
```text
Name             Value             Type    Location
----             -----             ----    --------
profile             None    None
access_key     ****************7XX  shared-credentials-file
secret_key     ****************KEY  shared-credentials-file
region         us-east-1  config-file  ~/.aws/config
```

Exposes the active configuration values and, importantly, the origin type that won the precedence evaluation.

- Use this to audit which credentials will authorize a command before running destructive changes.

**Best practices:**

- Prefer aws sso login --profile <name> with IAM Identity Center for temporary credentials over static IAM user access keys.
- Audit configurations routinely. An "Unable to locate credentials" error in CloudShell often means an expired console session, not missing local files.

**Common errors:**

- **The security token is invalid or an ExpiredToken exception occurs during execution.**: Temporary credentials (SSO or assumed roles) have expired. Refresh them via your SSO provider, or update AWS_SESSION_TOKEN alongside the access keys.

**Advanced notes:**

- **Configuration precedence hierarchy:** The CLI resolves credentials and parameters in a strict order, which matters in CI/CD:
1. Command line options (--region, --profile) beat everything else.
2. Environment variables like AWS_DEFAULT_REGION override local files.
3. Assume-role credentials sourced via CLI assume-role processes.
4. Shared credentials file, usually ~/.aws/credentials.
5. Shared config file, usually ~/.aws/config.
6. Container credentials, fetched when running inside Amazon ECS tasks.
7. Instance profile credentials from the EC2 metadata endpoint.
- **Deep diagnostics with the debug flag:** Appending --debug prints the full execution lifecycle: where the CLI searches for credentials, the botocore calls it builds, the HTTP payloads sent to AWS, and the raw JSON responses.

### System Environment Variables

Inject authentication and default behavior at runtime without writing config files to disk, which suits stateless containers and pipelines.

**Keywords:** AWS CLI, automation, environment variables

#### Injecting credentials via shell session variables

```bash
# Set credentials and defaults for the current terminal session
export AWS_ACCESS_KEY_ID="AKIAIOSFODNN7EXAMPLE"
export AWS_SECRET_ACCESS_KEY="wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"
export AWS_DEFAULT_REGION="us-east-1"
export AWS_DEFAULT_OUTPUT="table"
```

Assigns security and formatting variables to the environment scope, overriding ~/.aws/credentials for the life of the terminal.

- Run unset AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY AWS_DEFAULT_REGION AWS_DEFAULT_OUTPUT to revoke them and fall back to the config files.

**Best practices:**

- When automating on AWS, prefer an IAM role attached to the execution environment (like an EC2 instance profile) over exporting static user credentials.

**Common errors:**

- **SignatureDoesNotMatch or InvalidClientTokenId exceptions.**: The exported AWS_SECRET_ACCESS_KEY is usually truncated or has trailing spaces, or temporary credentials are being used without AWS_SESSION_TOKEN.

**Advanced notes:**

- **Integrating with temporary assumed roles:** With an STS assumed role, static keys are not enough. You must also export AWS_SESSION_TOKEN="<long-token-string>". Without it the API rejects the request as unauthenticated.

## EC2 Instance Control & Filtering

Manage compute lifecycle safely and filter metadata on the server side to cut bandwidth and avoid client-side timeouts.

### Lifecycle Management & Dry-Runs

Move instances between running, stopped, and terminated states, with dry-run checks so bad permissions or parameters fail before they cause downtime.

**Keywords:** AWS CLI, EC2, automation, lifecycle

#### Validate authorization with the dry-run flag

```bash
# Check IAM permissions and syntax without changing state
aws ec2 stop-instances --instance-ids i-0123456789abcdef0 --dry-run
```

_output_
```text
An error occurred (DryRunOperation) when calling the StopInstances operation: Request would have succeeded, but DryRun flag is set.
```

Asks the EC2 API to validate IAM privileges and request syntax without modifying the instance.

- Ideal for validating automation without risking production downtime.
- A principal without the permission gets UnauthorizedOperation instead of DryRunOperation.

#### Start and stop instances

```bash
# Start a stopped EC2 instance
aws ec2 start-instances --instance-ids i-0123456789abcdef0

# Stop a running EC2 instance
aws ec2 stop-instances --instance-ids i-0123456789abcdef0
```

Sends state-change signals to transition instances into running or stopped modes.

- The response shows both PreviousState and CurrentState for each instance.

#### Terminate an instance permanently

```bash
# Permanently destroy the instance (irreversible)
aws ec2 terminate-instances --instance-ids i-0123456789abcdef0
```

Sends a decommission signal that permanently shuts down and destroys the target node.

- Final and destructive. Attached EBS volumes with DeleteOnTermination set to true are deleted with the host.

**Best practices:**

- Put --dry-run checks in CI/CD test phases to confirm a newly attached IAM role has the privileges it needs before deployment steps run.

**Common errors:**

- **An error occurred (OperationNotPermitted) when calling the TerminateInstances operation.**: Termination protection is enabled. Disable it with aws ec2 modify-instance-attribute --instance-id i-0123456789abcdef0 --no-disable-api-termination.

**Advanced notes:**

- **The dry-run exception pattern:** A DryRunOperation error is the intended successful result of a dry-run, not a failure. Automation should trap that specific code and treat it as a green light to run the live command.

### Server-Side Metadata Filters

Push filtering to the AWS API so only matching resources come back, avoiding client-side memory bloat and pagination throttling.

**Keywords:** AWS CLI, EC2, --filters, metadata

#### Retrieve only running instances

```bash
# Return metadata only for instances in the 'running' state
aws ec2 describe-instances --filters "Name=instance-state-name,Values=running"
```

Requests only instances matching the running state, discarding stopped, pending, and terminated nodes before the payload leaves AWS.

- This dramatically reduces the JSON payload size.
- Filters require the exact Name=attribute-name,Values=value string format.

#### Compound filtering with tags and VPC constraints

```bash
# Both conditions must match (implicit AND)
aws ec2 describe-instances \
  --filters "Name=vpc-id,Values=vpc-0123456789abcdef0" "Name=tag:Environment,Values=production"
```

Chains filters; the API treats multiple filters as a logical AND, so every condition must be true.

- [object Object]

**Best practices:**

- Use a two-tier approach - server-side --filters to limit the data sent over the network, then a client-side --query to shape the final output.

**Common errors:**

- **The filter 'status' is invalid.**: Filter properties are case-sensitive and must match the EC2 API schema exactly, like instance-state-name rather than a shorthand.

**Advanced notes:**

- **Wildcards in filter values:** The Values parameter supports wildcards. Use * to match zero or more characters and ? to match a single character. Values='prod-*' captures prod-web, prod-database, and anything else with that prefix.

## S3 Storage Management

Move, sync, and audit objects with high-level commands, and understand recursion, filter order, and access control before running destructive operations.

### Recursive Sync, Copy & Move Operations

cp, sync, and mv abstract multipart uploads and can traverse nested directories, with state-checking to transfer only what changed.

**Keywords:** AWS CLI, S3, automation, sync

#### Upload objects with recursive directory traversal

```bash
# Upload a single local file
aws s3 cp local-archive.tar.gz s3://my-assets-bucket/backups/

# Recursively upload an entire directory tree
aws s3 cp ./local-dir s3://my-assets-bucket/backups/ --recursive
```

Walks the local directory and uploads every discovered file into the target S3 prefix in parallel.

- --recursive works the same for local-to-S3, S3-to-local, and S3-to-S3 copies.

#### Idempotent sync with strict deletion

```bash
# Upload only changed files; delete remote files missing locally
aws s3 sync ./dist/ s3://my-static-web-bucket/ --delete
```

Compares sizes and timestamps, transfers only deltas, and removes remote objects no longer present locally.

- sync is content-aware and recursive by default.
- --delete is destructive - it purges orphaned objects. Great for static sites, dangerous for shared buckets.

#### Move objects while stacking exclusion filters

```bash
# Move only .csv files, then delete them from the source
aws s3 mv s3://my-source-bucket/logs/ s3://my-destination-bucket/archive-logs/ \
  --recursive \
  --exclude "*" \
  --include "*.csv"
```

Recursively moves files matching an extension to a new bucket, deleting them from the source on success.

- Filters evaluate left to right. To target only .csv, exclude everything first (*), then re-include *.csv.

**Best practices:**

- Test destructive recursive operations with --dryrun. It models what will be overwritten or deleted without making any API changes.
- For large datasets, prefer aws s3 sync over recursive aws s3 cp. sync computes deltas; cp overwrites every destination object regardless of changes.

**Common errors:**

- **An error occurred (AccessDenied) when calling the PutObject operation.**: Confirm the IAM profile has s3:PutObject. Also check for explicit DENY bucket policies or restrictive VPC endpoint policies, which always beat IAM allow rules.

**Advanced notes:**

- **How S3 filter patterns are evaluated:** Patterns evaluate against the source directory with the path prepended. For aws s3 cp /tmp/foo s3://bucket/ --recursive --exclude ".git/*", files evaluate as /tmp/foo/.git/config, so patterns must account for the full path or they may not match nested files.
- **Direct storage class assignment:** Set the destination storage class during a transfer with --storage-class (STANDARD_IA, GLACIER, INTELLIGENT_TIERING) instead of waiting on lifecycle policies.

### Data Analytics, Deletion & Secure Sharing

Audit storage footprints, purge large prefixes, and share private objects with third parties without provisioning IAM users.

**Keywords:** AWS CLI, S3, presign

#### Recursive audit of storage consumption

```bash
# Total object count and size under a prefix, in human units
aws s3 ls s3://my-assets-bucket/images/ --recursive --human-readable --summarize
```

_output_
```text
2026-03-25 10:15:32    2.4 MiB  images/logo.png
2026-03-25 11:42:01   15.1 MiB  images/hero.png

Total Objects: 2
Total Size: 17.5 MiB
```

Crawls the prefix tree, formats byte counts into readable units, and summarizes total storage used.

- Handy for spotting storage anomalies from the terminal without touching CloudWatch.

#### Purge a nested prefix

```bash
# Recursively delete every object under the prefix
aws s3 rm s3://my-assets-bucket/temp-logs/ --recursive
```

Recursively deletes all objects beneath the key prefix.

- S3 folders are logical only. Deleting all keys sharing a prefix effectively removes the folder.

#### Mint a temporary presigned URL

```bash
# Signed download link valid for 3600 seconds
aws s3 presign s3://my-assets-bucket/reports/financials.pdf --expires-in 3600
```

_output_
```text
https://my-assets-bucket.s3.amazonaws.com/reports/financials.pdf?AWSAccessKeyId=AKIA...&Signature=abcd...&Expires=1774431600
```

Generates a signed HTTP link that lets an unauthenticated user download a restricted object until the signature expires.

- The default expiry is 3600 seconds (1 hour).
- An IAM user can sign up to 604800 seconds (7 days). With STS temporary credentials, it cannot exceed the role's session duration.

**Best practices:**

- Keep presigned URL expiry to the minimum time needed for the transfer to reduce the risk of leaked links.

**Common errors:**

- **NoSuchBucket exception during list or delete operations.**: Check the bucket name spelling and confirm the CLI profile targets the region where the bucket actually lives.

**Advanced notes:**

- **Recursive rm vs S3 lifecycle policies:** aws s3 rm --recursive works, but on millions of objects it makes millions of individual HTTP requests, which is slow and expensive. For massive purges, use an S3 Lifecycle Expiration Policy so AWS deletes asynchronously at no API cost.

## Identity & Access Management (IAM)

Verify the executing principal, define trust and permission policies, and provision least-privilege roles from the terminal.

### Principal Auditing & Verification

Confirm which identity and account a command will run against before it executes, which matters in multi-account Organizations.

**Keywords:** AWS CLI, IAM, STS, security

#### Interrogate the active STS context

```bash
# The "whoami" of AWS - who am I and in which account?
aws sts get-caller-identity
```

_output_
```json
{
    "UserId": "AIDASODNN7EXAMPLE",
    "Account": "123456789012",
    "Arn": "arn:aws:iam::123456789012:user/developer-admin"
}
```

Validates the authentication tokens and returns the User ID, Account ID, and identity ARN.

- Think of it as whoami for AWS.

#### List all account users

```bash
# Enumerate IAM users with metadata and ARNs
aws iam list-users
```

Returns a JSON array of metadata, creation dates, and ARNs for every IAM user in the account.

- Useful for compliance audits and spotting unauthorized "ghost" users.

**Best practices:**

- Use JMESPath to enforce account checks in scripts - ACT_ID=$(aws sts get-caller-identity --query Account --output text).

**Common errors:**

- **An error occurred (AccessDenied) when calling get-caller-identity.**: This is an authentication failure, not a permissions boundary. Confirm the access keys are structurally valid and that traffic can reach STS endpoints.

**Advanced notes:**

- **Parsing assumed-role ARNs:** Under an assumed role or SSO federation, the ARN is not a standard IAM user. It reflects STS assumption: arn:aws:sts::123456789012:assumed-role/RoleName/SessionName.

### Role Delegation & Trust Policies

Roles decouple permissions from static credentials. Creating one splits into a trust policy (who can assume it) and a permissions policy (what it can do).

**Keywords:** AWS CLI, IAM, roles, trust-policy

#### Provision a service role from a JSON trust document

```bash
# 1. Save the trust policy to ec2-trust.json:
# {
#   "Version": "2012-10-17",
#   "Statement": [
#     {
#       "Effect": "Allow",
#       "Principal": { "Service": "ec2.amazonaws.com" },
#       "Action": "sts:AssumeRole"
#     }
#   ]
# }

# 2. Create the role from that document
aws iam create-role \
  --role-name ComputeS3ReadOnly \
  --assume-role-policy-document file://ec2-trust.json
```

Creates a role that delegates sts:AssumeRole to the EC2 service principal, letting instances wear the role.

- --assume-role-policy-document needs the file:// prefix to read the local JSON file.
- A trust policy is a resource-based policy attached to the role itself.

#### Attach a managed permissions policy to a role

```bash
# Grant the role read-only S3 access
aws iam attach-role-policy \
  --role-name ComputeS3ReadOnly \
  --policy-arn arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess
```

Attaches an AWS-managed read-only policy to the new role, defining its downstream privileges.

- This action completes instantly and returns no JSON body on success.

**Best practices:**

- Enforce least privilege. Tighten trust policies with a Condition block to limit sts:AssumeRole to specific org boundaries or source IPs.

**Common errors:**

- **MalformedPolicyDocument exception halts role creation.**: The JSON payload has a syntax error. Validate the file, or when passing strings inline, escape nested quotes with backslashes.

**Advanced notes:**

- **Local file path formats:** The file:// parameter is picky about paths. On Unix/Linux/macOS use file://path/to/policy.json. On Windows use drive notation: file://C:\path\to\policy.json.

## Data Querying with JMESPath

Filter and reshape JSON responses client-side with the built-in --query engine, no jq or awk required.

### Projections, Filtering & Analytics

Isolate nested keys, restructure dictionaries, run logical checks, and apply math functions against arrays.

**Keywords:** AWS CLI, JMESPath, --query, JSON

#### Extract a flat list of running instance IDs

```bash
# Filter to running nodes and return only their IDs, space-delimited
aws ec2 describe-instances \
  --query "Reservations[].Instances[?State.Name=='running'].InstanceId" \
  --output text
```

_output_
```text
i-0123456789abcdef0 i-0987654321fedcba0
```

Traverses the nested reservations array, filters to active nodes, and returns a flat space-delimited string of IDs.

- The empty projection [] flattens the nested arrays.
- String comparisons must use single quotes ('running').

#### Project metadata into custom JSON mappings

```bash
# Rename fields into a custom, readable shape
aws ec2 describe-instances \
  --query "Reservations[].Instances[].{NodeID:InstanceId,ComputeType:InstanceType,Zone:Placement.AvailabilityZone}"
```

_output_
```json
[
    {
        "NodeID": "i-0123456789abcdef0",
        "ComputeType": "t3.medium",
        "Zone": "us-east-1a"
    }
]
```

Builds new dictionaries by mapping native AWS keys to custom aliases.

- Multi-select hashes use comma-separated NewName:OldName pairs inside curly braces {}.

#### Apply numerical logic and array functions

```bash
# Volumes larger than 100 GB
aws ec2 describe-volumes \
  --query "Volumes[?Size > \`100\`].{ID:VolumeId,Size:Size}"

# Count of running instances
aws ec2 describe-instances \
  --query "length(Reservations[].Instances[?State.Name=='running'])"
```

Shows numerical operators and built-in functions to filter by size and calculate lengths.

- Numeric literals must be wrapped in backticks (for example, `100`) so they parse as integers, not strings.
- length() works on strings, arrays, and objects.

**Best practices:**

- Wrap the whole --query string in single quotes in bash so the shell does not expand brackets, braces, or asterisks.

**Common errors:**

- **Bad value for --query: Parse error at column...**: Usually a quote mismatch. On Unix wrap the outer query in single quotes and use double quotes or backticks inside. On Windows CMD the outer string often needs double quotes with deeper escaping.

**Advanced notes:**

- **JMESPath function arsenal:** JMESPath goes beyond filtering with built-in functions:
- length(x) - element count of an array or string. Example: length(Reservations[]).
- sort_by(x, &key) - sort an array by a key. Example: sort_by(Images, &CreationDate).
- starts_with(s, prefix) - boolean prefix test. Example: starts_with(Name, 'prod-').
- contains(x, val) - membership test on strings or arrays. Example: contains(Tags[].Key, 'Env').
- join(delim, x) - concatenate an array into one string. Example: join(',', InstanceIds).

## Serialization & Output Formats

Switch the output serializer at runtime to suit humans reading diagnostics or machines consuming automated streams.

### Formatting Types (table, text, json, yaml)

Render ASCII tables for reporting, strip syntax for scripting, keep JSON for payloads, or emit YAML for IaC audits.

**Keywords:** AWS CLI, --output, automation, formatting

#### Render metadata as an ASCII table

```bash
# Project fields, then format as a table
aws ec2 describe-instances \
  --query "Reservations[].Instances[].{ID:InstanceId,Zone:Placement.AvailabilityZone}" \
  --output table
```

_output_
```text
------------------------------------------------
|               DescribeInstances              |
+-----------------------+----------------------+
|  ID                   |  Zone                |
+-----------------------+----------------------+
|  i-0123456789abcdef0  |  us-east-1a          |
|  i-0987654321fedcba0  |  us-east-1a          |
+-----------------------+----------------------+
```

Interprets the projected dictionaries and aligns them into labeled columns.

- [object Object]

#### Emit YAML for GitOps-friendly output

```bash
# Dump structured output as readable YAML
aws sts get-caller-identity --output yaml
```

_output_
```yaml
Account: '123456789012'
Arn: arn:aws:iam::123456789012:user/admin-developer
UserId: AIDASODNN7EXAMPLE
```

Dumps nested response parameters into legible YAML.

- Handy for capturing current system state into GitOps repositories.

**Best practices:**

- Use --output text when capturing output into shell variables (ID=$(aws ec2 ...)). It strips quotes and brackets, saving you messy sed cleanup.

**Common errors:**

- **The table format shows blanks, fails to align, or prints 'None'.**: ASCII tables cannot render flat strings or deeply nested arrays. Project the data into a clean, uniform list of simple dictionaries first.

**Advanced notes:**

- **Output format changes pagination behavior:** Format silently affects pagination. With --output text, data is paginated before --query runs, so the query runs on each page. With --output json, the CLI pulls all pages, aggregates them, then runs --query once. This explains seemingly intermittent missing data.

## Systems Manager (SSM) Security

Replace SSH and bastion hosts with SSM Session Manager, routing encrypted shells and tunnels over the AWS backbone with no inbound ports.

### Interactive Shells & Secure Tunnels

The SSM agent makes outbound connections to the Systems Manager endpoint, so you never open inbound security group rules.

**Keywords:** AWS CLI, EC2, automation, Session Manager, SSM

#### Start an interactive shell without SSH

```bash
# Open a secure terminal on the instance, no SSH key needed
aws ssm start-session --target i-0123456789abcdef0
```

_output_
```text
Starting session with SessionId: developer-admin-0123456789abcdef0
sh-4.2$
```

Opens a secure real-time terminal directly inside the target EC2 instance.

- Requires the Session Manager plugin installed on your local machine.
- The instance must run the SSM Agent and have AmazonSSMManagedInstanceCore attached to its instance profile.

#### Open a secure local port-forwarding tunnel

```bash
# Forward local port 33060 to port 3306 on the private instance
aws ssm start-session \
  --target i-0123456789abcdef0 \
  --document-name AWS-StartPortForwardingSession \
  --parameters '{"portNumber":["3306"],"localPortNumber":["33060"]}'
```

_output_
```text
Starting session with SessionId: local-tunnel-01234...
Port 33060 opened for session id local-tunnel-01234...
```

Routes traffic from your local port 33060 through SSM into port 3306 on the private EC2 target.

- Uses the AWS-StartPortForwardingSession document to build the tunnel.
- Great for running a local DB client against a private database tier with no internet exposure.

**Best practices:**

- Eliminate bastion hosts and close inbound port 22 in every security group, routing all access through auditable Session Manager connections.

**Common errors:**

- **An error occurred (TargetNotConnected) when calling the StartSession operation.**: The SSM Agent cannot reach the control plane. Confirm the agent is running, the instance profile has AmazonSSMManagedInstanceCore, and VPC endpoints or a NAT gateway provide outbound routing.

**Advanced notes:**

- **Session logging and audit trails:** Unlike SSH, Session Manager integrates with auditing. Session input and output can be tracked, encrypted with AWS KMS, and streamed to S3 buckets or CloudWatch Log groups for compliance.

## Pagination & Waiters

Control how the CLI fetches multi-page results, block until a resource reaches a state, and reuse JSON input skeletons.

### Controlling Pagination

Tune per-call size and total items, or disable auto-pagination entirely.

**Keywords:** pagination, page-size, max-items

#### Page size, item cap, and resuming

```bash
aws ec2 describe-instances --page-size 20   # 20 items per API call
aws iam list-users --max-items 50           # cap total returned at 50
aws iam list-users --max-items 50 --starting-token <token> # resume
```

--page-size sets the per-request round-trip size; --max-items caps total output and prints a NextToken if more remains.

- --page-size and --max-items are independent - one controls API load, the other how much you get back.

**Best practices:**

- Use --page-size to avoid throttling on large accounts and --max-items only to limit output; treating them as the same thing causes surprises.

**Common errors:**

- **A truncated result with a printed NextToken.**: More data remains. Resume with --starting-token <token> rather than assuming the list is complete.

### Waiters & JSON Skeletons

Block until a state is reached, and generate or reuse structured JSON input.

**Keywords:** wait, cli-input-json, generate-cli-skeleton

#### Wait for a resource state

```bash
aws ec2 wait instance-running --instance-ids i-0abc123
aws cloudformation wait stack-create-complete --stack-name my-stack
```

Waiters poll on the right interval and exit non-zero on failure or a terminal state like ROLLBACK.

#### Generate and reuse a JSON skeleton

```bash
aws ec2 run-instances --generate-cli-skeleton input > params.json
aws ec2 run-instances --cli-input-json file://params.json
```

Emit a template of every parameter, edit it, then run from the file instead of long inline flags.

**Best practices:**

- Chain a waiter after any create/start command in scripts instead of sleep loops - waiters fail fast on terminal errors rather than waiting out the timeout.

## STS & Cross-Account Access

Assume IAM roles for temporary credentials, export them, and configure automatic role assumption with MFA.

### Assuming Roles

Request temporary credentials for a cross-account role and export them to the environment.

**Keywords:** sts, assume-role, session-token

#### Assume a role and export the credentials

```bash
aws sts assume-role \
  --role-arn arn:aws:iam::222222222222:role/DeployRole \
  --role-session-name deploy
# export via --query (repeat for SecretAccessKey and SessionToken)
export AWS_ACCESS_KEY_ID=$(aws sts assume-role --role-arn <arn> \
  --role-session-name s --query 'Credentials.AccessKeyId' --output text)
```

Temporary credentials require all three variables incl. AWS_SESSION_TOKEN; a meaningful --role-session-name shows up in CloudTrail.

**Best practices:**

- Prefer source_profile + role_arn in config over manual export - the CLI assumes and refreshes automatically, and static exports expire mid-task and leak easily.

**Common errors:**

- **An error occurred (ExpiredToken): the security token included in the request is expired.**: Temporary credentials aged out (default 1 hour). Re-run assume-role, or use a config profile that auto-refreshes.

### Config-File Assumption & MFA

Assume roles transparently from a named profile, including MFA-protected roles.

**Keywords:** source_profile, role_arn, mfa_serial

#### A role-assuming profile with MFA

```bash
# ~/.aws/config
[profile deploy]
role_arn       = arn:aws:iam::222222222222:role/DeployRole
source_profile = default
mfa_serial     = arn:aws:iam::111111111111:mfa/alice
# then: aws s3 ls --profile deploy  (prompts for the OTP once)
```

The CLI assumes the role transparently and caches the MFA-backed session, so you enter the token code once, not per command.

- credential_process = /path/to/program delegates credential retrieval to an external helper that outputs JSON.

**Best practices:**

- Keep a long-lived base profile as source_profile and put mfa_serial in config; the cached session means one OTP prompt instead of one per command.

## CloudWatch Logs

Stream, search, and manage retention on CloudWatch Logs groups from the terminal.

### Tailing & Searching

Live-tail new events or search historical events across all streams in a group.

**Keywords:** logs, tail, filter-log-events

#### Tail live and search history

```bash
aws logs tail /aws/lambda/my-func --follow --since 1h # like tail -f
aws logs tail /aws/lambda/my-func --follow --filter-pattern ERROR
aws logs filter-log-events --log-group-name /aws/lambda/my-func \
  --filter-pattern "ERROR" # search all streams
```

tail --follow streams new events; --since adds recent context; filter-log-events searches history.

- filter-log-events uses epoch MILLISECONDS for --start-time/--end-time - generate them with date -d '1 hour ago' +%s000.

**Best practices:**

- Combine --follow with --since (e.g. --since 10m) so you get recent context immediately rather than waiting only for brand-new events.

**Common errors:**

- **ResourceNotFoundException: the specified log group does not exist.**: Usually a typo or wrong region. Confirm the exact name with aws logs describe-log-groups and check --region.

### Groups & Retention

List log groups and control how long events are retained.

**Keywords:** describe-log-groups, put-retention-policy, retention

#### Set a retention policy

```bash
aws logs describe-log-groups --log-group-name-prefix /aws/lambda/
aws logs put-retention-policy --log-group-name /aws/lambda/my-func \
  --retention-in-days 30
```

New groups never expire by default; put-retention-policy caps storage (valid values include 1, 7, 14, 30, 90, 365 days).

**Best practices:**

- Set a retention policy on every log group - the default "never expire" grows storage cost forever.

## Lambda

List, inspect, invoke, and deploy Lambda functions, including the AWS CLI v2 base64 payload gotcha.

### Inspecting & Invoking

Read function config and invoke functions synchronously or asynchronously.

**Keywords:** lambda, invoke, cli-binary-format

#### Invoke with a raw JSON payload

```bash
aws lambda invoke --function-name my-func \
  --cli-binary-format raw-in-base64-out \
  --payload '{"name":"Bob"}' response.json
aws lambda list-functions --query 'Functions[].FunctionName' --output text
```

In v2 --payload is base64 by default; --cli-binary-format raw-in-base64-out lets you pass literal JSON. The output file is a required positional arg.

- Set it once with aws configure set cli-binary-format raw-in-base64-out to omit the flag on future invokes.

**Best practices:**

- Use get-function-configuration (not get-function) when you only need settings - it skips generating the code-download URL and returns faster.

**Common errors:**

- **Invalid base64: "{"name":"Bob"}".**: Add --cli-binary-format raw-in-base64-out (or set it as default) so --payload is read as raw JSON in AWS CLI v2.

### Deploying Code

Ship a new zip or image and change runtime settings without redeploying.

**Keywords:** update-function-code, fileb, update-function-configuration

#### Deploy a new package

```bash
aws lambda update-function-code --function-name my-func \
  --zip-file fileb://function.zip # fileb:// reads raw binary
aws lambda update-function-configuration --function-name my-func \
  --timeout 30 --memory-size 512
```

Use fileb:// (binary), not file:// (text), for --zip-file - file:// UTF-8-decodes the archive and corrupts the upload.

**Best practices:**

- After an update, wait with aws lambda wait function-updated --function-name my-func before the next change to avoid ResourceConflictException.

## Secrets & Parameter Store

Retrieve and write secrets from Secrets Manager and SSM Parameter Store, including encrypted SecureString handling.

### Secrets Manager

Create secrets and fetch their values, optionally pulling a single JSON field.

**Keywords:** secretsmanager, get-secret-value, SecretString

#### Read and create secrets

```bash
aws secretsmanager get-secret-value --secret-id prod/db \
  --query SecretString --output text
aws secretsmanager create-secret --name prod/db \
  --secret-string '{"user":"admin","password":"s3cr3t"}'
```

--query SecretString --output text strips the JSON envelope; pipe to jq -r .password to pull one field.

**Best practices:**

- Reference secrets by full ARN when identical names might exist across replicated regions, to avoid ambiguity.

### SSM Parameter Store

Read and write plain and encrypted parameters, and load a whole path in one call.

**Keywords:** ssm, get-parameter, SecureString

#### Read and write parameters

```bash
aws ssm get-parameter --name /prod/db/password --with-decryption \
  --query 'Parameter.Value' --output text
aws ssm get-parameters-by-path --path /prod/db/ --recursive --with-decryption
aws ssm put-parameter --name /prod/db/password --value 's3cr3t' \
  --type SecureString --overwrite
```

--with-decryption returns the plaintext of a SecureString (needs kms:Decrypt); --overwrite is required to update an existing parameter.

- Namespace parameters as /app/env/key so get-parameters-by-path --recursive loads a whole environment in one request.

**Best practices:**

- Use SecureString for static config secrets (cheaper) and Secrets Manager where built-in automatic rotation matters.

**Common errors:**

- **A SecureString value comes back as an unreadable KMS blob.**: You forgot --with-decryption. Add it and ensure the caller has kms:Decrypt on the key.

## ECR & Containers

Authenticate Docker to ECR, manage repositories and images, and trigger ECS redeployments.

### Auth, Repositories & Images

Log Docker into ECR and manage repositories and images.

**Keywords:** ecr, get-login-password, docker

#### Authenticate Docker to ECR

```bash
aws ecr get-login-password --region us-east-1 | docker login \
  --username AWS --password-stdin \
  111111111111.dkr.ecr.us-east-1.amazonaws.com
```

Pipes a 12-hour token into docker login via stdin, keeping it out of shell history and the process list.

- The old aws ecr get-login (which printed docker login -p <token>) is removed in AWS CLI v2 - get-login-password is the only supported command.

#### Create a repo and list images

```bash
aws ecr create-repository --repository-name my-app \
  --image-scanning-configuration scanOnPush=true
aws ecr list-images --repository-name my-app --filter tagStatus=UNTAGGED
```

scanOnPush=true enables vulnerability scanning on push; filtering untagged images finds cleanup candidates.

**Best practices:**

- Always pipe get-login-password into --password-stdin; never pass the token as --password where it lands in shell history.

**Common errors:**

- **denied: your authorization token has expired. Reauthenticate and try again.**: The ECR token lasts only 12 hours. Re-run the get-login-password | docker login command.

### ECS Deployments

Force a service to redeploy or scale its running task count.

**Keywords:** ecs, update-service, force-new-deployment

#### Force a redeploy or scale

```bash
aws ecs update-service --cluster prod --service web \
  --force-new-deployment # pull latest :tag, same task def
aws ecs update-service --cluster prod --service web --desired-count 4
```

--force-new-deployment rolls tasks to the newest image on a mutable tag; --desired-count scales the running task count.

**Best practices:**

- For auditable rollbacks, prefer immutable image tags and a new task-definition revision over force-redeploying a mutable :latest tag.
