VPC, IAM, Security Groups, Route 53, and CloudFront commands for production AWS networking and security.
Create a production VPC with public and private subnets.
## Create VPC
aws ec2 create-vpc \
--cidr-block 10.0.0.0/16 \
--tag-specifications \
'ResourceType=vpc,Tags=[{Key=Name,Value=prod-vpc}]' \
--region ap-south-1
## Enable DNS hostnames (required for RDS endpoints)
aws ec2 modify-vpc-attribute \
--vpc-id vpc-xxxxxxxx \
--enable-dns-hostnames \
--region ap-south-1
## Create private subnet
aws ec2 create-subnet \
--vpc-id vpc-xxxxxxxx \
--cidr-block 10.0.10.0/24 \
--availability-zone ap-south-1a \
--tag-specifications \
'ResourceType=subnet,Tags=[{Key=Name,Value=private-1a}]' \
--region ap-south-1
Parameter Breakdown:
10.0.0.0/16: 65,536 IPs for the VPC — gives room for many subnets10.0.10.0/24: 251 usable IPs per subnet (5 reserved by AWS)--enable-dns-hostnames: Allows RDS, ECS, and other services to use DNS namesConnect public subnets to internet, private subnets to NAT.
## Create and attach Internet Gateway
aws ec2 create-internet-gateway \
--tag-specifications \
'ResourceType=internet-gateway,Tags=[{Key=Name,Value=prod-igw}]' \
--region ap-south-1
aws ec2 attach-internet-gateway \
--internet-gateway-id igw-xxxxxxxx \
--vpc-id vpc-xxxxxxxx \
--region ap-south-1
## Create NAT Gateway (one per AZ)
aws ec2 allocate-address \
--domain vpc --region ap-south-1
aws ec2 create-nat-gateway \
--subnet-id subnet-public-1a \
--allocation-id eipalloc-xxxxxxxx \
--tag-specifications \
'ResourceType=natgateway,Tags=[{Key=Name,Value=nat-gw-1a}]' \
--region ap-south-1
Parameter Breakdown:
--allocation-id: Elastic IP required — NAT GW needs a static public IPRoute public subnets to IGW, private subnets to NAT.
## Create public route table
aws ec2 create-route-table \
--vpc-id vpc-xxxxxxxx \
--tag-specifications \
'ResourceType=route-table,Tags=[{Key=Name,Value=public-rt}]' \
--region ap-south-1
## Add default route to Internet Gateway
aws ec2 create-route \
--route-table-id rtb-public \
--destination-cidr-block 0.0.0.0/0 \
--gateway-id igw-xxxxxxxx \
--region ap-south-1
## Private route table → NAT Gateway
aws ec2 create-route \
--route-table-id rtb-private-1a \
--destination-cidr-block 0.0.0.0/0 \
--nat-gateway-id nat-xxxxxxxx \
--region ap-south-1
## Associate subnet with route table
aws ec2 associate-route-table \
--route-table-id rtb-public \
--subnet-id subnet-public-1a \
--region ap-south-1
Parameter Breakdown:
0.0.0.0/0 → igw: All non-VPC traffic exits to internet (public subnet rule)0.0.0.0/0 → nat: Private instances reach internet via NAT (outbound only)Route S3 and DynamoDB traffic through private AWS network.
## Free S3 Gateway Endpoint
aws ec2 create-vpc-endpoint \
--vpc-id vpc-xxxxxxxx \
--service-name com.amazonaws.ap-south-1.s3 \
--route-table-ids rtb-private-1a rtb-private-1b \
--region ap-south-1
## Free DynamoDB Gateway Endpoint
aws ec2 create-vpc-endpoint \
--vpc-id vpc-xxxxxxxx \
--service-name com.amazonaws.ap-south-1.dynamodb \
--route-table-ids rtb-private-1a rtb-private-1b \
--region ap-south-1
## Verify endpoint is attached to route tables
aws ec2 describe-route-tables \
--route-table-ids rtb-private-1a \
--query 'RouteTables[*].Routes' \
--region ap-south-1
Parameter Breakdown:
Create chained Security Groups for ALB → EC2 → RDS.
## ALB Security Group — internet-facing
aws ec2 create-security-group \
--group-name alb-prod-sg \
--description "ALB prod inbound" \
--vpc-id vpc-xxxxxxxx \
--region ap-south-1
aws ec2 authorize-security-group-ingress \
--group-id sg-alb-prod \
--protocol tcp --port 443 \
--cidr 0.0.0.0/0 \
--region ap-south-1
## App Security Group — allow only from ALB SG
aws ec2 authorize-security-group-ingress \
--group-id sg-app-prod \
--protocol tcp --port 8080 \
--source-group sg-alb-prod \
--region ap-south-1
## RDS Security Group — allow only from App SG
aws ec2 authorize-security-group-ingress \
--group-id sg-rds-prod \
--protocol tcp --port 5432 \
--source-group sg-app-prod \
--region ap-south-1
## Audit Security Groups for dangerous rules
aws ec2 describe-security-groups \
--query 'SecurityGroups[?IpPermissions[?IpRanges[?CidrIp==`0.0.0.0/0`] && FromPort==`22`]].[GroupId,GroupName]' \
--region ap-south-1
Parameter Breakdown:
--source-group sg-alb-prod: Use SG ID as source — not IP, IPs change on restartCreate an IAM Role for EC2 with least-privilege S3 access.
## Create IAM Role for EC2
aws iam create-role \
--role-name prod-ec2-app-role \
--assume-role-policy-document '{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Principal": {"Service": "ec2.amazonaws.com"},
"Action": "sts:AssumeRole"
}]
}'
## Attach least-privilege inline policy
aws iam put-role-policy \
--role-name prod-ec2-app-role \
--policy-name s3-read-payment-logs \
--policy-document '{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Action": ["s3:GetObject","s3:ListBucket"],
"Resource": [
"arn:aws:s3:::razorpay-payment-logs",
"arn:aws:s3:::razorpay-payment-logs/*"
]
}]
}'
## Attach role to EC2 instance
aws ec2 associate-iam-instance-profile \
--instance-id i-0abc123def456789 \
--iam-instance-profile Name=prod-ec2-app-role \
--region ap-south-1
## Download IAM Credentials Report (CSV)
aws iam generate-credential-report
aws iam get-credential-report \
--query Content \
--output text | base64 -d > credentials-report.csv
Parameter Breakdown:
Create weighted routing records for A/B traffic splitting.
## Create weighted A records (70/30 split)
aws route53 change-resource-record-sets \
--hosted-zone-id ZONE_ID \
--change-batch '{
"Changes": [{
"Action": "CREATE",
"ResourceRecordSet": {
"Name": "api.prod.in",
"Type": "A",
"SetIdentifier": "primary-v1",
"Weight": 70,
"AliasTarget": {
"HostedZoneId": "ZP97RAFLXTNZK",
"DNSName": "prod-alb-v1.ap-south-1.elb.amazonaws.com",
"EvaluateTargetHealth": true
}
}
},{
"Action": "CREATE",
"ResourceRecordSet": {
"Name": "api.prod.in",
"Type": "A",
"SetIdentifier": "canary-v2",
"Weight": 30,
"AliasTarget": {
"HostedZoneId": "ZP97RAFLXTNZK",
"DNSName": "prod-alb-v2.ap-south-1.elb.amazonaws.com",
"EvaluateTargetHealth": true
}
}
}]
}'
Parameter Breakdown:
Weight 70/30: 70% traffic to v1, 30% to v2 — for canary deploymentsEvaluateTargetHealth true: Route 53 excludes the unhealthy ALB automaticallyEnable Flow Logs and query for rejected connections.
## Enable VPC Flow Logs to CloudWatch
aws ec2 create-flow-logs \
--resource-type VPC \
--resource-ids vpc-xxxxxxxx \
--traffic-type ALL \
--log-destination-type cloud-watch-logs \
--log-group-name /vpc/prod-flow-logs \
--deliver-logs-permission-arn \
arn:aws:iam::123456789012:role/FlowLogsRole \
--region ap-south-1
## Query rejected connections in CloudWatch Logs Insights
aws logs start-query \
--log-group-name /vpc/prod-flow-logs \
--start-time $(date -d "1 hour ago" +%s) \
--end-time $(date +%s) \
--query-string \
'fields @timestamp, srcAddr, dstAddr, dstPort, action
| filter action="REJECT"
| stats count(*) by srcAddr
| sort by count desc
| limit 20' \
--region ap-south-1
Parameter Breakdown:
--traffic-type ALL: Logs both ACCEPT and REJECT — essential for debugging| Resource | Attach policy to | When to use |
|---|---|---|
| IAM User | Human beings | Console and CLI access for team members |
| IAM Role | AWS services | EC2, Lambda, ECS — never store access keys |
| IAM Group | Multiple users | Apply same permissions to a team |
| Resource Policy | S3, SQS, KMS | Cross-account access or service-level control |