Setup & configuration
Get credentials in place and control output before anything else.
Configure credentials
Interactive prompt for access key, secret key, default region, and output format. Add --profile to keep multiple accounts side by side.
$ aws configure
$ aws configure --profile staging
Check who you are
Confirms which account and identity your current credentials resolve to. The first command to run when something returns AccessDenied.
$ aws sts get-caller-identity
Control output per command
Any command accepts --output (json, table, text, yaml), --region, --profile, and a JMESPath --query to trim the response.
$ aws ec2 describe-instances --output table --region us-west-2 \
--query 'Reservations[].Instances[].[InstanceId,State.Name]'
Built-in help
Every command and subcommand has a manual page.
$ aws s3 cp help
S3
Two command sets exist: high-level aws s3 (rsync-like) and low-level aws s3api.
List buckets and objects
No argument lists all buckets; a URI lists its contents. --recursive walks the whole prefix, --human-readable --summarize adds sizes and totals.
$ aws s3 ls
$ aws s3 ls s3://my-bucket/logs/ --recursive --human-readable --summarize
Copy files up or down
cp works in either direction and between buckets. Add --recursive for directories.
$ aws s3 cp ./report.pdf s3://my-bucket/reports/
$ aws s3 cp s3://my-bucket/reports/report.pdf .
Sync a directory
Copies only new or changed files. --delete removes remote files that no longer exist locally — the standard static-site deploy.
$ aws s3 sync ./dist s3://my-bucket --delete --exclude "*.map"
Delete objects or a bucket
rm --recursive empties a prefix; rb --force empties and removes the bucket itself.
$ aws s3 rm s3://my-bucket/logs/ --recursive
$ aws s3 rb s3://my-bucket --force
Generate a presigned URL
Creates a temporary link to a private object. --expires-in is in seconds (default 3600).
$ aws s3 presign s3://my-bucket/reports/report.pdf --expires-in 86400
Create a bucket
$ aws s3 mb s3://my-new-bucket --region us-west-2
EC2
List instances
Filter server-side with --filters, then trim client-side with --query.
$ aws ec2 describe-instances \
--filters "Name=instance-state-name,Values=running" \
--query 'Reservations[].Instances[].[InstanceId,InstanceType,PublicIpAddress,Tags[?Key==`Name`]|[0].Value]' \
--output table
Launch an instance
$ aws ec2 run-instances \
--image-id ami-0abcdef1234567890 \
--instance-type t3.micro \
--key-name my-key \
--security-group-ids sg-0123456789abcdef0 \
--tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=web-1}]'
Stop, start, terminate
Stopped instances keep their EBS volumes; terminate is permanent.
$ aws ec2 stop-instances --instance-ids i-0123456789abcdef0
$ aws ec2 start-instances --instance-ids i-0123456789abcdef0
$ aws ec2 terminate-instances --instance-ids i-0123456789abcdef0
Open a port on a security group
$ aws ec2 authorize-security-group-ingress \
--group-id sg-0123456789abcdef0 \
--protocol tcp --port 22 --cidr 203.0.113.0/24
Find the latest Amazon Linux AMI
Reads the public SSM parameter, so you never hardcode a stale AMI ID.
$ aws ssm get-parameter \
--name /aws/service/ami-amazon-linux-latest/al2023-ami-kernel-default-x86_64 \
--query 'Parameter.Value' --output text
IAM
List users, roles, policies
$ aws iam list-users --query 'Users[].UserName'
$ aws iam list-roles --query 'Roles[].RoleName'
$ aws iam list-attached-role-policies --role-name my-role
Create a user and access key
The secret key is shown once in the response — store it immediately.
$ aws iam create-user --user-name ci-deployer
$ aws iam create-access-key --user-name ci-deployer
Attach a managed policy
$ aws iam attach-user-policy \
--user-name ci-deployer \
--policy-arn arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess
Test whether an action is allowed
Simulates policy evaluation without actually calling the service.
$ aws iam simulate-principal-policy \
--policy-source-arn arn:aws:iam::123456789012:user/ci-deployer \
--action-names s3:PutObject
Lambda
List functions
$ aws lambda list-functions --query 'Functions[].[FunctionName,Runtime,LastModified]' --output table
Invoke a function
--cli-binary-format raw-in-base64-out lets you pass plain JSON on CLI v2. The response body lands in the output file.
$ aws lambda invoke \
--function-name my-function \
--cli-binary-format raw-in-base64-out \
--payload '{"key": "value"}' \
response.json && cat response.json
Deploy new code from a zip
$ zip -r function.zip . && aws lambda update-function-code \
--function-name my-function \
--zip-file fileb://function.zip
Update configuration
Note: --environment replaces the whole variable set, it doesn't merge.
$ aws lambda update-function-configuration \
--function-name my-function \
--timeout 30 --memory-size 512 \
--environment 'Variables={STAGE=prod,LOG_LEVEL=info}'
DynamoDB
List and describe tables
$ aws dynamodb list-tables
$ aws dynamodb describe-table --table-name Users --query 'Table.ItemCount'
Put an item
Every value is wrapped in a type descriptor: S string, N number, BOOL, L list, M map.
$ aws dynamodb put-item \
--table-name Users \
--item '{"userId": {"S": "u-100"}, "name": {"S": "Ada"}, "age": {"N": "37"}}'
Get an item by key
$ aws dynamodb get-item \
--table-name Users \
--key '{"userId": {"S": "u-100"}}'
Query by partition key
Prefer query over scan — it reads only the matching partition.
$ aws dynamodb query \
--table-name Orders \
--key-condition-expression 'userId = :u' \
--expression-attribute-values '{":u": {"S": "u-100"}}'
CloudFormation
Deploy a stack
deploy creates the stack if it's new and updates it via a change set if it exists — idempotent, so it's the CI-friendly choice.
$ aws cloudformation deploy \
--stack-name my-app \
--template-file template.yaml \
--parameter-overrides Stage=prod \
--capabilities CAPABILITY_NAMED_IAM
Inspect stacks and outputs
$ aws cloudformation describe-stacks --stack-name my-app \
--query 'Stacks[0].Outputs' --output table
See why a deploy failed
Events are newest-first; filter to the failures.
$ aws cloudformation describe-stack-events --stack-name my-app \
--query "StackEvents[?contains(ResourceStatus, 'FAILED')].[LogicalResourceId,ResourceStatusReason]" \
--output table
Delete a stack
$ aws cloudformation delete-stack --stack-name my-app
$ aws cloudformation wait stack-delete-complete --stack-name my-app
CloudWatch Logs
Tail logs live
The closest thing to tail -f for AWS. --since accepts values like 10m, 2h, 1d.
$ aws logs tail /aws/lambda/my-function --follow --since 15m
Search for a pattern
$ aws logs filter-log-events \
--log-group-name /aws/lambda/my-function \
--filter-pattern 'ERROR' \
--start-time $(date -d '1 hour ago' +%s000)
List log groups / set retention
$ aws logs describe-log-groups --query 'logGroups[].logGroupName'
$ aws logs put-retention-policy --log-group-name /aws/lambda/my-function --retention-in-days 30
ECR & ECS
Log Docker in to ECR
$ aws ecr get-login-password --region us-west-2 | \
docker login --username AWS --password-stdin \
123456789012.dkr.ecr.us-west-2.amazonaws.com
Tag and push an image
$ docker tag my-app:latest 123456789012.dkr.ecr.us-west-2.amazonaws.com/my-app:latest
$ docker push 123456789012.dkr.ecr.us-west-2.amazonaws.com/my-app:latest
Redeploy an ECS service
Forces new tasks with the same task definition — the standard "restart" after pushing a new :latest image.
$ aws ecs update-service --cluster prod --service web --force-new-deployment
List running tasks
$ aws ecs list-tasks --cluster prod --service-name web
SQS & SNS
Send a message
$ aws sqs send-message \
--queue-url https://sqs.us-west-2.amazonaws.com/123456789012/my-queue \
--message-body '{"orderId": 42}'
Receive and delete messages
Receiving doesn't remove a message — delete it with the receipt handle once processed.
$ aws sqs receive-message --queue-url $QUEUE_URL --wait-time-seconds 10
$ aws sqs delete-message --queue-url $QUEUE_URL --receipt-handle "$HANDLE"
Check queue depth
$ aws sqs get-queue-attributes --queue-url $QUEUE_URL \
--attribute-names ApproximateNumberOfMessages
Publish to an SNS topic
$ aws sns publish \
--topic-arn arn:aws:sns:us-west-2:123456789012:alerts \
--subject "Deploy finished" --message "v2.4.1 is live"
STS
Assume a role
Returns temporary credentials — export the three values it prints as AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, and AWS_SESSION_TOKEN.
$ aws sts assume-role \
--role-arn arn:aws:iam::123456789012:role/deploy-role \
--role-session-name cli-session
Get the current account ID
Handy inside scripts for building ARNs.
$ aws sts get-caller-identity --query Account --output text
No commands match "" — try a service name or verb like sync, tail, invoke.