Available for day contractsFrom 21st September I have availability for day and half day contracts. Please contact for more information.

Contact →
mikepreston.org

AWS

Essential AWS CLI commands and service configurations for cloud infrastructure management.

AWS

Essential AWS CLI commands and service configurations for cloud infrastructure management.

Overview

Amazon Web Services (AWS) provides on-demand cloud computing platforms and APIs. This cheatsheet covers core services for compute, storage, networking, identity management, containers, serverless, databases, and monitoring.

AWS Cloud ArchitectureMonitoringSecurityNetworkingDatabaseStorageComputeEC2 InstancesLambda FunctionsECS/EKS ContainersS3 BucketsEBS VolumesRDS DatabasesDynamoDB TablesVPCLoad BalancerRoute 53IAM Users/RolesSecurity GroupsCloudWatchUsersAWS Cloud ArchitectureMonitoringSecurityNetworkingDatabaseStorageComputeEC2 InstancesLambda FunctionsECS/EKS ContainersS3 BucketsEBS VolumesRDS DatabasesDynamoDB TablesVPCLoad BalancerRoute 53IAM Users/RolesSecurity GroupsCloudWatchUsers

AWS CLI Configuration

Installation and Setup

# Install AWS CLI v2 (Linux)
curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
unzip awscliv2.zip
sudo ./aws/install

# Verify installation
aws --version

# Configure default profile
aws configure
# AWS Access Key ID: AKIAIOSFODNN7EXAMPLE
# AWS Secret Access Key: wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
# Default region name: eu-west-1
# Default output format: json

Authentication Methods

# Configure named profile
aws configure --profile production

# Set profile for current session
export AWS_PROFILE=production

# Use profile for single command
aws s3 ls --profile production

# Configure with SSO
aws configure sso
aws sso login --profile sso-profile

# Assume role and export credentials
aws sts assume-role \
    --role-arn arn:aws:iam::123456789012:role/MyRole \
    --role-session-name MySession \
    --query 'Credentials.[AccessKeyId,SecretAccessKey,SessionToken]' \
    --output text

# Get caller identity (verify authentication)
aws sts get-caller-identity

Configuration Files

# ~/.aws/credentials
[default]
aws_access_key_id = AKIAIOSFODNN7EXAMPLE
aws_secret_access_key = wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY

[production]
aws_access_key_id = AKIAI44QH8DHBEXAMPLE
aws_secret_access_key = je7MtGbClwBF/2Zp9Utk/h3yCo8nvbEXAMPLEKEY

# ~/.aws/config
[default]
region = eu-west-1
output = json

[profile production]
region = eu-west-2
output = table
role_arn = arn:aws:iam::123456789012:role/ProductionRole
source_profile = default

EC2 Essentials

Key Pairs

# Create key pair and save private key
aws ec2 create-key-pair \
    --key-name my-key-pair \
    --query 'KeyMaterial' \
    --output text > my-key-pair.pem

# Set correct permissions
chmod 400 my-key-pair.pem

# List key pairs
aws ec2 describe-key-pairs

# Delete key pair
aws ec2 delete-key-pair --key-name my-key-pair

Security Groups

# Create security group
aws ec2 create-security-group \
    --group-name my-sg \
    --description "My security group" \
    --vpc-id vpc-1234567890abcdef0

# Add SSH access rule
aws ec2 authorize-security-group-ingress \
    --group-id sg-903004f8 \
    --protocol tcp \
    --port 22 \
    --cidr 10.0.0.0/8

# Add HTTP access
aws ec2 authorize-security-group-ingress \
    --group-id sg-903004f8 \
    --protocol tcp \
    --port 80 \
    --cidr 0.0.0.0/0

# List security groups
aws ec2 describe-security-groups --group-ids sg-903004f8

# Remove rule
aws ec2 revoke-security-group-ingress \
    --group-id sg-903004f8 \
    --protocol tcp \
    --port 22 \
    --cidr 10.0.0.0/8

Launching and Managing Instances

# Launch instance (enforce IMDSv2: HttpTokens=required mandates session
# tokens; HttpPutResponseHopLimit=1 stops containers/NAT'd workloads
# proxying the token — use 2 if containers legitimately need IMDS)
aws ec2 run-instances \
    --image-id ami-0abcdef1234567890 \
    --instance-type t3.micro \
    --key-name my-key-pair \
    --security-group-ids sg-903004f8 \
    --subnet-id subnet-6e7f829e \
    --count 1 \
    --metadata-options "HttpTokens=required,HttpPutResponseHopLimit=1,HttpEndpoint=enabled" \
    --tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=MyInstance}]'

