AWS Practical CLI and Services Reference for Engineers
Hands-on AWS CLI commands, service quick reference, IAM best practices, S3 and EC2 tips for cloud practitioners and solutions architects.
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.
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 profileaws configure --profile prod# Use a specific profile for any commandaws s3 ls --profile prod# Set a profile for the entire shell sessionexport 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.
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.
Good IAM hygiene is the single highest-leverage security practice in AWS. These are the principles I apply on every account I manage.
Least Privilege — grant only what is needed
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.
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 instanceaws ec2 associate-iam-instance-profile \ --instance-id i-0abcd1234efgh5678 \ --iam-instance-profile Name=MyAppRole
Enforce MFA on human users
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.
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 OUaws 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).
# List all bucketsaws 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 bucketaws s3 rb s3://my-old-bucket# Force-delete a bucket and all its contentsaws s3 rb s3://my-old-bucket --force
# Upload a single fileaws s3 cp ./report.pdf s3://my-bucket/reports/report.pdf# Download a fileaws s3 cp s3://my-bucket/reports/report.pdf ./report.pdf# Sync a local directory to S3 (only changed files)aws s3 sync ./dist s3://my-static-site --delete# Sync with a specific storage classaws s3 sync ./backups s3://my-backups \ --storage-class STANDARD_IA# Move (copy + delete source)aws s3 mv s3://my-bucket/old-path/ s3://my-bucket/new-path/ \ --recursive
# 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 dateaws 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 messageaws 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.