AWS Architecture
How AWS is structured for production work: accounts and regions as blast-radius boundaries, the IAM evaluation model, VPC networking, and the failure modes that catch teams building on it.
This reference describes how to design and implement AWS infrastructure patterns that are specific to the platform's capabilities and constraints. It covers deployment, networking, security, and operational practices that directly leverage AWS services rather than generic cloud concepts. This information applies when building systems that must interoperate with AWS-specific APIs, IAM policies, VPC configurations, or service integrations.
Design for Availability and Fault Isolation
Multi-AZ Deployments with Auto Scaling
Create an Auto Scaling group that spans multiple availability zones using the --availability-zones parameter:
aws autoscaling create-auto-scaling-group \
--auto-scaling-group-name my-app-asg \
--launch-template LaunchTemplateName=my-launch-template \
--min-size 2 \
--max-size 10 \
--desired-capacity 4 \
--availability-zones us-east-1a us-east-1b us-east-1c
Configure health checks with --health-check-type set to EC2:
aws autoscaling update-auto-scaling-group \
--auto-scaling-group-name my-app-asg \
--health-check-type EC2 \
--health-check-grace-period 300
Failure mode: If the launch template references an AMI that does not exist in all target AZs, instances fail to launch with InvalidAMIId.NotFound errors. The ASG will not auto-heal until you correct the AMI reference.
Network Isolation with VPC Subnets
Create a VPC with public and private subnets using the --cidr-block parameter:
aws ec2 create-vpc \
--cidr-block 10.0.0.0/16 \
--tag-specifications 'ResourceType=vpc,Tags=[{Key=Name,Value=my-vpc}]'
Create public subnet with automatic public IP assignment:
aws ec2 create-subnet \
--vpc-id vpc-12345678 \
--cidr-block 10.0.1.0/24 \
--availability-zone us-east-1a \
--map-public-ip-on-launch
Failure mode: When using --map-public-ip-on-launch, instances launched in public subnets will receive public IPs, but if you later modify the subnet to remove this flag, existing instances retain their public IPs. This can cause unexpected connectivity patterns.
IAM and Access Control
Service Role for EC2 Instances
Create an IAM role with a trust policy that allows EC2 service to assume the role:
aws iam create-role \
--role-name my-ec2-role \
--assume-role-policy-document file://trust-policy.json
Where trust-policy.json contains:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "ec2.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
}
Attach a managed policy to the role:
aws iam attach-role-policy \
--role-name my-ec2-role \
--policy-arn arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess
Failure mode: If you create an instance profile without attaching it to the role, the EC2 instances will not be able to assume the role even though the trust policy is correctly configured. The error message ClientError: InvalidParameterValue occurs when trying to associate a non-existent instance profile.
Data Storage Patterns
S3 Bucket with Versioning and Lifecycle Policies
Create a versioned bucket:
aws s3api create-bucket \
--bucket my-app-bucket \
--region us-east-1
Enable versioning:
aws s3api put-bucket-versioning \
--bucket my-app-bucket \
--versioning-configuration Status=Enabled
Set lifecycle policy to transition older versions to Glacier:
aws s3api put-bucket-lifecycle-configuration \
--bucket my-app-bucket \
--lifecycle-configuration file://lifecycle.json
Where lifecycle.json contains:
{
"Rules": [
{
"ID": "TransitionOldVersions",
"Status": "Enabled",
"Filter": {},
"NoncurrentVersionTransitions": [
{
"StorageClass": "GLACIER",
"Days": 30
}
]
}
]
}
Failure mode: If you enable versioning on a bucket that already contains objects, the existing objects remain unversioned. New versions are created only for objects written after enabling versioning. The VersionId field in S3 responses will be empty for pre-existing objects.
Load Balancer and Routing
Application Load Balancer with Target Groups
Create a target group:
aws elbv2 create-target-group \
--name my-target-group \
--protocol HTTP \
--port 80 \
--vpc-id vpc-12345678 \
--health-check-path /health
Register targets with specific port mapping:
aws elbv2 register-targets \
--target-group-arn arn:aws:elasticloadbalancing:us-east-1:123456789012:targetgroup/my-target-group/1234567890123456 \
--targets Id=i-1234567890abcdef0,Port=80 Id=i-1234567890abcdef1,Port=80
Failure mode: When registering targets with the wrong port, ALB returns HTTP 503 errors without clear indication that the target port does not match the target group configuration. The error appears in CloudWatch logs as Target group unhealthy.
Monitoring and Observability
CloudWatch Alarms for Auto Scaling Groups
Create an alarm for CPU utilization:
aws cloudwatch put-metric-alarm \
--alarm-name cpu-utilization-high \
--alarm-description "High CPU utilization on ASG" \
--metric-name CPUUtilization \
--namespace AWS/EC2 \
--statistic Average \
--period 300 \
--threshold 70.0 \
--comparison-operator GreaterThanThreshold \
--evaluation-periods 2 \
--dimensions Name=AutoScalingGroupName,Value=my-app-asg \
--alarm-actions arn:aws:sns:us-east-1:123456789012:my-topic
Failure mode: If you specify a CloudWatch alarm that references a dimension that doesn't exist on the metric, AWS will silently ignore the alarm or return an error when trying to update it. The alarm appears in the console but does not trigger correctly.
Network Access Control
Security Groups for Instance Communication
Create security group with ingress rules:
aws ec2 create-security-group \
--group-name my-app-sg \
--description "Security group for application servers" \
--vpc-id vpc-12345678
Add rule to allow SSH access:
aws ec2 authorize-security-group-ingress \
--group-id sg-12345678 \
--protocol tcp \
--port 22 \
--cidr 0.0.0.0/0
Failure mode: If you add a security group rule with a port range that includes ports not actually configured on the instance, traffic will be accepted by the security group but dropped by the instance itself. The rule appears valid in AWS console but produces no connection errors.
Configuration and Deployment
EC2 Launch Templates with User Data
Create launch template with user data script:
aws ec2 create-launch-template \
--launch-template-name my-launch-template \
--launch-template-data '{"ImageId":"ami-12345678","InstanceType":"t3.medium","UserData":"IyEvYmluL2Jhc2gKZWNobyAiSGVsbG8gV29ybGQiID4gL3RtcC9oZWxsby50eHQ="}'
The user data is base64 encoded script that runs on instance boot.
Failure mode: If the user data script fails to execute properly, EC2 instances will start without the expected configuration. The aws ec2 describe-instances command shows StateReason with message like Client.UserDataError when the user data script fails to parse or execute.
Cross-Service Integration
Lambda Function with S3 Event Notifications
Create S3 bucket notification configuration:
aws s3api put-bucket-notification-configuration \
--bucket my-app-bucket \
--notification-configuration file://notification.json
Where notification.json contains:
{
"LambdaFunctionConfigurations": [
{
"Id": "process-images",
"LambdaFunctionArn": "arn:aws:lambda:us-east-1:123456789012:function:process-image-function",
"Events": ["s3:ObjectCreated:*"],
"Filter": {
"Key": {
"FilterRules": [
{
"Name": "suffix",
"Value": ".jpg"
}
]
}
}
}
]
}
Failure mode: If the Lambda function does not have sufficient permissions to read from S3 or write to CloudWatch Logs, the event notification fails silently. The S3 event will appear in the bucket's event history but no Lambda invocation occurs.
Scaling and Performance
Auto Scaling with Custom Metrics
Create a custom CloudWatch metric for application performance:
aws cloudwatch put-metric-data \
--namespace MyApplication \
--metric-data '[{"MetricName":"RequestLatency","Value":250.0,"Unit":"Milliseconds"}]'
Configure scaling policy based on this metric:
aws autoscaling put-scaling-policy \
--policy-name latency-scale-out \
--auto-scaling-group-name my-app-asg \
--policy-type TargetTrackingScaling \
--target-tracking-configuration '{"PredefinedMetricSpecification":{"PredefinedMetricType":"ALBRequestCountPerTarget","ResourceLabel":"app/my-alb/1234567890123456"},"TargetValue":100.0}'
Failure mode: If you use a predefined metric type that doesn't match your actual resource configuration, the scaling policy will not function correctly. For example, using ALBRequestCountPerTarget for an ALB that doesn't exist or has mismatched target groups results in policy errors and no scaling action.
Troubleshooting Common Issues
Instance State and Health Checks
When debugging instance failures, check EC2 status:
aws ec2 describe-instances \
--instance-ids i-1234567890abcdef0 \
--query 'Reservations[].Instances[].{InstanceId:InstanceId,State:State.Name,Health:HealthStatus}'
If HealthStatus is impaired, check the instance's system logs:
aws ec2 get-console-output \
--instance-id i-1234567890abcdef0
Failure mode: Instances may appear healthy in EC2 console but fail to respond to load balancer health checks. This often occurs when the instance's application fails to start properly, even though the system is running. The health check will return HTTP 503 errors with no clear indication of the underlying application failure.
Resource Limits and Quotas
Check current limits for your account:
aws service-quotas list-service-quotas \
--service-code ec2
Failure mode: If you exceed VPC or subnet limits, new resources fail to create with ResourceLimitExceeded errors. These errors are not always clear about which specific limit was exceeded. You may need to check the AWS Console or use describe-account-attributes to identify the actual constraint.
This page was rewritten on 16 October 2026. It replaced a templated version that was not specific to its own title. How these pages are written and checked. If anything here is wrong, tell us at [email protected] and we will correct it.