# Require IMDSv2 on an existing instance
aws ec2 modify-instance-metadata-options \
    --instance-id i-1234567890abcdef0 \
    --http-tokens required \
    --http-put-response-hop-limit 1 \
    --http-endpoint enabled

# List instances
aws ec2 describe-instances \
    --filters "Name=instance-state-name,Values=running" \
    --query 'Reservations[].Instances[].{ID:InstanceId,Type:InstanceType,IP:PublicIpAddress,Name:Tags[?Key==`Name`].Value|[0]}'

# Get instance public IP
aws ec2 describe-instances \
    --instance-ids i-1234567890abcdef0 \
    --query 'Reservations[0].Instances[0].PublicIpAddress' \
    --output text

# Stop instance
aws ec2 stop-instances --instance-ids i-1234567890abcdef0

# Start instance
aws ec2 start-instances --instance-ids i-1234567890abcdef0

# Terminate instance
aws ec2 terminate-instances --instance-ids i-1234567890abcdef0

# Connect via SSH
ssh -i my-key-pair.pem ec2-user@ec2-198-51-100-1.compute-1.amazonaws.com

AMI Management

# Create AMI from instance
aws ec2 create-image \
    --instance-id i-1234567890abcdef0 \
    --name "My-AMI-$(date +%Y%m%d)" \
    --description "Backup AMI"

# List AMIs
aws ec2 describe-images \
    --owners self \
    --query 'Images[*].{ID:ImageId,Name:Name,Created:CreationDate}'

# Copy AMI to another region
aws ec2 copy-image \
    --source-image-id ami-0abcdef1234567890 \
    --source-region eu-west-1 \
    --region eu-west-2 \
    --name "Copied-AMI"

S3 Operations

Bucket Management

# Create bucket (with region)
aws s3 mb s3://my-unique-bucket-name --region eu-west-1

# Create bucket with versioning enabled
aws s3api create-bucket \
    --bucket my-versioned-bucket \
    --region eu-west-1 \
    --create-bucket-configuration LocationConstraint=eu-west-1

aws s3api put-bucket-versioning \
    --bucket my-versioned-bucket \
    --versioning-configuration Status=Enabled

# List buckets
aws s3 ls

# List bucket contents
aws s3 ls s3://my-bucket/prefix/

# Delete empty bucket
aws s3 rb s3://my-bucket

# Delete bucket with contents
aws s3 rb s3://my-bucket --force

Upload and Download

# Upload single file
aws s3 cp myfile.txt s3://my-bucket/

# Upload with custom metadata
aws s3 cp myfile.txt s3://my-bucket/ \
    --metadata '{"project":"demo","env":"prod"}'

# Download file
aws s3 cp s3://my-bucket/myfile.txt ./

# Upload directory recursively
aws s3 cp ./local-dir s3://my-bucket/remote-dir --recursive

# Download directory
aws s3 cp s3://my-bucket/remote-dir ./local-dir --recursive

# Sync local to S3 (only changed files)
aws s3 sync ./local-dir s3://my-bucket/remote-dir

# Sync S3 to local
aws s3 sync s3://my-bucket/remote-dir ./local-dir

# Sync with delete (mirror)
aws s3 sync ./local-dir s3://my-bucket/remote-dir --delete

# Sync excluding patterns
aws s3 sync ./local-dir s3://my-bucket/remote-dir \
    --exclude "*.tmp" \
    --exclude "*.log"

Lifecycle Rules

# Create lifecycle configuration file
cat > lifecycle.json << 'EOF'
{
    "Rules": [
        {
            "ID": "MoveToGlacier",
            "Status": "Enabled",
            "Filter": {
                "Prefix": "logs/"
            },
            "Transitions": [
                {
                    "Days": 30,
                    "StorageClass": "STANDARD_IA"
                },
                {
                    "Days": 90,
                    "StorageClass": "GLACIER"
                }
            ],
            "Expiration": {
                "Days": 365
            }
        }
    ]
}
EOF

