Skip to main content
AWS is the backbone of most production infrastructure I work with day-to-day. These notes distill the commands and patterns I reach for most often — from wiring up the CLI on a fresh machine to auditing IAM policies and managing EC2 fleets. The goal is a fast lookup reference, not a comprehensive tutorial.

AWS CLI Setup

1

Install the AWS CLI

Download and install AWS CLI v2 for your platform.
brew install awscli
2

Configure credentials

Run the interactive setup. You’ll need an Access Key ID and Secret from IAM.
aws configure
# 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
Credentials are stored in ~/.aws/credentials; config in ~/.aws/config.
3

Use named profiles

Managing multiple accounts is much cleaner with named profiles.
# Configure a named profile
aws configure --profile prod

# Use a specific profile for any command
aws s3 ls --profile prod

# Set a profile for the entire shell session
export AWS_PROFILE=prod
4

Verify the active identity

aws sts get-caller-identity
# Returns Account, UserId, and ARN — great sanity check before destructive ops
Store long-term credentials only in non-production tooling profiles. For production workloads running on EC2 or Lambda, always use IAM roles attached to the compute resource — no static keys needed.

Key Services Quick Reference

EC2

Virtual machines in the cloud. Choose instance families based on workload: t3/t4g for burstable dev, m6i for general-purpose, c6i for compute-heavy, r6i for memory-intensive apps.

S3

Object storage with 11 nines durability. Used for backups, static sites, data lakes, Lambda deployment packages, Terraform state, and log archives.

IAM

Identity and Access Management controls who can do what across all AWS services. Covers users, groups, roles, and policies (identity-based and resource-based).

VPC

Your private network in AWS. Define subnets (public/private), route tables, internet gateways, NAT gateways, security groups, and NACLs.

Lambda

Serverless compute. Run code in response to events (API Gateway, S3, SQS, EventBridge). Billed per invocation and duration (100ms increments).

RDS

Managed relational databases: PostgreSQL, MySQL, MariaDB, Oracle, SQL Server, and Aurora. Handles backups, patching, and failover automatically.

CloudWatch

Metrics, logs, alarms, and dashboards. Use metric filters to turn log patterns into actionable alerts, and Container Insights for ECS/EKS observability.

CloudTrail

API audit log for every call made to your account. Essential for security investigations, compliance, and change tracking. Enable in all regions.

IAM Best Practices

Good IAM hygiene is the single highest-leverage security practice in AWS. These are the principles I apply on every account I manage.
Start with a deny-all posture and add only the permissions required. Use IAM Access Analyzer and the last-accessed data in IAM to identify and remove unused permissions over time.
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "s3:GetObject",
        "s3:PutObject"
      ],
      "Resource": "arn:aws:s3:::my-app-bucket/*"
    }
  ]
}
EC2 instances, Lambda functions, ECS tasks, and CI/CD pipelines should all authenticate via IAM roles, not static access keys. Roles use temporary credentials rotated automatically by STS.
# Attach an instance profile (role) to a running EC2 instance
aws ec2 associate-iam-instance-profile \
  --instance-id i-0abcd1234efgh5678 \
  --iam-instance-profile Name=MyAppRole
Require MFA for the root account immediately. Enforce MFA on all IAM users via an SCP (if using AWS Organizations) or a conditional IAM policy.
{
  "Effect": "Deny",
  "NotAction": [
    "iam:CreateVirtualMFADevice",
    "iam:EnableMFADevice",
    "iam:GetUser",
    "iam:ListMFADevices",
    "iam:ListVirtualMFADevices",
    "sts:GetSessionToken"
  ],
  "Resource": "*",
  "Condition": {
    "BoolIfExists": {
      "aws:MultiFactorAuthPresent": "false"
    }
  }
}
Permission boundaries cap the maximum permissions a role or user can have, even if their attached policies are overly broad. Service Control Policies (SCPs) in AWS Organizations apply account-wide guardrails — great for preventing region sprawl or blocking specific risky actions.
# List SCPs attached to an OU
aws organizations list-policies-for-target \
  --target-id ou-xxxx-yyyyyyyy \
  --filter SERVICE_CONTROL_POLICY
Never use the root account for day-to-day work. Lock it down with MFA, delete or disable root access keys, and only access it for tasks that genuinely require root (a very short list).

S3 Operations

# List all buckets
aws s3 ls

# Create a bucket (region must match your config or be specified)
aws s3 mb s3://my-new-bucket --region us-east-1

# List objects in a bucket (with sizes)
aws s3 ls s3://my-bucket --human-readable --recursive

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

# Force-delete a bucket and all its contents
aws s3 rb s3://my-old-bucket --force

EC2 Instance Management

1

Find the right AMI

# Find latest Amazon Linux 2023 AMI in us-east-1
aws ec2 describe-images \
  --owners amazon \
  --filters "Name=name,Values=al2023-ami-*-x86_64" \
            "Name=state,Values=available" \
  --query "sort_by(Images, &CreationDate)[-1].ImageId" \
  --output text
2

Launch an instance

