Master cloud infrastructure fundamentals including compute resources, networking, storage, and security across AWS, Azure, and GCP for scalable application deployment
Cloud infrastructure forms the foundation of modern DevOps practices, providing on-demand, scalable resources for deploying and operating applications. Understanding infrastructure basics is essential for building reliable, cost-effective systems.
Why infrastructure knowledge matters:
Cloud Service Models:
Major Cloud Providers:
This lesson covers infrastructure fundamentals applicable across all major cloud providers, with specific examples from AWS, Azure, and GCP where relevant.
Cloud Deployment Models
Advantages:
Disadvantages:
Advantages:
Disadvantages:
Advantages:
Use cases:
Advantages:
Disadvantages:
Cloud Provider Comparison
1Service Category | AWS | Azure | GCP
2--------------------|----------------------|--------------------------|-------------------------
3Compute (VMs) | EC2 | Virtual Machines | Compute Engine
4Containers | ECS, EKS | AKS | GKE
5Serverless | Lambda | Functions | Cloud Functions
6Object Storage | S3 | Blob Storage | Cloud Storage
7Block Storage | EBS | Managed Disks | Persistent Disk
8Database (SQL) | RDS | SQL Database | Cloud SQL
9Database (NoSQL) | DynamoDB | Cosmos DB | Firestore
10Load Balancing | ELB/ALB | Load Balancer | Cloud Load Balancing
11CDN | CloudFront | Azure CDN | Cloud CDN
12DNS | Route 53 | Azure DNS | Cloud DNS
13Monitoring | CloudWatch | Monitor | Cloud Monitoring
14IAM | IAM | Active Directory | IAM
15Networking | VPC | Virtual Network | VPCChoosing a Cloud Provider
Consider these factors:
Getting Started Example (AWS):
1# Install AWS CLI
2curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
3unzip awscliv2.zip
4sudo ./aws/install
5
6# Configure credentials
7aws configure
8# AWS Access Key ID: YOUR_ACCESS_KEY
9# AWS Secret Access Key: YOUR_SECRET_KEY
10# Default region: us-east-1
11# Default output format: json
12
13# Verify configuration
14aws sts get-caller-identity
15
16# List regions
17aws ec2 describe-regions --output table
18
19# Check current region
20aws configure get regionGetting Started Example (Azure):
1# Install Azure CLI
2curl -sL https://aka.ms/InstallAzureCLIDeb | sudo bash
3
4# Login
5az login
6
7# Set subscription
8az account set --subscription "My Subscription"
9
10# List locations
11az account list-locations --output table
12
13# Set default location
14az configure --defaults location=eastusGetting Started Example (GCP):
1# Install gcloud CLI
2curl https://sdk.cloud.google.com | bash
3exec -l $SHELL
4
5# Initialize and authenticate
6gcloud init
7
8# Set project
9gcloud config set project my-project-id
10
11# List zones
12gcloud compute zones list
13
14# Set default zone
15gcloud config set compute/zone us-central1-aMulti-Cloud Management Tools:
Compute Options Spectrum:
1More Control Less Management
2 ↓ ↓
3 Bare Metal → VMs → Containers → Container Services → Serverless
4 ↑ ↑
5More Responsibility More Automation1. Virtual Machines (VMs)
Virtual machines provide complete control over the operating system and runtime environment.
When to use:
AWS EC2 Instance Types:
1Type Purpose Examples Typical Use Cases
2----------|--------------------|--------------------|---------------------------
3General Balanced t3, m5 Web servers, dev environments
4Compute CPU-intensive c5, c6 Batch processing, HPC
5Memory RAM-intensive r5, r6 Databases, caching
6Storage High disk I/O i3, d2 Data warehousing, Hadoop
7GPU Graphics/ML p3, p4, g4 ML training, renderingVM Best Practices:
✅ Right-size instances based on actual usage
✅ Use auto-scaling for variable workloads
✅ Leverage spot/preemptible instances for cost savings
✅ Implement automated patching and updates
✅ Use instance metadata service for configuration
✅ Enable detailed monitoring
✅ Tag resources for cost allocation
Creating a VM (AWS EC2):
1# Create EC2 instance with user data
2aws ec2 run-instances \
3 --image-id ami-0c55b159cbfafe1f0 \
4 --instance-type t3.micro \
5 --key-name my-key \
6 --security-group-ids sg-0123456789abcdef0 \
7 --subnet-id subnet-0123456789abcdef0 \
8 --user-data file://install-app.sh \
9 --tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=web-server},{Key=Environment,Value=production}]'
10
11# User data script (install-app.sh)
12#!/bin/bash
13yum update -y
14yum install -y docker
15systemctl start docker
16systemctl enable docker
17docker run -d -p 80:80 nginx2. Containers
Containers package applications with their dependencies, providing consistency across environments.
Container Platforms:
When to use:
Container vs. VM:
1Virtual Machine:
2┌─────────────────────────────┐
3│ App A │
4│ ┌──────────┐ │
5│ │ Bins/Libs │ │
6│ └──────────┘ │
7│ Guest OS │
8└─────────────────────────────┘
9│ Hypervisor │
10└─────────────────────────────┘
11│ Host OS │
12└─────────────────────────────┘
13│ Hardware │
14└─────────────────────────────┘
15
16Container:
17┌─────────────────────────────┐
18│ App A │ App B │ App C │
19│ ┌─────┐ │ ┌─────┐ │ ┌─────┐ │
20│ │Bins │ │ │Bins │ │ │Bins │ │
21│ └─────┘ │ └─────┘ │ └─────┘ │
22└─────────────────────────────┘
23│ Container Runtime (Docker) │
24└─────────────────────────────┘
25│ Host OS │
26└─────────────────────────────┘
27│ Hardware │
28└─────────────────────────────┘Running containers on AWS ECS:
1// Task definition
2{
3 "family": "web-app",
4 "networkMode": "awsvpc",
5 "requiresCompatibilities": ["FARGATE"],
6 "cpu": "1024",
7 "memory": "2048",
8 "containerDefinitions": [
9 {
10 "name": "nginx",
11 "image": "nginx:latest",
12 "portMappings": [
13 {
14 "containerPort": 80,
15 "protocol": "tcp"
16 }
17 ],
18 "environment": [
19 {
20 "name": "ENVIRONMENT",
21 "value": "production"
22 }
23 ],
24 "logConfiguration": {
25 "logDriver": "awslogs",
26 "options": {
27 "awslogs-group": "/ecs/web-app",
28 "awslogs-region": "us-east-1",
29 "awslogs-stream-prefix": "nginx"
30 }
31 }
32 }
33 ]
34}3. Serverless Computing
Serverless allows running code without managing servers. Pay only for actual execution time.
Serverless Platforms:
When to use:
Serverless Benefits:
Serverless Limitations:
AWS Lambda Function:
1# lambda_function.py
2import json
3import boto3
4from datetime import datetime
5
6s3 = boto3.client('s3')
7
8def lambda_handler(event, context):
9 """
10 Process uploaded images
11 Triggered by S3 upload event
12 """
13
14 # Get bucket and key from event
15 bucket = event['Records'][0]['s3']['bucket']['name']
16 key = event['Records'][0]['s3']['object']['key']
17
18 print(f"Processing {key} from {bucket}")
19
20 # Download file
21 local_path = f"/tmp/{key}"
22 s3.download_file(bucket, key, local_path)
23
24 # Process image (resize, compress, etc.)
25 # ... processing logic ...
26
27 # Upload processed image
28 output_key = f"processed/{key}"
29 s3.upload_file(local_path, bucket, output_key)
30
31 return {
32 'statusCode': 200,
33 'body': json.dumps({
34 'message': 'Image processed successfully',
35 'output': output_key
36 })
37 }Deploying Lambda function:
1# Package function
2zip function.zip lambda_function.py
3
4# Create function
5aws lambda create-function \
6 --function-name process-images \
7 --runtime python3.9 \
8 --role arn:aws:iam::123456789012:role/lambda-execution-role \
9 --handler lambda_function.lambda_handler \
10 --zip-file fileb://function.zip \
11 --timeout 60 \
12 --memory-size 512
13
14# Add S3 trigger
15aws lambda add-permission \
16 --function-name process-images \
17 --statement-id s3-trigger \
18 --action lambda:InvokeFunction \
19 --principal s3.amazonaws.com \
20 --source-arn arn:aws:s3:::my-bucketChoosing the Right Compute Option:
1Decision Tree:
2
3Need OS control or custom kernel? → VM
4 │ No
5 ↓
6Predictable, steady workload? → Containers on managed service
7 │ No
8 ↓
9Event-driven or sporadic traffic? → Serverless (Lambda/Functions)
10 │ No
11 ↓
12Need portability across clouds? → Containers on Kubernetes
13 │ No
14 ↓
15Want minimal management? → Serverless containers (Fargate/Cloud Run)Cost Comparison Example (running 24/7):
1Workload: API server, 2 vCPU, 4GB RAM, us-east-1
2
3Option Monthly Cost Startup Time Management
4------------------------------- --------------- --------------- ------------
5EC2 t3.medium (on-demand) ~$30 Minutes High
6EC2 t3.medium (reserved 1yr) ~$20 Minutes High
7ECS Fargate ~$40 Seconds Medium
8Lambda (1M requests, 512MB) ~$10 Milliseconds Low
9EKS (t3.medium nodes) ~$100 Minutes Very High
10
11For intermittent workloads (1 hour/day):
12EC2 (on-demand, stopped) ~$1 + storage Minutes High
13Fargate ~$1.3 Seconds Medium
14Lambda ~$0.30 Milliseconds LowCloud Networking Architecture
1 Internet
2 │
3 ↓
4 ┌───────────────┐
5 │ Internet │
6 │ Gateway (IGW) │
7 └───────────────┘
8 │
9 ┌───────────┴───────────┐
10 │ │
11 ┌───────────────┐ ┌───────────────┐
12 │ Public Subnet │ │ Public Subnet │
13 │ (AZ-1a) │ │ (AZ-1b) │
14 │ │ │ │
15 │ ┌───────────┐ │ │ ┌───────────┐ │
16 │ │ ALB │ │ │ │ ALB │ │
17 │ └───────────┘ │ │ └───────────┘ │
18 └───────┬───────┘ └───────┬───────┘
19 │ │
20 └──────────┬────────────┘
21 │
22 ┌──────────┴──────────┐
23 │ │
24 ┌───────────────┐ ┌───────────────┐
25 │Private Subnet │ │Private Subnet │
26 │ (AZ-1a) │ │ (AZ-1b) │
27 │ │ │ │
28 │ ┌─────────┐ │ │ ┌─────────┐ │
29 │ │ EC2/ECS │ │ │ │ EC2/ECS │ │
30 │ └─────────┘ │ │ └─────────┘ │
31 └───────┬───────┘ └───────┬───────┘
32 │ │
33 └─────────┬───────────┘
34 │
35 ┌─────────┴─────────┐
36 │ │
37 ┌───────────────┐ ┌───────────────┐
38 │ DB Subnet │ │ DB Subnet │
39 │ (AZ-1a) │ │ (AZ-1b) │
40 │ │ │ │
41 │ ┌─────────┐ │ │ ┌─────────┐ │
42 │ │ RDS │ │ │ │ RDS │ │
43 │ └─────────┘ │ │ └─────────┘ │
44 └───────────────┘ └───────────────┘1. Virtual Private Cloud (VPC)
A VPC is your isolated network in the cloud, providing complete control over IP addressing, subnets, routing, and security.
VPC Components:
Creating a VPC (AWS):
1# Create VPC
2VPC_ID=$(aws ec2 create-vpc \
3 --cidr-block 10.0.0.0/16 \
4 --tag-specifications 'ResourceType=vpc,Tags=[{Key=Name,Value=production-vpc}]' \
5 --query 'Vpc.VpcId' \
6 --output text)
7
8echo "Created VPC: $VPC_ID"
9
10# Enable DNS hostnames
11aws ec2 modify-vpc-attribute \
12 --vpc-id $VPC_ID \
13 --enable-dns-hostnames
14
15# Create Internet Gateway
16IGW_ID=$(aws ec2 create-internet-gateway \
17 --tag-specifications 'ResourceType=internet-gateway,Tags=[{Key=Name,Value=production-igw}]' \
18 --query 'InternetGateway.InternetGatewayId' \
19 --output text)
20
21# Attach Internet Gateway to VPC
22aws ec2 attach-internet-gateway \
23 --internet-gateway-id $IGW_ID \
24 --vpc-id $VPC_ID
25
26echo "Created and attached IGW: $IGW_ID"2. Subnets
Subnets divide your VPC into smaller network segments, typically across different availability zones for high availability.
Subnet Types:
IP Address Planning:
1VPC: 10.0.0.0/16 (65,536 IPs)
2
3Public Subnets (for load balancers, bastion hosts):
4 - 10.0.1.0/24 (AZ-1a) - 256 IPs
5 - 10.0.2.0/24 (AZ-1b) - 256 IPs
6 - 10.0.3.0/24 (AZ-1c) - 256 IPs
7
8Private Subnets (for application servers):
9 - 10.0.11.0/24 (AZ-1a) - 256 IPs
10 - 10.0.12.0/24 (AZ-1b) - 256 IPs
11 - 10.0.13.0/24 (AZ-1c) - 256 IPs
12
13Database Subnets (for RDS, ElastiCache):
14 - 10.0.21.0/24 (AZ-1a) - 256 IPs
15 - 10.0.22.0/24 (AZ-1b) - 256 IPs
16 - 10.0.23.0/24 (AZ-1c) - 256 IPsCreating Subnets:
1# Create public subnet in AZ-1a
2PUBLIC_SUBNET_1A=$(aws ec2 create-subnet \
3 --vpc-id $VPC_ID \
4 --cidr-block 10.0.1.0/24 \
5 --availability-zone us-east-1a \
6 --tag-specifications 'ResourceType=subnet,Tags=[{Key=Name,Value=public-1a}]' \
7 --query 'Subnet.SubnetId' \
8 --output text)
9
10# Enable auto-assign public IP
11aws ec2 modify-subnet-attribute \
12 --subnet-id $PUBLIC_SUBNET_1A \
13 --map-public-ip-on-launch
14
15# Create private subnet in AZ-1a
16PRIVATE_SUBNET_1A=$(aws ec2 create-subnet \
17 --vpc-id $VPC_ID \
18 --cidr-block 10.0.11.0/24 \
19 --availability-zone us-east-1a \
20 --tag-specifications 'ResourceType=subnet,Tags=[{Key=Name,Value=private-1a}]' \
21 --query 'Subnet.SubnetId' \
22 --output text)3. Load Balancers
Load balancers distribute traffic across multiple targets for high availability and scalability.
Load Balancer Types:
Application Load Balancer (ALB)
Network Load Balancer (NLB)
Classic Load Balancer (CLB)
Creating an Application Load Balancer:
1# Create ALB
2ALB_ARN=$(aws elbv2 create-load-balancer \
3 --name production-alb \
4 --subnets $PUBLIC_SUBNET_1A $PUBLIC_SUBNET_1B \
5 --security-groups $ALB_SG_ID \
6 --scheme internet-facing \
7 --type application \
8 --ip-address-type ipv4 \
9 --tags Key=Name,Value=production-alb \
10 --query 'LoadBalancers[0].LoadBalancerArn' \
11 --output text)
12
13# Create target group
14TG_ARN=$(aws elbv2 create-target-group \
15 --name web-servers \
16 --protocol HTTP \
17 --port 80 \
18 --vpc-id $VPC_ID \
19 --health-check-enabled \
20 --health-check-path /health \
21 --health-check-interval-seconds 30 \
22 --healthy-threshold-count 2 \
23 --unhealthy-threshold-count 3 \
24 --query 'TargetGroups[0].TargetGroupArn' \
25 --output text)
26
27# Create listener
28aws elbv2 create-listener \
29 --load-balancer-arn $ALB_ARN \
30 --protocol HTTPS \
31 --port 443 \
32 --certificates CertificateArn=$CERTIFICATE_ARN \
33 --default-actions Type=forward,TargetGroupArn=$TG_ARN
34
35# Register targets
36aws elbv2 register-targets \
37 --target-group-arn $TG_ARN \
38 --targets Id=i-1234567890abcdef0 Id=i-0987654321fedcba04. DNS (Route 53)
DNS translates domain names to IP addresses. Cloud providers offer managed DNS services.
Route 53 Routing Policies:
Creating DNS Records:
1# Create hosted zone
2ZONE_ID=$(aws route53 create-hosted-zone \
3 --name example.com \
4 --caller-reference $(date +%s) \
5 --query 'HostedZone.Id' \
6 --output text)
7
8# Create A record pointing to ALB
9aws route53 change-resource-record-sets \
10 --hosted-zone-id $ZONE_ID \
11 --change-batch '{
12 "Changes": [{
13 "Action": "CREATE",
14 "ResourceRecordSet": {
15 "Name": "www.example.com",
16 "Type": "A",
17 "AliasTarget": {
18 "HostedZoneId": "Z35SXDOTRQ7X7K",
19 "DNSName": "production-alb-123456789.us-east-1.elb.amazonaws.com",
20 "EvaluateTargetHealth": true
21 }
22 }
23 }]
24 }'5. Security Groups & Network ACLs
Security Groups - Virtual firewalls for instances (stateful)
1# Create security group for web servers
2WEB_SG=$(aws ec2 create-security-group \
3 --group-name web-servers \
4 --description "Security group for web servers" \
5 --vpc-id $VPC_ID \
6 --query 'GroupId' \
7 --output text)
8
9# Allow HTTP from ALB
10aws ec2 authorize-security-group-ingress \
11 --group-id $WEB_SG \
12 --protocol tcp \
13 --port 80 \
14 --source-group $ALB_SG
15
16# Allow HTTPS from ALB
17aws ec2 authorize-security-group-ingress \
18 --group-id $WEB_SG \
19 --protocol tcp \
20 --port 443 \
21 --source-group $ALB_SG
22
23# Allow SSH from bastion
24aws ec2 authorize-security-group-ingress \
25 --group-id $WEB_SG \
26 --protocol tcp \
27 --port 22 \
28 --source-group $BASTION_SGNetwork ACLs - Subnet-level firewalls (stateless)
Network ACLs provide an additional layer of security at the subnet level.
Best Practices:
✅ Use multiple availability zones for high availability
✅ Separate public, private, and database subnets
✅ Use smallest subnet size needed (plan for growth)
✅ Implement defense in depth (Security Groups + NACLs)
✅ Use private subnets for application and database tiers
✅ Enable VPC Flow Logs for network monitoring
✅ Use VPC endpoints for AWS services (avoid internet routing)
✅ Implement least-privilege security group rules
✅ Document your network architecture
Cloud Storage Types
1Storage Type | Use Case | Access | Durability
2-------------------|-----------------------------| --------------|------------
3Block Storage | VM disks, databases | Block-level | 99.999%
4Object Storage | Static files, backups | HTTP API | 99.999999999%
5File Storage | Shared file systems | NFS/SMB | 99.99%
6Database (SQL) | Structured data | SQL | 99.95%
7Database (NoSQL) | Unstructured/semi-struct | API/Query | 99.99%
8Cache | In-memory data | Key-value | Best-effort1. Block Storage
Block storage provides raw storage volumes that can be attached to instances, similar to hard drives.
AWS EBS (Elastic Block Store)
EBS Volume Types:
Creating and Attaching EBS Volume:
1# Create EBS volume
2VOLUME_ID=$(aws ec2 create-volume \
3 --availability-zone us-east-1a \
4 --size 100 \
5 --volume-type gp3 \
6 --iops 3000 \
7 --throughput 125 \
8 --encrypted \
9 --tag-specifications 'ResourceType=volume,Tags=[{Key=Name,Value=app-data}]' \
10 --query 'VolumeId' \
11 --output text)
12
13# Wait for volume to be available
14aws ec2 wait volume-available --volume-ids $VOLUME_ID
15
16# Attach to instance
17aws ec2 attach-volume \
18 --volume-id $VOLUME_ID \
19 --instance-id i-1234567890abcdef0 \
20 --device /dev/sdf
21
22# On the instance, format and mount
23sudo mkfs -t ext4 /dev/xvdf
24sudo mkdir /data
25sudo mount /dev/xvdf /data
26echo '/dev/xvdf /data ext4 defaults,nofail 0 2' | sudo tee -a /etc/fstabEBS Snapshots (Backups):
1# Create snapshot
2SNAPSHOT_ID=$(aws ec2 create-snapshot \
3 --volume-id $VOLUME_ID \
4 --description "Backup $(date +%Y-%m-%d)" \
5 --tag-specifications 'ResourceType=snapshot,Tags=[{Key=Name,Value=daily-backup}]' \
6 --query 'SnapshotId' \
7 --output text)
8
9# List snapshots
10aws ec2 describe-snapshots \
11 --owner-ids self \
12 --filters "Name=volume-id,Values=$VOLUME_ID"
13
14# Restore from snapshot
15aws ec2 create-volume \
16 --snapshot-id $SNAPSHOT_ID \
17 --availability-zone us-east-1a \
18 --volume-type gp32. Object Storage
Object storage stores files as objects with metadata, accessed via HTTP API. Highly durable and scalable.
AWS S3 (Simple Storage Service)
S3 Storage Classes:
S3 Operations:
1# Create bucket
2aws s3api create-bucket \
3 --bucket my-app-assets \
4 --region us-east-1
5
6# Enable versioning
7aws s3api put-bucket-versioning \
8 --bucket my-app-assets \
9 --versioning-configuration Status=Enabled
10
11# Enable encryption
12aws s3api put-bucket-encryption \
13 --bucket my-app-assets \
14 --server-side-encryption-configuration '{
15 "Rules": [{
16 "ApplyServerSideEncryptionByDefault": {
17 "SSEAlgorithm": "AES256"
18 }
19 }]
20 }'
21
22# Upload file
23aws s3 cp local-file.txt s3://my-app-assets/path/to/file.txt
24
25# Upload with metadata
26aws s3 cp image.jpg s3://my-app-assets/images/image.jpg \
27 --metadata "author=john,category=product" \
28 --content-type "image/jpeg" \
29 --cache-control "max-age=31536000"
30
31# Sync directory
32aws s3 sync ./dist s3://my-app-assets/static/ --delete
33
34# Download file
35aws s3 cp s3://my-app-assets/path/to/file.txt local-file.txt
36
37# List objects
38aws s3 ls s3://my-app-assets/images/
39
40# Generate presigned URL (temporary access)
41aws s3 presign s3://my-app-assets/private/document.pdf --expires-in 3600S3 Lifecycle Policies:
1{
2 "Rules": [
3 {
4 "Id": "Transition old logs to Glacier",
5 "Status": "Enabled",
6 "Filter": {
7 "Prefix": "logs/"
8 },
9 "Transitions": [
10 {
11 "Days": 30,
12 "StorageClass": "STANDARD_IA"
13 },
14 {
15 "Days": 90,
16 "StorageClass": "GLACIER"
17 }
18 ],
19 "Expiration": {
20 "Days": 365
21 }
22 },
23 {
24 "Id": "Delete incomplete multipart uploads",
25 "Status": "Enabled",
26 "AbortIncompleteMultipartUpload": {
27 "DaysAfterInitiation": 7
28 }
29 }
30 ]
31}3. Database Services
Relational Databases (SQL)
RDS Benefits:
Creating RDS Instance:
1# Create DB subnet group
2aws rds create-db-subnet-group \
3 --db-subnet-group-name my-db-subnet-group \
4 --db-subnet-group-description "Subnets for database" \
5 --subnet-ids subnet-xxx subnet-yyy subnet-zzz
6
7# Create RDS instance
8aws rds create-db-instance \
9 --db-instance-identifier production-db \
10 --db-instance-class db.t3.medium \
11 --engine postgres \
12 --engine-version 14.7 \
13 --master-username admin \
14 --master-user-password SecurePassword123! \
15 --allocated-storage 100 \
16 --storage-type gp3 \
17 --storage-encrypted \
18 --vpc-security-group-ids sg-xxxxx \
19 --db-subnet-group-name my-db-subnet-group \
20 --backup-retention-period 7 \
21 --preferred-backup-window "03:00-04:00" \
22 --preferred-maintenance-window "Mon:04:00-Mon:05:00" \
23 --multi-az \
24 --publicly-accessible false \
25 --enable-performance-insights \
26 --performance-insights-retention-period 7
27
28# Create read replica
29aws rds create-db-instance-read-replica \
30 --db-instance-identifier production-db-replica \
31 --source-db-instance-identifier production-db \
32 --db-instance-class db.t3.mediumNoSQL Databases
DynamoDB Operations:
1# Create table
2aws dynamodb create-table \
3 --table-name Users \
4 --attribute-definitions \
5 AttributeName=userId,AttributeType=S \
6 AttributeName=email,AttributeType=S \
7 --key-schema \
8 AttributeName=userId,KeyType=HASH \
9 --global-secondary-indexes '[
10 {
11 "IndexName": "email-index",
12 "KeySchema": [{"AttributeName":"email","KeyType":"HASH"}],
13 "Projection": {"ProjectionType":"ALL"},
14 "ProvisionedThroughput": {"ReadCapacityUnits":5,"WriteCapacityUnits":5}
15 }
16 ]' \
17 --billing-mode PAY_PER_REQUEST
18
19# Put item
20aws dynamodb put-item \
21 --table-name Users \
22 --item '{
23 "userId": {"S": "123"},
24 "email": {"S": "[email protected]"},
25 "name": {"S": "John Doe"}
26 }'
27
28# Get item
29aws dynamodb get-item \
30 --table-name Users \
31 --key '{"userId": {"S": "123"}}'
32
33# Query by index
34aws dynamodb query \
35 --table-name Users \
36 --index-name email-index \
37 --key-condition-expression "email = :email" \
38 --expression-attribute-values '{":email": {"S": "[email protected]"}}'4. Caching
Creating Redis Cluster:
1# Create Redis cluster
2aws elasticache create-replication-group \
3 --replication-group-id production-cache \
4 --replication-group-description "Production Redis cluster" \
5 --engine redis \
6 --cache-node-type cache.r6g.large \
7 --num-cache-clusters 3 \
8 --automatic-failover-enabled \
9 --multi-az-enabled \
10 --cache-subnet-group-name my-cache-subnet-group \
11 --security-group-ids sg-xxxxx \
12 --at-rest-encryption-enabled \
13 --transit-encryption-enabledStorage Decision Matrix:
1Use Case | Recommended Storage
2----------------------------------|--------------------------------
3VM boot disk | Block (EBS gp3)
4Database files | Block (EBS io2)
5Static website | Object (S3 Standard)
6User uploads | Object (S3 Standard/IA)
7Log archives | Object (S3 Glacier)
8Shared file system | File (EFS/FSx)
9Structured data | Database (RDS/Aurora)
10High-speed K/V store | Cache (ElastiCache Redis)
11Document store | NoSQL (DynamoDB/DocumentDB)
12Time-series data | NoSQL (Timestream)Best Practices:
✅ Enable encryption at rest for all storage
✅ Use versioning for critical data (S3, RDS backups)
✅ Implement automated backup strategies
✅ Use appropriate storage class based on access patterns
✅ Enable cross-region replication for disaster recovery
✅ Monitor storage costs and optimize regularly
✅ Use lifecycle policies to automatically transition data
✅ Tag all storage resources for cost allocation
Cloud Security Shared Responsibility Model
1 Customer Responsibility
2 "Security IN the Cloud"
3┌─────────────────────────────────────────────────────┐
4│ Data │
5│ Application │
6│ Identity & Access Management │
7│ Operating System, Network & Firewall Configuration │
8│ Client-Side Encryption │
9│ Server-Side Encryption │
10│ Network Traffic Protection │
11└─────────────────────────────────────────────────────┘
12
13 Provider Responsibility
14 "Security OF the Cloud"
15┌─────────────────────────────────────────────────────┐
16│ Hardware/AWS Global Infrastructure │
17│ Compute, Storage, Database, Networking │
18│ Regions, Availability Zones, Edge Locations │
19│ Software │
20└─────────────────────────────────────────────────────┘1. Identity and Access Management (IAM)
IAM controls who can access what resources in your cloud environment.
IAM Principles:
IAM Components (AWS):
1Users Individual people
2├── Groups Collections of users with shared permissions
3│ └── Policies Permission documents (what actions on what resources)
4│
5Roles Assumed by AWS services or users
6└── Policies Attached to rolesCreating IAM User and Policy:
1# Create IAM user
2aws iam create-user --user-name developer
3
4# Create policy (read-only S3 access)
5cat > s3-readonly-policy.json <<EOF
6{
7 "Version": "2012-10-17",
8 "Statement": [
9 {
10 "Effect": "Allow",
11 "Action": [
12 "s3:GetObject",
13 "s3:ListBucket"
14 ],
15 "Resource": [
16 "arn:aws:s3:::my-bucket/*",
17 "arn:aws:s3:::my-bucket"
18 ]
19 }
20 ]
21}
22EOF
23
24# Create policy
25POLICY_ARN=$(aws iam create-policy \
26 --policy-name S3ReadOnlyPolicy \
27 --policy-document file://s3-readonly-policy.json \
28 --query 'Policy.Arn' \
29 --output text)
30
31# Attach policy to user
32aws iam attach-user-policy \
33 --user-name developer \
34 --policy-arn $POLICY_ARN
35
36# Create access key
37aws iam create-access-key --user-name developerIAM Roles for Services:
1# Create role for EC2
2cat > ec2-trust-policy.json <<EOF
3{
4 "Version": "2012-10-17",
5 "Statement": [
6 {
7 "Effect": "Allow",
8 "Principal": {
9 "Service": "ec2.amazonaws.com"
10 },
11 "Action": "sts:AssumeRole"
12 }
13 ]
14}
15EOF
16
17aws iam create-role \
18 --role-name EC2-S3-Access \
19 --assume-role-policy-document file://ec2-trust-policy.json
20
21# Attach managed policy
22aws iam attach-role-policy \
23 --role-name EC2-S3-Access \
24 --policy-arn arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess
25
26# Create instance profile
27aws iam create-instance-profile --instance-profile-name EC2-S3-Profile
28
29# Add role to instance profile
30aws iam add-role-to-instance-profile \
31 --instance-profile-name EC2-S3-Profile \
32 --role-name EC2-S3-Access2. Encryption
Encryption at Rest:
Encryption in Transit:
KMS Key Management:
1# Create KMS key
2KEY_ID=$(aws kms create-key \
3 --description "Application data encryption key" \
4 --key-usage ENCRYPT_DECRYPT \
5 --origin AWS_KMS \
6 --query 'KeyMetadata.KeyId' \
7 --output text)
8
9# Create alias
10aws kms create-alias \
11 --alias-name alias/app-data-key \
12 --target-key-id $KEY_ID
13
14# Enable key rotation
15aws kms enable-key-rotation --key-id $KEY_ID
16
17# Encrypt data
18CIPHERTEXT=$(aws kms encrypt \
19 --key-id $KEY_ID \
20 --plaintext "sensitive data" \
21 --query 'CiphertextBlob' \
22 --output text)
23
24# Decrypt data
25aws kms decrypt \
26 --ciphertext-blob fileb://<(echo $CIPHERTEXT | base64 -d) \
27 --query 'Plaintext' \
28 --output text | base64 -d3. Network Security
Security Groups (Stateful Firewall):
1# Create security group
2SG_ID=$(aws ec2 create-security-group \
3 --group-name web-servers \
4 --description "Security group for web servers" \
5 --vpc-id $VPC_ID \
6 --query 'GroupId' \
7 --output text)
8
9# Allow HTTPS from ALB
10aws ec2 authorize-security-group-ingress \
11 --group-id $SG_ID \
12 --protocol tcp \
13 --port 443 \
14 --source-group $ALB_SG_ID
15
16# Allow SSH from bastion
17aws ec2 authorize-security-group-ingress \
18 --group-id $SG_ID \
19 --protocol tcp \
20 --port 22 \
21 --source-group $BASTION_SG_IDNetwork ACLs (Stateless Firewall):
1# Create NACL
2NACL_ID=$(aws ec2 create-network-acl \
3 --vpc-id $VPC_ID \
4 --query 'NetworkAcl.NetworkAclId' \
5 --output text)
6
7# Allow inbound HTTPS
8aws ec2 create-network-acl-entry \
9 --network-acl-id $NACL_ID \
10 --rule-number 100 \
11 --protocol tcp \
12 --port-range From=443,To=443 \
13 --cidr-block 0.0.0.0/0 \
14 --rule-action allow \
15 --ingress
16
17# Allow outbound HTTPS (stateless, need explicit rule)
18aws ec2 create-network-acl-entry \
19 --network-acl-id $NACL_ID \
20 --rule-number 100 \
21 --protocol tcp \
22 --port-range From=1024,To=65535 \
23 --cidr-block 0.0.0.0/0 \
24 --rule-action allow \
25 --egress4. Secrets Management
AWS Secrets Manager:
1# Store secret
2aws secretsmanager create-secret \
3 --name production/database/password \
4 --description "Production database password" \
5 --secret-string "SuperSecurePassword123!"
6
7# Store JSON secret
8aws secretsmanager create-secret \
9 --name production/api/keys \
10 --secret-string '{
11 "api_key": "sk_live_xxxx",
12 "api_secret": "secret_xxxx"
13 }'
14
15# Retrieve secret
16aws secretsmanager get-secret-value \
17 --secret-id production/database/password \
18 --query 'SecretString' \
19 --output text
20
21# Enable rotation
22aws secretsmanager rotate-secret \
23 --secret-id production/database/password \
24 --rotation-lambda-arn $LAMBDA_ARN \
25 --rotation-rules AutomaticallyAfterDays=305. Compliance & Auditing
CloudTrail (Audit Logging):
1# Create trail
2aws cloudtrail create-trail \
3 --name organization-trail \
4 --s3-bucket-name cloudtrail-logs \
5 --include-global-service-events \
6 --is-multi-region-trail \
7 --enable-log-file-validation
8
9# Start logging
10aws cloudtrail start-logging --name organization-trail
11
12# Query recent events
13aws cloudtrail lookup-events \
14 --lookup-attributes AttributeKey=EventName,AttributeValue=DeleteBucket \
15 --max-results 10AWS Config (Configuration Compliance):
1# Enable Config
2aws configservice put-configuration-recorder \
3 --configuration-recorder name=default,roleARN=$CONFIG_ROLE_ARN
4
5aws configservice put-delivery-channel \
6 --delivery-channel name=default,s3BucketName=config-logs
7
8# Start recording
9aws configservice start-configuration-recorder \
10 --configuration-recorder-name default
11
12# Create compliance rule
13aws configservice put-config-rule \
14 --config-rule '{
15 "ConfigRuleName": "encrypted-volumes",
16 "Source": {
17 "Owner": "AWS",
18 "SourceIdentifier": "ENCRYPTED_VOLUMES"
19 },
20 "Scope": {
21 "ComplianceResourceTypes": ["AWS::EC2::Volume"]
22 }
23 }'6. Security Best Practices
✅ Enable MFA for all user accounts, especially root
✅ Use IAM roles instead of access keys where possible
✅ Rotate credentials regularly (90 days max)
✅ Enable encryption for all data (rest and transit)
✅ Use VPC for network isolation
✅ Implement least privilege access
✅ Enable logging (CloudTrail, VPC Flow Logs, application logs)
✅ Use WAF for web application firewall
✅ Enable GuardDuty for threat detection
✅ Regular security audits with AWS Security Hub
✅ Patch management for all instances
✅ Backup strategy with tested restore procedures
7. Incident Response
Preparation:
Detection:
Containment:
Eradication:
Recovery:
Post-Incident:
End-to-end infrastructure provisioning including VPC, compute, storage, database, and security for a production web application
1# complete-infrastructure.sh
2# Complete cloud infrastructure setup script
3# Provisions VPC, EC2, RDS, S3, security groups, and monitoring
4
5#!/bin/bash
6set -euo pipefail
7
8# Configuration
9ENVIRONMENT="production"
10PROJECT_NAME="myapp"
11REGION="us-east-1"
12VPC_CIDR="10.0.0.0/16"
13
14echo "🚀 Provisioning complete cloud infrastructure..."
15echo "Environment: $ENVIRONMENT"
16echo "Region: $REGION"
17
18# ==================== STEP 1: VPC & NETWORKING ====================
19
20echo ""
21echo "Step 1: Creating VPC and networking..."
22
23# Create VPC
24VPC_ID=$(aws ec2 create-vpc \
25 --cidr-block $VPC_CIDR \
26 --tag-specifications "ResourceType=vpc,Tags=[{Key=Name,Value=$PROJECT_NAME-vpc},{Key=Environment,Value=$ENVIRONMENT}]" \
27 --query 'Vpc.VpcId' \
28 --output text)
29
30aws ec2 modify-vpc-attribute --vpc-id $VPC_ID --enable-dns-hostnames
31echo "✓ Created VPC: $VPC_ID"
32
33# Create Internet Gateway
34IGW_ID=$(aws ec2 create-internet-gateway \
35 --tag-specifications "ResourceType=internet-gateway,Tags=[{Key=Name,Value=$PROJECT_NAME-igw}]" \
36 --query 'InternetGateway.InternetGatewayId' \
37 --output text)
38
39aws ec2 attach-internet-gateway --internet-gateway-id $IGW_ID --vpc-id $VPC_ID
40echo "✓ Created Internet Gateway: $IGW_ID"
41
42# Create Subnets
43PUBLIC_SUBNET_1=$(aws ec2 create-subnet \
44 --vpc-id $VPC_ID \
45 --cidr-block 10.0.1.0/24 \
46 --availability-zone ${REGION}a \
47 --tag-specifications "ResourceType=subnet,Tags=[{Key=Name,Value=$PROJECT_NAME-public-1a}]" \
48 --query 'Subnet.SubnetId' \
49 --output text)
50
51aws ec2 modify-subnet-attribute --subnet-id $PUBLIC_SUBNET_1 --map-public-ip-on-launch
52
53PUBLIC_SUBNET_2=$(aws ec2 create-subnet \
54 --vpc-id $VPC_ID \
55 --cidr-block 10.0.2.0/24 \
56 --availability-zone ${REGION}b \
57 --tag-specifications "ResourceType=subnet,Tags=[{Key=Name,Value=$PROJECT_NAME-public-1b}]" \
58 --query 'Subnet.SubnetId' \
59 --output text)
60
61aws ec2 modify-subnet-attribute --subnet-id $PUBLIC_SUBNET_2 --map-public-ip-on-launch
62echo "✓ Created public subnets"
63
64PRIVATE_SUBNET_1=$(aws ec2 create-subnet \
65 --vpc-id $VPC_ID \
66 --cidr-block 10.0.11.0/24 \
67 --availability-zone ${REGION}a \
68 --tag-specifications "ResourceType=subnet,Tags=[{Key=Name,Value=$PROJECT_NAME-private-1a}]" \
69 --query 'Subnet.SubnetId' \
70 --output text)
71
72PRIVATE_SUBNET_2=$(aws ec2 create-subnet \
73 --vpc-id $VPC_ID \
74 --cidr-block 10.0.12.0/24 \
75 --availability-zone ${REGION}b \
76 --tag-specifications "ResourceType=subnet,Tags=[{Key=Name,Value=$PROJECT_NAME-private-1b}]" \
77 --query 'Subnet.SubnetId' \
78 --output text)
79
80echo "✓ Created private subnets"
81
82DB_SUBNET_1=$(aws ec2 create-subnet \
83 --vpc-id $VPC_ID \
84 --cidr-block 10.0.21.0/24 \
85 --availability-zone ${REGION}a \
86 --tag-specifications "ResourceType=subnet,Tags=[{Key=Name,Value=$PROJECT_NAME-db-1a}]" \
87 --query 'Subnet.SubnetId' \
88 --output text)
89
90DB_SUBNET_2=$(aws ec2 create-subnet \
91 --vpc-id $VPC_ID \
92 --cidr-block 10.0.22.0/24 \
93 --availability-zone ${REGION}b \
94 --tag-specifications "ResourceType=subnet,Tags=[{Key=Name,Value=$PROJECT_NAME-db-1b}]" \
95 --query 'Subnet.SubnetId' \
96 --output text)
97
98echo "✓ Created database subnets"
99
100# Create NAT Gateway
101EIP=$(aws ec2 allocate-address --domain vpc --query 'AllocationId' --output text)
102
103NAT_GW=$(aws ec2 create-nat-gateway \
104 --subnet-id $PUBLIC_SUBNET_1 \
105 --allocation-id $EIP \
106 --tag-specifications "ResourceType=natgateway,Tags=[{Key=Name,Value=$PROJECT_NAME-nat}]" \
107 --query 'NatGateway.NatGatewayId' \
108 --output text)
109
110echo "✓ Created NAT Gateway: $NAT_GW"
111echo "Waiting for NAT Gateway to become available..."
112aws ec2 wait nat-gateway-available --nat-gateway-ids $NAT_GW
113
114# Create Route Tables
115PUBLIC_RT=$(aws ec2 create-route-table \
116 --vpc-id $VPC_ID \
117 --tag-specifications "ResourceType=route-table,Tags=[{Key=Name,Value=$PROJECT_NAME-public-rt}]" \
118 --query 'RouteTable.RouteTableId' \
119 --output text)
120
121aws ec2 create-route --route-table-id $PUBLIC_RT --destination-cidr-block 0.0.0.0/0 --gateway-id $IGW_ID
122
123aws ec2 associate-route-table --route-table-id $PUBLIC_RT --subnet-id $PUBLIC_SUBNET_1
124aws ec2 associate-route-table --route-table-id $PUBLIC_RT --subnet-id $PUBLIC_SUBNET_2
125
126PRIVATE_RT=$(aws ec2 create-route-table \
127 --vpc-id $VPC_ID \
128 --tag-specifications "ResourceType=route-table,Tags=[{Key=Name,Value=$PROJECT_NAME-private-rt}]" \
129 --query 'RouteTable.RouteTableId' \
130 --output text)
131
132aws ec2 create-route --route-table-id $PRIVATE_RT --destination-cidr-block 0.0.0.0/0 --nat-gateway-id $NAT_GW
133
134aws ec2 associate-route-table --route-table-id $PRIVATE_RT --subnet-id $PRIVATE_SUBNET_1
135aws ec2 associate-route-table --route-table-id $PRIVATE_RT --subnet-id $PRIVATE_SUBNET_2
136
137echo "✓ Configured route tables"
138
139# ==================== STEP 2: SECURITY GROUPS ====================
140
141echo ""
142echo "Step 2: Creating security groups..."
143
144# ALB Security Group
145ALB_SG=$(aws ec2 create-security-group \
146 --group-name ${PROJECT_NAME}-alb-sg \
147 --description "Security group for Application Load Balancer" \
148 --vpc-id $VPC_ID \
149 --query 'GroupId' \
150 --output text)
151
152aws ec2 authorize-security-group-ingress --group-id $ALB_SG --protocol tcp --port 443 --cidr 0.0.0.0/0
153aws ec2 authorize-security-group-ingress --group-id $ALB_SG --protocol tcp --port 80 --cidr 0.0.0.0/0
154
155echo "✓ Created ALB security group: $ALB_SG"
156
157# App Security Group
158APP_SG=$(aws ec2 create-security-group \
159 --group-name ${PROJECT_NAME}-app-sg \
160 --description "Security group for application servers" \
161 --vpc-id $VPC_ID \
162 --query 'GroupId' \
163 --output text)
164
165aws ec2 authorize-security-group-ingress --group-id $APP_SG --protocol tcp --port 8080 --source-group $ALB_SG
166
167echo "✓ Created App security group: $APP_SG"
168
169# DB Security Group
170DB_SG=$(aws ec2 create-security-group \
171 --group-name ${PROJECT_NAME}-db-sg \
172 --description "Security group for database" \
173 --vpc-id $VPC_ID \
174 --query 'GroupId' \
175 --output text)
176
177aws ec2 authorize-security-group-ingress --group-id $DB_SG --protocol tcp --port 5432 --source-group $APP_SG
178
179echo "✓ Created DB security group: $DB_SG"
180
181# ==================== STEP 3: STORAGE ====================
182
183echo ""
184echo "Step 3: Creating storage resources..."
185
186# Create S3 bucket
187S3_BUCKET="${PROJECT_NAME}-assets-$(date +%s)"
188aws s3api create-bucket --bucket $S3_BUCKET --region $REGION
189
190aws s3api put-bucket-versioning \
191 --bucket $S3_BUCKET \
192 --versioning-configuration Status=Enabled
193
194aws s3api put-bucket-encryption \
195 --bucket $S3_BUCKET \
196 --server-side-encryption-configuration '{
197 "Rules": [{
198 "ApplyServerSideEncryptionByDefault": {
199 "SSEAlgorithm": "AES256"
200 }
201 }]
202 }'
203
204echo "✓ Created S3 bucket: $S3_BUCKET"
205
206# ==================== STEP 4: DATABASE ====================
207
208echo ""
209echo "Step 4: Creating RDS database..."
210
211# Create DB subnet group
212aws rds create-db-subnet-group \
213 --db-subnet-group-name ${PROJECT_NAME}-db-subnet-group \
214 --db-subnet-group-description "Subnet group for database" \
215 --subnet-ids $DB_SUBNET_1 $DB_SUBNET_2
216
217# Create RDS instance
218aws rds create-db-instance \
219 --db-instance-identifier ${PROJECT_NAME}-db \
220 --db-instance-class db.t3.medium \
221 --engine postgres \
222 --engine-version 14.7 \
223 --master-username admin \
224 --master-user-password "ChangeMe123!" \
225 --allocated-storage 100 \
226 --storage-type gp3 \
227 --storage-encrypted \
228 --vpc-security-group-ids $DB_SG \
229 --db-subnet-group-name ${PROJECT_NAME}-db-subnet-group \
230 --backup-retention-period 7 \
231 --multi-az \
232 --publicly-accessible false
233
234echo "✓ Created RDS instance (this will take several minutes)"
235
236# ==================== STEP 5: COMPUTE ====================
237
238echo ""
239echo "Step 5: Creating EC2 instances..."
240
241# Get latest Amazon Linux 2 AMI
242AMI_ID=$(aws ec2 describe-images \
243 --owners amazon \
244 --filters "Name=name,Values=amzn2-ami-hvm-*-x86_64-gp2" \
245 --query 'sort_by(Images, &CreationDate)[-1].ImageId' \
246 --output text)
247
248# Launch EC2 instances
249INSTANCE_1=$(aws ec2 run-instances \
250 --image-id $AMI_ID \
251 --instance-type t3.medium \
252 --subnet-id $PRIVATE_SUBNET_1 \
253 --security-group-ids $APP_SG \
254 --tag-specifications "ResourceType=instance,Tags=[{Key=Name,Value=$PROJECT_NAME-app-1}]" \
255 --user-data file://user-data.sh \
256 --query 'Instances[0].InstanceId' \
257 --output text)
258
259INSTANCE_2=$(aws ec2 run-instances \
260 --image-id $AMI_ID \
261 --instance-type t3.medium \
262 --subnet-id $PRIVATE_SUBNET_2 \
263 --security-group-ids $APP_SG \
264 --tag-specifications "ResourceType=instance,Tags=[{Key=Name,Value=$PROJECT_NAME-app-2}]" \
265 --user-data file://user-data.sh \
266 --query 'Instances[0].InstanceId' \
267 --output text)
268
269echo "✓ Launched EC2 instances: $INSTANCE_1, $INSTANCE_2"
270
271# ==================== STEP 6: LOAD BALANCER ====================
272
273echo ""
274echo "Step 6: Creating load balancer..."
275
276# Create ALB
277ALB_ARN=$(aws elbv2 create-load-balancer \
278 --name ${PROJECT_NAME}-alb \
279 --subnets $PUBLIC_SUBNET_1 $PUBLIC_SUBNET_2 \
280 --security-groups $ALB_SG \
281 --scheme internet-facing \
282 --type application \
283 --query 'LoadBalancers[0].LoadBalancerArn' \
284 --output text)
285
286ALB_DNS=$(aws elbv2 describe-load-balancers \
287 --load-balancer-arns $ALB_ARN \
288 --query 'LoadBalancers[0].DNSName' \
289 --output text)
290
291echo "✓ Created ALB: $ALB_DNS"
292
293# Create target group
294TG_ARN=$(aws elbv2 create-target-group \
295 --name ${PROJECT_NAME}-tg \
296 --protocol HTTP \
297 --port 8080 \
298 --vpc-id $VPC_ID \
299 --health-check-path /health \
300 --query 'TargetGroups[0].TargetGroupArn' \
301 --output text)
302
303# Register targets
304aws elbv2 register-targets \
305 --target-group-arn $TG_ARN \
306 --targets Id=$INSTANCE_1 Id=$INSTANCE_2
307
308# Create listener
309aws elbv2 create-listener \
310 --load-balancer-arn $ALB_ARN \
311 --protocol HTTP \
312 --port 80 \
313 --default-actions Type=forward,TargetGroupArn=$TG_ARN
314
315echo "✓ Configured load balancer"
316
317# ==================== SUMMARY ====================
318
319echo ""
320echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
321echo "✅ Infrastructure Provisioning Complete!"
322echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
323echo ""
324echo "Resources Created:"
325echo " • VPC: $VPC_ID"
326echo " • Public Subnets: $PUBLIC_SUBNET_1, $PUBLIC_SUBNET_2"
327echo " • Private Subnets: $PRIVATE_SUBNET_1, $PRIVATE_SUBNET_2"
328echo " • Database Subnets: $DB_SUBNET_1, $DB_SUBNET_2"
329echo " • NAT Gateway: $NAT_GW"
330echo " • Security Groups: $ALB_SG (ALB), $APP_SG (App), $DB_SG (DB)"
331echo " • S3 Bucket: $S3_BUCKET"
332echo " • RDS Instance: ${PROJECT_NAME}-db (provisioning...)"
333echo " • EC2 Instances: $INSTANCE_1, $INSTANCE_2"
334echo " • Load Balancer: $ALB_DNS"
335echo ""
336echo "Application URL: http://$ALB_DNS"
337echo ""
338echo "Next Steps:"
339echo " 1. Wait for RDS to finish provisioning (~10 minutes)"
340echo " 2. Update app configuration with database endpoint"
341echo " 3. Configure DNS (Route 53) to point to ALB"
342echo " 4. Set up monitoring (CloudWatch, alarms)"
343echo " 5. Configure backups and disaster recovery"
344echo ""Your progress is saved in this browser, on this device only.