# Apply lifecycle configuration
aws s3api put-bucket-lifecycle-configuration \
    --bucket my-bucket \
    --lifecycle-configuration file://lifecycle.json

# Get lifecycle configuration
aws s3api get-bucket-lifecycle-configuration --bucket my-bucket

Presigned URLs

# Generate presigned URL for download (1 hour expiry)
aws s3 presign s3://my-bucket/myfile.txt --expires-in 3600

# Note: `aws s3 presign` only generates GET (download) URLs.
# For upload (PUT) presigned URLs, use an SDK, e.g. boto3:
# boto3.client('s3').generate_presigned_url('put_object',
#     Params={'Bucket': 'my-bucket', 'Key': 'upload-path/file.txt'},
#     ExpiresIn=3600)

IAM Basics

IAM Architecture

ResourcesIAM ComponentsAssumeAssumeIAM UserIAM GroupIAM RoleIAM PolicyEC2S3LambdaResourcesIAM ComponentsAssumeAssumeIAM UserIAM GroupIAM RoleIAM PolicyEC2S3Lambda

User Management

# Create user
aws iam create-user --user-name john.smith

# Create access keys for user
aws iam create-access-key --user-name john.smith

# List users
aws iam list-users

# Add user to group
aws iam add-user-to-group \
    --user-name john.smith \
    --group-name developers

# Delete user
aws iam delete-user --user-name john.smith

Role Management

# Create trust policy file
cat > trust-policy.json << 'EOF'
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "Service": "ec2.amazonaws.com"
            },
            "Action": "sts:AssumeRole"
        }
    ]
}
EOF

# Create role
aws iam create-role \
    --role-name MyEC2Role \
    --assume-role-policy-document file://trust-policy.json

# Attach managed policy to role
aws iam attach-role-policy \
    --role-name MyEC2Role \
    --policy-arn arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess

# Create instance profile (for EC2)
aws iam create-instance-profile --instance-profile-name MyEC2Profile

# Add role to instance profile
aws iam add-role-to-instance-profile \
    --instance-profile-name MyEC2Profile \
    --role-name MyEC2Role

# Assume role (get temporary credentials)
aws sts assume-role \
    --role-arn arn:aws:iam::123456789012:role/MyRole \
    --role-session-name MySession

Policy Management

# Create policy document
cat > s3-policy.json << 'EOF'
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "s3:GetObject",
                "s3:PutObject",
                "s3:ListBucket"
            ],
            "Resource": [
                "arn:aws:s3:::my-bucket",
                "arn:aws:s3:::my-bucket/*"
            ]
        }
    ]
}
EOF

# Create custom policy
aws iam create-policy \
    --policy-name MyS3Policy \
    --policy-document file://s3-policy.json

# Attach policy to user
aws iam attach-user-policy \
    --user-name john.smith \
    --policy-arn arn:aws:iam::123456789012:policy/MyS3Policy

# List attached policies for user
aws iam list-attached-user-policies --user-name john.smith

# Get policy details
aws iam get-policy \
    --policy-arn arn:aws:iam::123456789012:policy/MyS3Policy

VPC Fundamentals

VPC Architecture

VPC 10.0.0.0/16Private Subnet 10.0.3.0/24Private Subnet 10.0.2.0/24Public Subnet 10.0.1.0/24NAT GatewayApplication LBBastion HostEC2 InstanceEC2 InstanceRDS DatabaseInternet GatewayInternetVPC 10.0.0.0/16Private Subnet 10.0.3.0/24Private Subnet 10.0.2.0/24Public Subnet 10.0.1.0/24NAT GatewayApplication LBBastion HostEC2 InstanceEC2 InstanceRDS DatabaseInternet GatewayInternet

VPC and Subnet Creation

# Create VPC
aws ec2 create-vpc \
    --cidr-block 10.0.0.0/16 \
    --tag-specifications 'ResourceType=vpc,Tags=[{Key=Name,Value=MyVPC}]'

# Enable DNS hostnames
aws ec2 modify-vpc-attribute \
    --vpc-id vpc-1234567890abcdef0 \
    --enable-dns-hostnames

# Create public subnet
aws ec2 create-subnet \
    --vpc-id vpc-1234567890abcdef0 \
    --cidr-block 10.0.1.0/24 \
    --availability-zone eu-west-1a \
    --tag-specifications 'ResourceType=subnet,Tags=[{Key=Name,Value=PublicSubnet}]'

