CLI, SDK and IAM policies — Cheat Sheet (DVA-C02)
Verified against AWS documentation on 2026-10-12 · ⚠️ = changed vs older material: learn the value shown here (older practice material may say otherwise; full list in notes/cli-sdk-policies-guide.md#changed-vs-older-material) · ⚠️ volatile = AWS has announced a change · explanations and examples: notes/cli-sdk-policies-guide.md.
AWS CLI profiles and configuration files
| Topic | Current facts | Memory hook |
|---|---|---|
| CLI version | Use v2; ⚠️ v1 in maintenance mode since 2026-07-15, end of support 2027-07-15 | v1 is frozen |
| Two files | ~/.aws/credentials = secrets; ~/.aws/config = Region, output, role, SSO, login and retry settings |
Secrets vs settings |
| Section names | config: [default], [profile dev], [sso-session x], [services x]; credentials: [dev], never profile |
"profile" only in config |
| Same profile in both files | The keys in credentials win |
|
| Relocate the files | AWS_CONFIG_FILE, AWS_SHARED_CREDENTIALS_FILE (environment only) |
|
| Setting precedence | Command-line option > environment variable > profile setting | Closest to the command wins |
| Region order | --region > AWS_REGION > AWS_DEFAULT_REGION > profile region (CLI v1 and boto3 read only AWS_DEFAULT_REGION, not AWS_REGION) |
|
| No Region configured | ⚠️ No default: CLI "You must specify a region", boto3 NoRegionError, Java 2.x client creation fails |
No Region, no call |
| Output formats | json (default), yaml, yaml-stream, text, table, off |
|
aws configure subcommands |
list (value + source), list-profiles, get, set, import --csv (IAM user CSV), export-credentials, sso, sso-session |
|
| Custom endpoint order | --endpoint-url > AWS_ENDPOINT_URL_<SERVICE> > AWS_ENDPOINT_URL > [services] endpoint_url > profile endpoint_url |
Most specific wins |
| First debugging tool | --debug: credential lookup, canonical request, raw response |
MFA with the CLI and session tokens
| Topic | Current facts | Memory hook |
|---|---|---|
aws sts get-session-token |
--serial-number (virtual device ARN or hardware serial), --token-code (6 digits), --duration-seconds |
Code in, session out |
| Session duration | IAM user 900–129,600 s, default 43,200 s; root 900–3,600 s | 15 min to 36 h, default 12 h |
| Returned values | AccessKeyId (ASIA…), SecretAccessKey, SessionToken, Expiration: use all three |
ASIA = temporary |
| What the session can do | The IAM user's own permissions; IAM calls only with MFA info; STS only AssumeRole and GetCallerIdentity |
Same user, MFA stamp |
| Role profile with MFA | role_arn + source_profile + mfa_serial; CLI prompts "Enter MFA code"; cache ~/.aws/cli/cache |
|
duration_seconds |
Default 3,600; 900 up to the role maximum (≤ 43,200); role chaining caps sessions at 1 h | |
| MFA condition keys | aws:MultiFactorAuthPresent (absent for access-key calls), aws:MultiFactorAuthAge (seconds since MFA); policy patterns in advanced-identity §10 |
|
mfa_serial without role_arn |
Ignored: MFA prompts happen only in role profiles; use get-session-token for the user's own calls |
No role, no prompt |
| Not usable for API MFA | Root user credentials, U2F security keys | TOTP for the CLI |
| MFA condition in a resource policy | Needs GetSessionToken credentials; AssumeRole credentials carry no MFA context into later calls |
CLI credential provider chain
| Step | AWS CLI v2 source (⚠️ 10 steps, first match wins) | Note |
|---|---|---|
| 1 | Command-line options (--profile) |
Skips exported keys |
| 2 | AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_SESSION_TOKEN |
Beats the profile chosen by AWS_PROFILE |
| 3 | Assume role: role_arn + source_profile or credential_source |
|
| 4 | Web identity: AWS_WEB_IDENTITY_TOKEN_FILE + AWS_ROLE_ARN |
OIDC, EKS IRSA |
| 5 | IAM Identity Center (aws sso login) |
|
| 6 | ~/.aws/credentials |
|
| 7 | credential_process |
Roles Anywhere helper |
| 8 | ~/.aws/config |
|
| 9 | Container credentials (ECS task role, 169.254.170.2) |
|
| 10 | EC2 instance profile through IMDS | Last resort |
| Topic | Current facts | Memory hook |
|---|---|---|
credential_source values |
Environment, Ec2InstanceMetadata, EcsContainer; never together with source_profile |
|
| Caches | Roles ~/.aws/cli/cache; Identity Center ~/.aws/sso/cache; console login ~/.aws/login/cache |
|
| Revoked role session | Cached credentials aren't renewed; delete ~/.aws/cli/cache |
|
| IMDS knobs (CLI) | 1 attempt, 1 s timeout (AWS_METADATA_SERVICE_NUM_ATTEMPTS, AWS_METADATA_SERVICE_TIMEOUT); AWS_EC2_METADATA_DISABLED=true skips IMDS |
|
| Diagnose | aws sts get-caller-identity → aws configure list → --debug ("Found credentials in …") |
Who, from where, how |
| Classic failure | Keys left in the environment beat the newly attached instance profile; remove them | Stray keys win |
SDK default credential provider chains
| SDK or platform | How credentials are found | Memory hook |
|---|---|---|
| Java 2.x ⚠️ | aws.accessKeyId / aws.secretAccessKey / aws.sessionToken → env → web identity → profile (SSO, role, login, process, keys) → ECS container → IMDS |
Properties first, instance last |
| Java 2.x failure | SdkClientException: Unable to load credentials from any of the providers in the chain |
|
| boto3 | Client params → Session params → env → assume role → web identity → Identity Center → credentials file → console login → config file → Boto2 files → container → IMDS (12 steps) | |
| boto3 failure | NoCredentialsError ("Unable to locate credentials") |
|
| Profile named in code | boto3.Session(profile_name=…) skips exported keys; AWS_PROFILE leaves them in front |
Explicit beats ambient |
| Lambda | Runtime sets AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_SESSION_TOKEN from the execution role |
Role arrives as env vars |
| ECS | Task role through AWS_CONTAINER_CREDENTIALS_RELATIVE_URI, before IMDS |
Task before host |
| EKS Pod Identity | AWS_CONTAINER_CREDENTIALS_FULL_URI + AWS_CONTAINER_AUTHORIZATION_TOKEN_FILE |
|
| EC2 | IMDSv2 first; v1 fallback only after 403/404/405; AWS_EC2_METADATA_V1_DISABLED=true blocks it; Kotlin and Rust never fall back |
|
| Pin the instance role | Java InstanceProfileCredentialsProvider ignores exported keys |
Credentials best practices
| Situation | Use | Memory hook |
|---|---|---|
| Code on EC2, ECS, Lambda, EKS | Instance profile, task role, execution role, Pod Identity or IRSA | Compute brings its own role |
| Developer laptop | ⚠️ aws login (CLI 2.32.0+, 15-minute credentials refreshed for up to 12 h) or IAM Identity Center |
Borrow, don't own |
aws login permissions |
Managed policy SignInLocalDevelopmentAccess (root needs none); --remote without a browser |
|
| IAM Identity Center | aws configure sso ([sso-session]), aws sso login; PKCE by default since CLI 2.22.0, --use-device-code otherwise |
|
| On-premises server | IAM Roles Anywhere: trust anchor (your CA or AWS Private CA, never a public CA) + profile; role trusts rolesanywhere.amazonaws.com; aws_signing_helper via credential_process |
Certificate in, role out |
| CI/CD outside AWS | OIDC federation with AssumeRoleWithWebIdentity |
|
| Application secrets | Secrets Manager or Parameter Store SecureString, read with the role |
|
| Long-term keys unavoidable | One user per workload, least privilege, rotate, remove unused keys | |
| AWS CLI ranking | Console login → Identity Center → IAM user short-term → EC2 metadata → assume role; long-term keys "Not recommended" |
Using the AWS SDKs in application code
| Topic | Current facts | Memory hook |
|---|---|---|
| Current SDKs ⚠️ | C++, Go v2, Java 2.x, JavaScript v3, Kotlin, .NET 4.x, PHP 3.x, Python (boto3), Ruby 3.x, Rust, Swift, SAP ABAP, Tools for PowerShell | |
| End of support | Java 1.x, JavaScript v2, Go v1, .NET 3.x | |
| CLI internals | The AWS CLI is built on botocore, the core of boto3 | |
| Region ⚠️ | No default; Java 2.x chain: builder → aws.region → AWS_REGION → profile → IMDS → exception |
|
| Clients | Create once and reuse; one per Region (Java 2.x clients are immutable); don't share across processes | |
| Pagination | get_paginator("list_objects_v2").paginate(PaginationConfig={"PageSize": 500}); S3 pages hold ≤ 1,000 keys |
Don't chase tokens by hand |
| Waiters | get_waiter("bucket_exists").wait(..., WaiterConfig={"Delay": 5, "MaxAttempts": 20}) |
Wait, don't loop |
| Service errors (boto3) | ClientError → err.response["Error"]["Code"]; modeled s3.exceptions.NoSuchKey |
|
| Client-side errors (boto3) | NoCredentialsError, NoRegionError, ParamValidationError, EndpointConnectionError, ReadTimeoutError |
|
| Local endpoint | boto3 endpoint_url=, Java .endpointOverride(URI), or AWS_ENDPOINT_URL_<SERVICE> |
CLI output filtering and pagination
| Option | Effect | Memory hook |
|---|---|---|
--page-size N |
Smaller pages per API call, same total output; fixes timeouts | Page size = per call |
--max-items N |
Prints N items plus a NextToken |
Max items = total |
--starting-token |
Resumes from the NextToken of a --max-items run |
|
--no-paginate |
First page only | |
--filters |
Server-side: less data returned by the API | Filter at the source |
--query |
Client-side JMESPath after the full response; with --output text it runs per page |
Query = cosmetics |
--no-cli-pager or AWS_PAGER="" |
Turns off the CLI v2 pager in scripts | |
--generate-cli-skeleton |
Parameter template for --cli-input-json or --cli-input-yaml (not for aws s3 commands) |
Service quotas and API rate limits
| Topic | Current facts | Memory hook |
|---|---|---|
| Quota vs rate limit | Quota = how much (create fails, VcpuLimitExceeded); rate limit = how fast (throttled, RequestLimitExceeded) |
Amount vs speed |
| Default vs applied | AWS default value vs your account's applied value; Regional quotas raised per Region, global ones requested in us-east-1 | |
| Request an increase | Service Quotas console or RequestServiceQuotaIncrease; small increases often automatic, larger ones via Support |
|
| Usage alarm | AWS/Usage metric with m1/SERVICE_QUOTA(m1)*100 > 80 |
|
| Automatic Management | Opt-in alerts at 80 % and 95 %; Notify Only or Notify and Auto-Adjust | |
| Quota request template | Organizations (all features), ≤ 10 quotas, new accounts only | |
| EC2 vCPU quota ⚠️ | Standard On-Demand (L-1216C47A) AWS default 5 vCPUs, raised automatically with usage |
|
| EC2 API buckets ⚠️ | Non-mutating 100 / 20 per s; unfiltered unpaginated DescribeInstances 50 / 10; mutating 50 / 5 |
Filter and paginate |
| EC2 API increase | AWS Support case; ≤ 3× per request; refill rate first | |
| Bursty vs constant throttling | Comes and goes → let the SDK retries absorb it; never lets up → cut calls, then raise the limit |
Retries and exponential backoff with jitter
| Topic | Current facts | Memory hook |
|---|---|---|
| Retry these ⚠️ | Transient (timeouts, 500/502/503/504, 400 RequestTimeout) and throttling (ThrottlingException, TooManyRequestsException, RequestLimitExceeded, SlowDown, ProvisionedThroughputExceededException), even with a 4xx status |
Errors that heal with time |
| Don't retry | AccessDeniedException, ValidationException, ResourceNotFoundException |
Same input, same failure |
standard |
Recommended; CLI v2 default; 3 attempts in total; 20 s maximum backoff; retry quota | |
adaptive |
Standard + client-side rate limiter; one hot resource; "experimental" in the CLI guide | Adaptive = self-braking |
legacy |
Default of CLI v1 and boto3 today: 5 attempts (CLI v1 DynamoDB 10) | |
| Settings | AWS_RETRY_MODE / retry_mode, AWS_MAX_ATTEMPTS / max_attempts (counts the first call) |
Attempts include the first try |
boto3 Config keys |
total_max_attempts includes the first call; max_attempts counts retries only |
|
| 2026 update ⚠️ volatile | Opt-in AWS_NEW_RETRIES_2026=true, default from November 2026: standard everywhere, DynamoDB 4 attempts |
|
| Full jitter | delay = random(0, 1) × min(cap, base × 2^retry) | Randomness breaks the herd |
| Idempotency | --client-token (≤ 64 ASCII characters); other parameters with the same token → IdempotentParameterMismatch |
Signing requests with SigV4
| Topic | Current facts | Memory hook |
|---|---|---|
| Who signs | SDKs and the CLI automatically; you only for raw HTTP | |
| Header form | Authorization: AWS4-HMAC-SHA256 Credential=<key-id>/<date>/<region>/<service>/aws4_request, SignedHeaders=…, Signature=… + X-Amz-Date |
|
| Temporary credentials | Add the session token as X-Amz-Security-Token |
The token travels too |
| Amazon S3 extra | x-amz-content-sha256 header (payload hash or UNSIGNED-PAYLOAD) |
|
| Query-string form | X-Amz-Algorithm, X-Amz-Credential, X-Amz-Date, X-Amz-Expires, X-Amz-SignedHeaders, X-Amz-Signature = a presigned URL |
|
| Signing key | HMAC chain: "AWS4" + secret → date → Region → service → aws4_request |
Date, Region, service, request |
| SigV4a | AWS4-ECDSA-P256-SHA256, X-Amz-Region-Set; needed for S3 Multi-Region Access Points |
a = a set of Regions |
| Clock skew | Signature expired, RequestExpired, S3 RequestTimeTooSkewed or SignatureDoesNotMatch → sync the clock (NTP) |
|
| Presigned URL lifetime | Shorter of X-Amz-Expires and the signing credentials' lifetime (AssumeRole default 1 h, EC2 role credentials about 6 h) |
URL dies with its signer |
| SigV2 | Deprecated: Regions launched after January 2014 accept only SigV4; eight older Regions still take SigV2 for S3; block it with s3:signatureversion |
Testing permissions with dry run and the policy simulator
| Tool | Current facts | Memory hook |
|---|---|---|
EC2 --dry-run |
DryRunOperation = allowed; UnauthorizedOperation = denied; nothing is created |
Dry-run success is an error |
aws s3 cp / rm / sync --dryrun |
Prints the planned operations only; no permission check | |
Lambda Invoke |
--invocation-type DryRun validates parameters and invoke permission (HTTP 204) |
|
| Successful dry run | Proves permissions and parameters, not launch-time capacity | |
| Non-EC2 or draft policy | Policy simulator: simulate-principal-policy (attached policies) or simulate-custom-policy (drafts, --context-entries); details in iam §6 |
No dry run? Simulate |
| Simulator results | allowed, explicitDeny, implicitDeny; nothing reaches the service; RCPs not evaluated |
Decoding authorization failures
| Error or tool | Meaning | Fix |
|---|---|---|
| Plain-text access denied | because no <type> policy allows = implicit; with an explicit deny in a <type> policy = explicit; one denying type named (catalogue: iam §6) |
Add an allow, or change the deny |
EC2 UnauthorizedOperation + encoded message |
Details hidden for privacy | aws sts decode-authorization-message, needs sts:DecodeAuthorizationMessage |
| Decoded fields | allowed, explicitDeny, matchedStatements, failures, context (principal, action, resource, conditions) |
|
InvalidClientTokenId / UnrecognizedClientException |
Unknown access key or token | Fix the credential source |
SignatureDoesNotMatch |
Wrong secret, altered request or clock skew | Fix credentials, proxy or NTP |
ExpiredTokenException (S3: ExpiredToken) |
Temporary session expired | Sign in again or get a new session |
Unable to locate credentials |
Nothing found in the chain | Attach a role or fix the profile |
aws sts get-caller-identity |
Shows Account, Arn, UserId; needs no permission, can't be denied |
Always allowed, run it first |
Numbers to know
| Item | Value | Note |
|---|---|---|
GetSessionToken duration, IAM user |
900–129,600 s (15 min–36 h) | Default 43,200 s (12 h) |
GetSessionToken duration, root |
900–3,600 s | Default 1 h; don't use root |
MFA TokenCode |
6 digits | TOTP from a virtual or hardware device |
Role profile duration_seconds |
Default 3,600 s | 900 up to the role maximum (≤ 43,200 s) |
| Role chaining session | 1 h maximum | Cannot be raised |
aws login minimum CLI version |
2.32.0 | Credentials refreshed every 15 min, session ≤ 12 h |
| IAM Identity Center PKCE default | CLI 2.22.0 and later | --use-device-code for the device flow |
| AWS CLI v2 credential sources | 10 ⚠️ | First match wins |
| boto3 credential search steps | 12 | Container and IMDS are the last two |
| AWS CLI v1 timeline | Maintenance 2026-07-15, end of support 2027-07-15 ⚠️ | Use v2 |
| CLI IMDS lookup | 1 attempt, 1 s timeout | AWS_METADATA_SERVICE_NUM_ATTEMPTS, AWS_METADATA_SERVICE_TIMEOUT |
| ECS credentials endpoint | 169.254.170.2 |
Plus AWS_CONTAINER_CREDENTIALS_RELATIVE_URI |
| IMDS endpoints | 169.254.169.254, [fd00:ec2::254] |
IPv6 needs endpoint mode IPv6 |
| Standard retry mode | 3 attempts in total | 1 call + 2 retries |
| Legacy retry mode (CLI v1, boto3) | 5 attempts | CLI v1 DynamoDB: 10 |
| Maximum retry backoff | 20 s | Standard and adaptive modes |
| 2026 retry update ⚠️ volatile | Base 50 ms transient / 1,000 ms throttling; DynamoDB 25 ms and 4 attempts | Opt-in now, default from November 2026 |
| 2026 retry quota ⚠️ volatile | 500 tokens; 14 per transient retry, 5 per throttling retry | Fails fast during outages |
| EC2 non-mutating API bucket | 100 burst, 20 per s | Filtered or paginated Describe* |
Unfiltered, unpaginated DescribeInstances |
50 burst, 10 per s ⚠️ | Course: 100 calls per second |
| EC2 mutating API bucket | 50 burst, 5 per s | |
RunInstances |
Request bucket 5 / 2 per s; resource bucket 1,000 instances / 2 per s | |
| EC2 API limit increase | ≤ 3× current per request | Through an AWS Support case |
| Running On-Demand Standard instances | AWS default 5 vCPUs ⚠️ | Quota code L-1216C47A; course: 1,152 |
| S3 request rate per partitioned prefix | ≥ 3,500 writes/s, 5,500 reads/s | 503 Slow Down while S3 scales |
| Automatic Management thresholds | 80 % and 95 % utilization | Notify Only or Notify and Auto-Adjust |
| Quota request template | ≤ 10 quotas | New accounts in the organization only |
| SigV4 presigned URL maximum | 604,800 s (7 days) | aws s3 presign default 3,600 s |
| S3 console presigned URL | 1 min–12 h | |
| Signature timestamp window | Varies by service: 5 min in most cases (IAM User Guide SigV4 page); 15 min either way in the RequestExpired error (STS, EC2) |
Plan for 5 min; sync the clock (NTP) |
| SigV4 signature length | 64 lowercase hex characters | 256-bit HMAC-SHA256 |
| EC2 client token | ≤ 64 ASCII characters | Case-sensitive |
EncodedMessage length |
1–10,240 characters | InvalidAuthorizationMessage if invalid or expired |
| S3 list page | ≤ 1,000 keys | Use a paginator |
| Default AssumeRole session | 1 h | Caps presigned URLs signed with it |
Decision rules
- If you see "the role is attached but calls still act as an IAM user", think stray keys earlier in the chain:
aws configure list, then remove them. - If you see
--profilevsAWS_PROFILEwith exported keys, think the option skips the keys and the variable doesn't. - If you see "You must specify a region" or
NoRegionError, think configure a Region: there is no default. - If you see "one service to a local emulator, no code change", think
AWS_ENDPOINT_URL_<SERVICE>. - If you see an MFA condition and access keys, think
aws sts get-session-tokenor a role profile withmfa_serial. - If you see
mfa_serialin a profile withoutrole_arn, think it's ignored: useget-session-token. - If you see a developer laptop needing credentials, think
aws loginor IAM Identity Center. - If you see an on-premises server with an existing CA, think IAM Roles Anywhere.
- If you see CI/CD outside AWS, think OIDC federation with
AssumeRoleWithWebIdentity. - If you see code on EC2, ECS or Lambda, think the service's role and the SDK default chain, no keys.
- If you see "only the instance role, whatever is exported", think
InstanceProfileCredentialsProvider. - If you see "only the first 1,000 results", think a paginator.
- If you see "wait until it exists", think a waiter or EventBridge events.
- If you see a CLI timeout on a big listing, think
--page-size. - If you see "N items now, the rest later", think
--max-items+--starting-token. - If you see "less data from the API", think server-side
--filters, not--query. - If you see EC2
RequestLimitExceeded, think filter, paginate and back off; then a Support case. - If you see
VcpuLimitExceeded, think a Service Quotas increase, not retries. - If you see "warn before the quota", think
AWS/Usagealarm withSERVICE_QUOTA()or Automatic Management. - If you see throttling that comes and goes, think the SDK's built-in retries with jittered backoff.
- If you see one hot table throttling a client, think retry mode
adaptive. - If you see
AccessDeniedExceptionorValidationException, think no retry. - If you see a retried create that must happen once, think a client token.
- If you see raw HTTP to AWS, think SigV4 signing plus
X-Amz-Security-Tokenfor temporary credentials. - If you see
Signature expiredorRequestTimeTooSkewedwith unchanged code, think clock skew and NTP. - If you see presigned URLs dying early, think the signing credentials expired.
- If you see a multi-Region signature, think SigV4a.
- If you see "check EC2 permissions without side effects", think
--dry-run. - If you see "test a draft policy", think
simulate-custom-policy. - If you see an encoded authorization message, think
aws sts decode-authorization-message.
Memory hooks
- "profile" lives only in the config file.
- Closest to the command wins: option, then environment, then profile.
- No Region, no call: nothing falls back to us-east-1.
- Code in, session out:
get-session-tokenturns a 6-digit code into MFA-stamped credentials. - ASIA means temporary: always carry the session token.
- No role, no prompt:
mfa_serialworks only next torole_arn. - Stray keys win: the instance role is the last resort.
--profilebeats exported keys;AWS_PROFILEdoesn't.- Task before host: the ECS task role is found before IMDS.
- Role arrives as env vars in Lambda.
- Borrow credentials, don't own them:
aws login, Identity Center, Roles Anywhere. - Certificate in, role out: IAM Roles Anywhere.
- Don't chase tokens by hand: paginators.
- Wait, don't loop: waiters.
- Page size = per call; max items = total.
- Filter at the source;
--queryis cosmetics. - Amount vs speed: quota vs rate limit.
- Filter and paginate to reach the big EC2 bucket.
- Randomness breaks the herd: full jitter.
- Attempts include the first try.
- Same input, same failure: don't retry 4xx validation or permission errors.
- The token travels too:
X-Amz-Security-Token. - The URL dies with its signer.
- Dry-run success is an error:
DryRunOperation. - Always allowed, run it first:
aws sts get-caller-identity.