aws ec2 run-instances \
  --image-id ami-0abcdef1234567890 \
  --instance-type t3.micro \
  --key-name my-key-pair \
  --security-group-ids sg-0123456789abcdef0 \
  --subnet-id subnet-0123456789abcdef0 \
  --iam-instance-profile Name=MyAppRole \
  --tag-specifications 'ResourceType=instance,Tags=[
    {Key=Name,Value=web-server-01},
    {Key=Env,Value=prod},
    {Key=Owner,Value=platform-team}
  ]' \
  --count 1
3

Common instance operations

# List running instances with Name tag and private IP
aws ec2 describe-instances \
  --filters "Name=instance-state-name,Values=running" \
  --query "Reservations[*].Instances[*].{
    Name:Tags[?Key=='Name']|[0].Value,
    ID:InstanceId,
    Type:InstanceType,
    IP:PrivateIpAddress,
    State:State.Name
  }" \
  --output table

# Stop / start / reboot
aws ec2 stop-instances --instance-ids i-0abcd1234efgh5678
aws ec2 start-instances --instance-ids i-0abcd1234efgh5678
aws ec2 reboot-instances --instance-ids i-0abcd1234efgh5678

# Terminate (irreversible)
aws ec2 terminate-instances --instance-ids i-0abcd1234efgh5678
4

Connect with SSM Session Manager

Skip bastion hosts and open SSH ports entirely. SSM Session Manager gives browser or CLI shell access through IAM authentication.
# Requires AmazonSSMManagedInstanceCore policy on the instance role
aws ssm start-session --target i-0abcd1234efgh5678

# Port-forward RDS or internal service to localhost
aws ssm start-session \
  --target i-0abcd1234efgh5678 \
  --document-name AWS-StartPortForwardingSession \
  --parameters '{"portNumber":["5432"],"localPortNumber":["5432"]}'

CloudWatch Essentials

# Tail a log group in real time (like `tail -f`)
aws logs tail /aws/lambda/my-function --follow

# Filter logs for ERROR entries in the last hour
aws logs filter-log-events \
  --log-group-name /aws/ecs/my-service \
  --filter-pattern "ERROR" \
  --start-time $(date -d '1 hour ago' +%s000)

# Get the latest CPU utilization metric for an EC2 instance
aws cloudwatch get-metric-statistics \
  --namespace AWS/EC2 \
  --metric-name CPUUtilization \
  --dimensions Name=InstanceId,Value=i-0abcd1234efgh5678 \
  --start-time $(date -u -d '30 minutes ago' +%FT%TZ) \
  --end-time $(date -u +%FT%TZ) \
  --period 300 \
  --statistics Average \
  --output table

# Create a simple alarm — email when CPU > 80% for 2 periods
aws cloudwatch put-metric-alarm \
  --alarm-name high-cpu-web-01 \
  --metric-name CPUUtilization \
  --namespace AWS/EC2 \
  --statistic Average \
  --period 300 \
  --threshold 80 \
  --comparison-operator GreaterThanThreshold \
  --evaluation-periods 2 \
  --alarm-actions arn:aws:sns:us-east-1:123456789012:alerts \
  --dimensions Name=InstanceId,Value=i-0abcd1234efgh5678

Useful One-Liners

# Find all unattached EBS volumes (potential waste)
aws ec2 describe-volumes \
  --filters Name=status,Values=available \
  --query "Volumes[*].{ID:VolumeId,Size:Size,AZ:AvailabilityZone}" \
  --output table

# List all IAM users with their last-used date
aws iam list-users \
  --query "Users[*].{User:UserName,Created:CreateDate}" \
  --output table

# Find security groups with unrestricted SSH (0.0.0.0/0 on port 22)
aws ec2 describe-security-groups \
  --filters Name=ip-permission.from-port,Values=22 \
            Name=ip-permission.to-port,Values=22 \
            Name=ip-permission.cidr,Values='0.0.0.0/0' \
  --query "SecurityGroups[*].{ID:GroupId,Name:GroupName,VPC:VpcId}" \
  --output table

# Get storage size for a specific S3 bucket (BucketSizeBytes requires BucketName + StorageType)
aws cloudwatch get-metric-statistics \
  --namespace AWS/S3 \
  --metric-name BucketSizeBytes \
  --dimensions Name=BucketName,Value=my-bucket \
              Name=StorageType,Value=StandardStorage \
  --start-time $(date -d '2 days ago' +%F) \
  --end-time $(date +%F) \
  --period 86400 --statistics Average

# Decode an encoded authorization failure message
aws sts decode-authorization-message \
  --encoded-message <paste-encoded-message-here> \
  --query DecodedMessage --output text | python3 -m json.tool
Always run destructive commands (terminate-instances, rb --force, delete-*) against a dry-run first where supported (--dry-run flag), or at minimum double-check the target resource IDs. A misplaced --recursive or wrong account profile has caused real production incidents.

Terraform

Provision and manage AWS resources as code with Terraform — state, modules, and real-world patterns.

FinOps & Cost Management

AWS Cost Explorer, Budgets, rightsizing, and tooling notes for keeping cloud spend under control.

GCP & Azure Reference

Quick reference for GCP and Azure CLI, key services, and cross-cloud comparisons.

Kubernetes

Container orchestration on EKS and beyond — deployments, networking, and operations.
Last modified on June 9, 2026