# Create private subnet
aws ec2 create-subnet \
    --vpc-id vpc-1234567890abcdef0 \
    --cidr-block 10.0.2.0/24 \
    --availability-zone eu-west-1a \
    --tag-specifications 'ResourceType=subnet,Tags=[{Key=Name,Value=PrivateSubnet}]'

# Enable auto-assign public IP for public subnet
aws ec2 modify-subnet-attribute \
    --subnet-id subnet-1234567890abcdef0 \
    --map-public-ip-on-launch

Internet Gateway and NAT Gateway

# Create internet gateway
aws ec2 create-internet-gateway \
    --tag-specifications 'ResourceType=internet-gateway,Tags=[{Key=Name,Value=MyIGW}]'

# Attach to VPC
aws ec2 attach-internet-gateway \
    --internet-gateway-id igw-1234567890abcdef0 \
    --vpc-id vpc-1234567890abcdef0

# Allocate Elastic IP for NAT Gateway
aws ec2 allocate-address --domain vpc

# Create NAT gateway in public subnet
aws ec2 create-nat-gateway \
    --subnet-id subnet-1234567890abcdef0 \
    --allocation-id eipalloc-1234567890abcdef0 \
    --tag-specifications 'ResourceType=natgateway,Tags=[{Key=Name,Value=MyNAT}]'

Route Tables

# Create route table
aws ec2 create-route-table \
    --vpc-id vpc-1234567890abcdef0 \
    --tag-specifications 'ResourceType=route-table,Tags=[{Key=Name,Value=PublicRT}]'

# Add route to internet gateway (for public subnet)
aws ec2 create-route \
    --route-table-id rtb-1234567890abcdef0 \
    --destination-cidr-block 0.0.0.0/0 \
    --gateway-id igw-1234567890abcdef0

# Add route to NAT gateway (for private subnet)
aws ec2 create-route \
    --route-table-id rtb-0987654321fedcba0 \
    --destination-cidr-block 0.0.0.0/0 \
    --nat-gateway-id nat-1234567890abcdef0

# Associate route table with subnet
aws ec2 associate-route-table \
    --route-table-id rtb-1234567890abcdef0 \
    --subnet-id subnet-1234567890abcdef0

# List route tables
aws ec2 describe-route-tables \
    --filters "Name=vpc-id,Values=vpc-1234567890abcdef0"

ECS/EKS Basics

ECS Cluster Management

# Create ECS cluster
aws ecs create-cluster --cluster-name my-cluster

# List clusters
aws ecs list-clusters

# Describe cluster
aws ecs describe-clusters --clusters my-cluster

Task Definitions

# Register task definition
cat > task-def.json << 'EOF'
{
    "family": "my-app",
    "networkMode": "awsvpc",
    "requiresCompatibilities": ["FARGATE"],
    "cpu": "256",
    "memory": "512",
    "executionRoleArn": "arn:aws:iam::123456789012:role/ecsTaskExecutionRole",
    "containerDefinitions": [
        {
            "name": "my-container",
            "image": "nginx:latest",
            "essential": true,
            "portMappings": [
                {
                    "containerPort": 80,
                    "protocol": "tcp"
                }
            ],
            "logConfiguration": {
                "logDriver": "awslogs",
                "options": {
                    "awslogs-group": "/ecs/my-app",
                    "awslogs-region": "eu-west-1",
                    "awslogs-stream-prefix": "ecs"
                }
            }
        }
    ]
}
EOF

aws ecs register-task-definition --cli-input-json file://task-def.json

# List task definitions
aws ecs list-task-definitions --family-prefix my-app

# Deregister task definition
aws ecs deregister-task-definition --task-definition my-app:1

ECS Services

# Create service
aws ecs create-service \
    --cluster my-cluster \
    --service-name my-service \
    --task-definition my-app:1 \
    --desired-count 2 \
    --launch-type FARGATE \
    --network-configuration "awsvpcConfiguration={subnets=[subnet-12345],securityGroups=[sg-12345],assignPublicIp=ENABLED}"

# Update service (deploy new task definition)
aws ecs update-service \
    --cluster my-cluster \
    --service my-service \
    --task-definition my-app:2 \
    --desired-count 3

# List services
aws ecs list-services --cluster my-cluster

# Describe service
aws ecs describe-services \
    --cluster my-cluster \
    --services my-service

EKS Cluster Management

# Create EKS cluster
aws eks create-cluster \
    --name my-eks-cluster \
    --role-arn arn:aws:iam::123456789012:role/EKSClusterRole \
    --resources-vpc-config subnetIds=subnet-12345,subnet-67890,securityGroupIds=sg-12345

# Update kubeconfig for kubectl
aws eks update-kubeconfig --name my-eks-cluster --region eu-west-1

# List clusters
aws eks list-clusters

# Describe cluster
aws eks describe-cluster --name my-eks-cluster

# Create node group
aws eks create-nodegroup \
    --cluster-name my-eks-cluster \
    --nodegroup-name my-nodes \
    --node-role arn:aws:iam::123456789012:role/EKSNodeRole \
    --subnets subnet-12345 subnet-67890 \
    --instance-types t3.medium \
    --scaling-config minSize=1,maxSize=3,desiredSize=2

Lambda Functions

Serverless Architecture

CloudWatchS3DynamoDBLambdaAPI GatewayClientCloudWatchS3DynamoDBLambdaAPI GatewayClientHTTP RequestInvoke FunctionRead/Write DataStore/Retrieve FilesLog EventsResponseHTTP ResponseCloudWatchS3DynamoDBLambdaAPI GatewayClientCloudWatchS3DynamoDBLambdaAPI GatewayClientHTTP RequestInvoke FunctionRead/Write DataStore/Retrieve FilesLog EventsResponseHTTP Response

Creating and Managing Functions

# Create deployment package
zip function.zip lambda_function.py

# Create Lambda function
# (python3.11 runs on Amazon Linux 2; prefer the AL2023-based
# python3.12/3.13 runtimes for new functions)
aws lambda create-function \
    --function-name my-function \
    --runtime python3.13 \
    --role arn:aws:iam::123456789012:role/LambdaExecutionRole \
    --handler lambda_function.handler \
    --zip-file fileb://function.zip \
    --timeout 30 \
    --memory-size 256 \
    --environment "Variables={ENV=production,LOG_LEVEL=INFO}"

# Update function code
aws lambda update-function-code \
    --function-name my-function \
    --zip-file fileb://function.zip

# Update function configuration
aws lambda update-function-configuration \
    --function-name my-function \
    --timeout 60 \
    --memory-size 512

# List functions
aws lambda list-functions

# Get function details
aws lambda get-function --function-name my-function

Invoking Functions

# Invoke synchronously
aws lambda invoke \
    --function-name my-function \
    --payload '{"key": "value"}' \
    --cli-binary-format raw-in-base64-out \
    response.json

# Invoke asynchronously
aws lambda invoke \
    --function-name my-function \
    --invocation-type Event \
    --payload '{"key": "value"}' \
    --cli-binary-format raw-in-base64-out \
    response.json

# Invoke with log output
aws lambda invoke \
    --function-name my-function \
    --payload '{"key": "value"}' \
    --cli-binary-format raw-in-base64-out \
    --log-type Tail \
    --query 'LogResult' \
    --output text response.json | base64 --decode

Event Source Mappings

# Create SQS trigger
aws lambda create-event-source-mapping \
    --function-name my-function \
    --event-source-arn arn:aws:sqs:eu-west-1:123456789012:my-queue \
    --batch-size 10

# Create DynamoDB Streams trigger
aws lambda create-event-source-mapping \
    --function-name my-function \
    --event-source-arn arn:aws:dynamodb:eu-west-1:123456789012:table/my-table/stream/2023-01-01T00:00:00.000 \
    --batch-size 100 \
    --starting-position LATEST

# List event source mappings
aws lambda list-event-source-mappings --function-name my-function

Function Versions and Aliases

# Publish version
aws lambda publish-version \
    --function-name my-function \
    --description "Production release v1.0"

# Create alias
aws lambda create-alias \
    --function-name my-function \
    --name prod \
    --function-version 1

# Update alias to new version
aws lambda update-alias \
    --function-name my-function \
    --name prod \
    --function-version 2

CloudWatch Logs and Metrics

Log Management

# Create log group
aws logs create-log-group --log-group-name /aws/my-app

# Set retention policy
aws logs put-retention-policy \
    --log-group-name /aws/my-app \
    --retention-in-days 30

# List log groups
aws logs describe-log-groups --log-group-name-prefix /aws/

# Get log streams
aws logs describe-log-streams \
    --log-group-name /aws/lambda/my-function \
    --order-by LastEventTime \
    --descending

# Get log events
aws logs get-log-events \
    --log-group-name /aws/lambda/my-function \
    --log-stream-name '2023/01/01/[$LATEST]abc123'

# Filter log events (search logs)
aws logs filter-log-events \
    --log-group-name /aws/lambda/my-function \
    --filter-pattern "ERROR" \
    --start-time $(date -d '1 hour ago' +%s000)

# Tail logs in real-time (built into AWS CLI v2)
aws logs tail /aws/lambda/my-function --follow

CloudWatch Insights Queries

# Run Insights query
aws logs start-query \
    --log-group-name /aws/lambda/my-function \
    --start-time $(date -d '1 day ago' +%s) \
    --end-time $(date +%s) \
    --query-string 'fields @timestamp, @message | filter @message like /ERROR/ | sort @timestamp desc | limit 20'

# Get query results
aws logs get-query-results --query-id abc123-def456

Custom Metrics

# Put custom metric
aws cloudwatch put-metric-data \
    --namespace "MyApp" \
    --metric-name "ProcessedItems" \
    --value 100 \
    --unit Count \
    --dimensions Environment=production,Service=api

# Get metric statistics
aws cloudwatch get-metric-statistics \
    --namespace AWS/Lambda \
    --metric-name Invocations \
    --dimensions Name=FunctionName,Value=my-function \
    --start-time $(date -d '1 hour ago' -u +%Y-%m-%dT%H:%M:%SZ) \
    --end-time $(date -u +%Y-%m-%dT%H:%M:%SZ) \
    --period 300 \
    --statistics Sum Average

CloudWatch Alarms

# Create alarm
aws cloudwatch put-metric-alarm \
    --alarm-name high-cpu-alarm \
    --alarm-description "Alarm when CPU exceeds 80%" \
    --metric-name CPUUtilization \
    --namespace AWS/EC2 \
    --statistic Average \
    --period 300 \
    --threshold 80 \
    --comparison-operator GreaterThanThreshold \
    --dimensions Name=InstanceId,Value=i-1234567890abcdef0 \
    --evaluation-periods 2 \
    --alarm-actions arn:aws:sns:eu-west-1:123456789012:my-topic

# List alarms
aws cloudwatch describe-alarms --alarm-name-prefix "high-"

# Delete alarm
aws cloudwatch delete-alarms --alarm-names high-cpu-alarm

DynamoDB Basics

Table Management

# Create table
aws dynamodb create-table \
    --table-name Users \
    --attribute-definitions \
        AttributeName=userId,AttributeType=S \
        AttributeName=email,AttributeType=S \
    --key-schema \
        AttributeName=userId,KeyType=HASH \
    --global-secondary-indexes \
        "IndexName=EmailIndex,KeySchema=[{AttributeName=email,KeyType=HASH}],Projection={ProjectionType=ALL},ProvisionedThroughput={ReadCapacityUnits=5,WriteCapacityUnits=5}" \
    --provisioned-throughput ReadCapacityUnits=5,WriteCapacityUnits=5

# Create on-demand table
aws dynamodb create-table \
    --table-name Orders \
    --attribute-definitions AttributeName=orderId,AttributeType=S \
    --key-schema AttributeName=orderId,KeyType=HASH \
    --billing-mode PAY_PER_REQUEST

# List tables
aws dynamodb list-tables

# Describe table
aws dynamodb describe-table --table-name Users

# Delete table
aws dynamodb delete-table --table-name Users

Read and Write Operations

# Put item
aws dynamodb put-item \
    --table-name Users \
    --item '{
        "userId": {"S": "user123"},
        "email": {"S": "john@example.com"},
        "name": {"S": "John Smith"},
        "age": {"N": "30"},
        "active": {"BOOL": true}
    }'

# Get item
aws dynamodb get-item \
    --table-name Users \
    --key '{"userId": {"S": "user123"}}'

# Update item
aws dynamodb update-item \
    --table-name Users \
    --key '{"userId": {"S": "user123"}}' \
    --update-expression "SET age = :newage, #n = :newname" \
    --expression-attribute-names '{"#n": "name"}' \
    --expression-attribute-values '{":newage": {"N": "31"}, ":newname": {"S": "John D. Smith"}}'

# Delete item
aws dynamodb delete-item \
    --table-name Users \
    --key '{"userId": {"S": "user123"}}'

# Batch write items
aws dynamodb batch-write-item --request-items file://batch-items.json

Queries and Scans

# Query by partition key
aws dynamodb query \
    --table-name Users \
    --key-condition-expression "userId = :uid" \
    --expression-attribute-values '{":uid": {"S": "user123"}}'

# Query with filter
aws dynamodb query \
    --table-name Orders \
    --key-condition-expression "customerId = :cid" \
    --filter-expression "orderStatus = :status" \
    --expression-attribute-values '{
        ":cid": {"S": "cust123"},
        ":status": {"S": "PENDING"}
    }'

# Query GSI
aws dynamodb query \
    --table-name Users \
    --index-name EmailIndex \
    --key-condition-expression "email = :email" \
    --expression-attribute-values '{":email": {"S": "john@example.com"}}'

# Scan with filter (use sparingly)
aws dynamodb scan \
    --table-name Users \
    --filter-expression "age > :minage" \
    --expression-attribute-values '{":minage": {"N": "25"}}'

RDS Basics

Instance Management

# Create RDS instance
aws rds create-db-instance \
    --db-instance-identifier my-postgres \
    --db-instance-class db.t3.micro \
    --engine postgres \
    --engine-version 17.9 \
    --master-username admin \
    --master-user-password 'MySecurePassword123!' \
    --allocated-storage 20 \
    --storage-type gp3 \
    --vpc-security-group-ids sg-12345678 \
    --db-subnet-group-name my-subnet-group \
    --backup-retention-period 7 \
    --multi-az \
    --publicly-accessible

# List instances
aws rds describe-db-instances \
    --query 'DBInstances[*].{ID:DBInstanceIdentifier,Status:DBInstanceStatus,Engine:Engine,Endpoint:Endpoint.Address}'

# Get connection endpoint
aws rds describe-db-instances \
    --db-instance-identifier my-postgres \
    --query 'DBInstances[0].Endpoint.Address' \
    --output text

# Modify instance
aws rds modify-db-instance \
    --db-instance-identifier my-postgres \
    --db-instance-class db.t3.small \
    --apply-immediately

# Stop instance
aws rds stop-db-instance --db-instance-identifier my-postgres

# Start instance
aws rds start-db-instance --db-instance-identifier my-postgres

# Delete instance
aws rds delete-db-instance \
    --db-instance-identifier my-postgres \
    --skip-final-snapshot

Subnet Groups

# Create DB subnet group
aws rds create-db-subnet-group \
    --db-subnet-group-name my-subnet-group \
    --db-subnet-group-description "My DB subnet group" \
    --subnet-ids subnet-12345678 subnet-87654321

# List subnet groups
aws rds describe-db-subnet-groups

Backups and Snapshots

# Create manual snapshot
aws rds create-db-snapshot \
    --db-instance-identifier my-postgres \
    --db-snapshot-identifier my-postgres-backup-$(date +%Y%m%d)

# List snapshots
aws rds describe-db-snapshots \
    --db-instance-identifier my-postgres

# Restore from snapshot
aws rds restore-db-instance-from-db-snapshot \
    --db-instance-identifier my-postgres-restored \
    --db-snapshot-identifier my-postgres-backup-20230101

# Copy snapshot to another region
aws rds copy-db-snapshot \
    --source-db-snapshot-identifier arn:aws:rds:eu-west-1:123456789012:snapshot:my-snapshot \
    --target-db-snapshot-identifier my-snapshot-copy \
    --source-region eu-west-1 \
    --region eu-west-2

Parameter Groups

# Create parameter group
aws rds create-db-parameter-group \
    --db-parameter-group-name my-postgres-params \
    --db-parameter-group-family postgres17 \
    --description "Custom PostgreSQL parameters"

# Modify parameters
aws rds modify-db-parameter-group \
    --db-parameter-group-name my-postgres-params \
    --parameters "ParameterName=max_connections,ParameterValue=200,ApplyMethod=pending-reboot" \
                 "ParameterName=shared_buffers,ParameterValue=256MB,ApplyMethod=pending-reboot"

# Apply parameter group to instance
aws rds modify-db-instance \
    --db-instance-identifier my-postgres \
    --db-parameter-group-name my-postgres-params \
    --apply-immediately

Quick Reference

Essential Commands

Task Command
Configure CLI aws configure
Verify identity aws sts get-caller-identity
Switch profile export AWS_PROFILE=myprofile
List EC2 instances aws ec2 describe-instances --query 'Reservations[].Instances[].{ID:InstanceId,State:State.Name}'
SSH to EC2 ssh -i key.pem ec2-user@<ip>
List S3 buckets aws s3 ls
Sync to S3 aws s3 sync ./local s3://bucket/path
Invoke Lambda aws lambda invoke --function-name fn --payload '{}' out.json
Tail CloudWatch logs aws logs tail /aws/lambda/fn --follow
Get RDS endpoint aws rds describe-db-instances --db-instance-identifier mydb --query 'DBInstances[0].Endpoint.Address'
Query DynamoDB aws dynamodb query --table-name tbl --key-condition-expression "pk = :v" --expression-attribute-values '{":v":{"S":"val"}}'

Common Filters and Queries

# Get running EC2 instance IDs
aws ec2 describe-instances \
    --filters "Name=instance-state-name,Values=running" \
    --query 'Reservations[].Instances[].InstanceId' \
    --output text

# Get Lambda function names
aws lambda list-functions \
    --query 'Functions[].FunctionName' \
    --output text

# Get security group rules
aws ec2 describe-security-groups \
    --group-ids sg-12345678 \
    --query 'SecurityGroups[0].IpPermissions'

# Get latest Amazon Linux 2023 AMI (via SSM public parameter)
aws ssm get-parameter \
    --name /aws/service/ami-amazon-linux-latest/al2023-ami-kernel-default-x86_64 \
    --query 'Parameter.Value' \
    --output text

# Or via describe-images (AL2 reached end of life on 30 June 2026)
aws ec2 describe-images \
    --owners amazon \
    --filters "Name=name,Values=al2023-ami-*-x86_64" \
    --query 'Images | sort_by(@, &CreationDate) | [-1].ImageId' \
    --output text

Common Issues and Solutions

Issue Cause Solution
Unable to locate credentials No credentials configured Run aws configure or set AWS_PROFILE
An error occurred (AccessDenied) Insufficient IAM permissions Check IAM policies, verify user/role has required permissions
An error occurred (InvalidParameterValue) Wrong region or resource ID Verify --region flag matches resource location
Request limit exceeded API throttling Implement exponential backoff, use pagination
EC2 instance unreachable Security group or VPC config Check security group rules, verify route tables and IGW
S3 bucket already exists Bucket names are globally unique Choose a different, unique bucket name
Lambda timeout Execution time exceeded Increase timeout (max 15 min), optimise code
RDS connection refused Security group or network Add inbound rule for port 5432/3306, check VPC routing
DynamoDB ProvisionedThroughputExceededException Capacity exceeded Enable auto-scaling or switch to on-demand
ECS task stuck in PENDING No resources or image pull failure Check cluster capacity, verify image name and ECR permissions

Debugging Commands

# Check AWS CLI configuration
aws configure list

# Verify current identity
aws sts get-caller-identity

# Test IAM permissions
aws iam simulate-principal-policy \
    --policy-source-arn arn:aws:iam::123456789012:user/john \
    --action-names s3:GetObject \
    --resource-arns arn:aws:s3:::my-bucket/*

# Check VPC flow logs for network issues
aws logs filter-log-events \
    --log-group-name /aws/vpc/flowlogs \
    --filter-pattern "REJECT"

# Get CloudTrail events for API troubleshooting
aws cloudtrail lookup-events \
    --lookup-attributes AttributeKey=EventName,AttributeValue=RunInstances

Related Topics

To extend your AWS knowledge and build comprehensive cloud infrastructure skills, consider exploring these related topics:

  • Terraform - Infrastructure as code for provisioning AWS resources declaratively
  • GCP/Azure - Alternative cloud platforms for multi-cloud strategies
  • Container Security - Securing ECS/EKS workloads with image scanning and runtime protection
  • Prometheus/Grafana - Enhanced monitoring and visualisation beyond CloudWatch
  • Ansible - Configuration management for EC2 instances and hybrid environments
  • CI/CD Patterns - Automated deployment pipelines with CodePipeline, CodeBuild, or Jenkins