Tạo Virtual Private Cloud (VPC) foundation cho hệ thống VinaShoes E-commerce với:
📋 Prerequisites từ Tasks trước:
Deployment Strategy:
🎯 Objective: Setup VPC foundation với proper DNS configuration qua AWS Console
Console Steps:

vinashoes-vpc-prod10.0.0.0/16
productionVinaShoesDevOpsInfrastructure

CLI Alternative:
# Create VPC
aws ec2 create-vpc \
--cidr-block 10.0.0.0/16 \
--tag-specifications 'ResourceType=vpc,Tags=[{Key=Name,Value=vinashoes-vpc-prod},{Key=Environment,Value=production},{Key=Project,Value=VinaShoes}]'
# Save VPC ID
export VPC_ID=vpc-xxxxxxxxxxxxxxxxx
# Enable DNS features
aws ec2 modify-vpc-attribute --vpc-id $VPC_ID --enable-dns-hostnames
aws ec2 modify-vpc-attribute --vpc-id $VPC_ID --enable-dns-support
💡 Console vs CLI:
Console Verification:


CLI Verification:
# Verify VPC configuration
aws ec2 describe-vpcs \
--vpc-ids $VPC_ID \
--query 'Vpcs[0].[VpcId,CidrBlock,DnsHostnames,DnsSupport,State]' \
--output table
# Add additional tags if needed
aws ec2 create-tags \
--resources $VPC_ID \
--tags \
Key=Backup,Value=Required \
Key=ManagedBy,Value=DevOpsTeam
🎯 Deliverable 4.1: VPC created với DNS features enabled
vpc-xxxxxxxxxxxxxxxxx (save this for next steps)🎯 Objective: Create 2 Public + 2 Private subnets across 2 AZs qua AWS Console
Console Steps:

vinashoes-public-1a10.0.1.0/24Public1aproduction
vinashoes-public-1b10.0.2.0/24Public, AZ=1b
vinashoes-private-1a10.0.3.0/24Private, AZ=1a
vinashoes-private-1b10.0.4.0/24Private, AZ=1b

vinashoes-public-1a → Actions → Edit subnet settingsvinashoes-public-1b
CLI Alternative:
# Create public subnets
aws ec2 create-subnet --vpc-id $VPC_ID --cidr-block 10.0.1.0/24 --availability-zone ap-southeast-1a \
--tag-specifications 'ResourceType=subnet,Tags=[{Key=Name,Value=vinashoes-public-1a},{Key=Type,Value=Public}]'
export PUBLIC_SUBNET_1A=subnet-xxxxxxxxxxxxxxxxx
aws ec2 create-subnet --vpc-id $VPC_ID --cidr-block 10.0.2.0/24 --availability-zone ap-southeast-1b \
--tag-specifications 'ResourceType=subnet,Tags=[{Key=Name,Value=vinashoes-public-1b},{Key=Type,Value=Public}]'
export PUBLIC_SUBNET_1B=subnet-xxxxxxxxxxxxxxxxx
# Create private subnets
aws ec2 create-subnet --vpc-id $VPC_ID --cidr-block 10.0.3.0/24 --availability-zone ap-southeast-1a \
--tag-specifications 'ResourceType=subnet,Tags=[{Key=Name,Value=vinashoes-private-1a},{Key=Type,Value=Private}]'
export PRIVATE_SUBNET_1A=subnet-xxxxxxxxxxxxxxxxx
aws ec2 create-subnet --vpc-id $VPC_ID --cidr-block 10.0.4.0/24 --availability-zone ap-southeast-1b \
--tag-specifications 'ResourceType=subnet,Tags=[{Key=Name,Value=vinashoes-private-1b},{Key=Type,Value=Private}]'
export PRIVATE_SUBNET_1B=subnet-xxxxxxxxxxxxxxxxx
# Enable auto-assign public IP
aws ec2 modify-subnet-attribute --subnet-id $PUBLIC_SUBNET_1A --map-public-ip-on-launch
aws ec2 modify-subnet-attribute --subnet-id $PUBLIC_SUBNET_1B --map-public-ip-on-launch
Console Verification:



CLI Verification:
# List all subnets trong VPC
aws ec2 describe-subnets \
--filters "Name=vpc-id,Values=$VPC_ID" \
--query 'Subnets[*].[SubnetId,CidrBlock,AvailabilityZone,MapPublicIpOnLaunch,Tags[?Key==`Name`].Value | [0]]' \
--output table
# Count subnets (should be 4)
aws ec2 describe-subnets --filters "Name=vpc-id,Values=$VPC_ID" --query 'length(Subnets)'
⚠️ Subnet Planning:
🎯 Deliverable 4.2: 4 subnets created across 2 AZs
🎯 Objective: Enable internet access cho public subnets qua AWS Console
Console Steps:
vinashoes-igwproductionVinaShoesPublic-Internet-Access

vinashoes-igw → Actions → Attach to VPC

CLI Alternative:
# Create Internet Gateway
aws ec2 create-internet-gateway \
--tag-specifications 'ResourceType=internet-gateway,Tags=[{Key=Name,Value=vinashoes-igw},{Key=Environment,Value=production}]'
# Save IGW ID
export IGW_ID=igw-xxxxxxxxxxxxxxxxx
# Attach IGW to VPC
aws ec2 attach-internet-gateway \
--internet-gateway-id $IGW_ID \
--vpc-id $VPC_ID
# Verify attachment
aws ec2 describe-internet-gateways \
--internet-gateway-ids $IGW_ID \
--query 'InternetGateways[0].[InternetGatewayId,Attachments[0].VpcId,Attachments[0].State]' \
--output table
💡 Internet Gateway Notes:
🎯 Deliverable 4.3: Internet Gateway attached to VPC
igw-xxxxxxxxxxxxxxxxx (save for route tables)🎯 Objective: Setup routing cho public subnets (IGW) và private subnets qua AWS Console
Console Steps:
vinashoes-public-rtPublicproductionInternet-Access
vinashoes-public-rt → Routes tab → Edit routes0.0.0.0/0vinashoes-igw
10.0.0.0/16 → local (automatically added)0.0.0.0/0 → igw-xxxxxxxxx (manually added)
vinashoes-public-1avinashoes-public-1b
CLI Alternative:
# Create public route table
aws ec2 create-route-table \
--vpc-id $VPC_ID \
--tag-specifications 'ResourceType=route-table,Tags=[{Key=Name,Value=vinashoes-public-rt},{Key=Type,Value=Public}]'
export PUBLIC_RT_ID=rtb-xxxxxxxxxxxxxxxxx
# Add default route to IGW
aws ec2 create-route \
--route-table-id $PUBLIC_RT_ID \
--destination-cidr-block 0.0.0.0/0 \
--gateway-id $IGW_ID
# Associate public subnets
aws ec2 associate-route-table --route-table-id $PUBLIC_RT_ID --subnet-id $PUBLIC_SUBNET_1A
aws ec2 associate-route-table --route-table-id $PUBLIC_RT_ID --subnet-id $PUBLIC_SUBNET_1B
Console Steps cho AZ-1a:
vinashoes-private-rt-1aPrivate1aproduction
vinashoes-private-rt-1avinashoes-private-1a
Console Steps cho AZ-1b: 3. Create Private Route Table 1b:
vinashoes-private-rt-1bPrivate1bproduction
vinashoes-private-rt-1bvinashoes-private-1b
CLI Alternative:
# Create private route table for AZ-1a
aws ec2 create-route-table \
--vpc-id $VPC_ID \
--tag-specifications 'ResourceType=route-table,Tags=[{Key=Name,Value=vinashoes-private-rt-1a},{Key=Type,Value=Private},{Key=AZ,Value=1a}]'
export PRIVATE_RT_1A=rtb-xxxxxxxxxxxxxxxxx
# Create private route table for AZ-1b
aws ec2 create-route-table \
--vpc-id $VPC_ID \
--tag-specifications 'ResourceType=route-table,Tags=[{Key=Name,Value=vinashoes-private-rt-1b},{Key=Type,Value=Private},{Key=AZ,Value=1b}]'
export PRIVATE_RT_1B=rtb-xxxxxxxxxxxxxxxxx
# Associate private subnets
aws ec2 associate-route-table --route-table-id $PRIVATE_RT_1A --subnet-id $PRIVATE_SUBNET_1A
aws ec2 associate-route-table --route-table-id $PRIVATE_RT_1B --subnet-id $PRIVATE_SUBNET_1B
⚠️ Route Table Strategy:
Console Verification:
vinashoes-public-rt (2 subnet associations)vinashoes-private-rt-1a (1 subnet association)vinashoes-private-rt-1b (1 subnet association)default route table (0 associations)
vinashoes-public-rt → Routes tab10.0.0.0/16 → local0.0.0.0/0 → igw-xxxxxxxxx
10.0.0.0/16 → local (no internet route yet)

CLI Verification:
# Check public route table
aws ec2 describe-route-tables \
--route-table-ids $PUBLIC_RT_ID \
--query 'RouteTables[0].Routes[*].[DestinationCidrBlock,GatewayId,State]' \
--output table
# Check private route tables
aws ec2 describe-route-tables \
--route-table-ids $PRIVATE_RT_1A $PRIVATE_RT_1B \
--query 'RouteTables[*].[RouteTableId,Routes[*].DestinationCidrBlock]' \
--output table
# Verify internet route exists
aws ec2 describe-route-tables --route-table-ids $PUBLIC_RT_ID \
--query 'RouteTables[0].Routes[?DestinationCidrBlock==`0.0.0.0/0`].GatewayId' \
--output text
🎯 Deliverable 4.4: Route tables configured với IGW access cho public subnets
🎯 Objective: Setup security groups cho ECS Fargate và VPC endpoints qua AWS Console
Console Steps:
vinashoes-ecs-sgSecurity group for ECS Fargate tasks
3000HTTP from VPC for ECS Fargate
⚠️ IPv4/IPv6 CIDR Issue: Nếu gặp lỗi “You may not specify an IPv4 CIDR for an existing IPv6 CIDR rule”:
10.0.0.0/16 (đảm bảo chọn IPv4, không phải IPv6)Note: ALB Security Group sẽ được tạo trong Task sau. Hiện tại để Source empty, sẽ update sau khi có ALB SG.
0.0.0.0/0All outbound traffic to internet via NAT Gateway
ECS-FargateproductionVinaShoes
CLI Alternative:
# Create ECS security group
aws ec2 create-security-group \
--group-name vinashoes-ecs-sg \
--description "Security group for ECS Fargate tasks" \
--vpc-id $VPC_ID \
--tag-specifications 'ResourceType=security-group,Tags=[{Key=Name,Value=vinashoes-ecs-sg},{Key=Purpose,Value=ECS-Fargate}]'
export ECS_SG_ID=sg-xxxxxxxxxxxxxxxxx
# Add inbound rule for HTTP traffic from VPC
aws ec2 authorize-security-group-ingress \
--group-id $ECS_SG_ID \
--protocol tcp \
--port 3000 \
--cidr 10.0.0.0/16
# Outbound rules (default allows all outbound)
# Will update inbound rules later when ALB SG is created
Console Steps:
vinashoes-endpoint-sgSecurity group for VPC endpoints
443vinashoes-ecs-sgHTTPS from ECS tasks
0.0.0.0/0
VPC-EndpointsproductionVinaShoes
CLI Alternative:
# Create VPC endpoint security group
aws ec2 create-security-group \
--group-name vinashoes-endpoint-sg \
--description "Security group for VPC endpoints" \
--vpc-id $VPC_ID \
--tag-specifications 'ResourceType=security-group,Tags=[{Key=Name,Value=vinashoes-endpoint-sg},{Key=Purpose,Value=VPC-Endpoints}]'
export ENDPOINT_SG_ID=sg-xxxxxxxxxxxxxxxxx
# Allow HTTPS from ECS security group
aws ec2 authorize-security-group-ingress \
--group-id $ENDPOINT_SG_ID \
--protocol tcp \
--port 443 \
--source-group $ECS_SG_ID
💡 Security Group Best Practices:
Console Verification:
vinashoes-ecs-sg (custom)vinashoes-endpoint-sg (custom)default (VPC default SG)
Security Group Details Check:
vinashoes-ecs-sg:
💡 Troubleshooting Security Group Rules:
0.0.0.0/0 để test, sau đó restrict lạivinashoes-endpoint-sg:
CLI Verification:
# List all security groups trong VPC
aws ec2 describe-security-groups \
--filters "Name=vpc-id,Values=$VPC_ID" \
--query 'SecurityGroups[*].[GroupId,GroupName,Description]' \
--output table
# Check security group count (should be 3: 2 custom + 1 default)
aws ec2 describe-security-groups --filters "Name=vpc-id,Values=$VPC_ID" --query 'length(SecurityGroups)'
# Verify security group rules
aws ec2 describe-security-groups --group-ids $ECS_SG_ID $ENDPOINT_SG_ID --output table
🎯 Deliverable 4.5: Security groups foundation ready cho ECS và VPC endpoints
🎯 Objective: Validate toàn bộ VPC setup qua Console và CLI
VPC Dashboard Overview:
Navigate to VPC Dashboard:
Verify Resource Count:
Expected Resources:
✅ 1 VPC (vinashoes-vpc-prod)
✅ 4 Subnets (2 public, 2 private)
✅ 1 Internet Gateway (vinashoes-igw)
✅ 4 Route Tables (1 public, 2 private, 1 default)
✅ 3 Security Groups (ECS, Endpoint, Default)
✅ 0 NAT Gateways (will create in Task 5)
Resource Map Visualization:

Automated Validation Script:
# Test script to validate VPC setup
cat << 'EOF' > test-vpc-setup.sh
#!/bin/bash
echo "=== VPC Configuration Test ==="
# Test VPC exists and has DNS features
VPC_STATE=$(aws ec2 describe-vpcs --vpc-ids $VPC_ID --query 'Vpcs[0].State' --output text)
DNS_HOSTNAMES=$(aws ec2 describe-vpcs --vpc-ids $VPC_ID --query 'Vpcs[0].DnsHostnames' --output text)
DNS_SUPPORT=$(aws ec2 describe-vpcs --vpc-ids $VPC_ID --query 'Vpcs[0].DnsSupport' --output text)
echo "VPC State: $VPC_STATE (should be 'available')"
echo "DNS Hostnames: $DNS_HOSTNAMES (should be 'True')"
echo "DNS Support: $DNS_SUPPORT (should be 'True')"
# Test subnet count
SUBNET_COUNT=$(aws ec2 describe-subnets --filters "Name=vpc-id,Values=$VPC_ID" --query 'length(Subnets)')
echo "Subnet Count: $SUBNET_COUNT (should be 4)"
# Test IGW attachment
IGW_STATE=$(aws ec2 describe-internet-gateways --internet-gateway-ids $IGW_ID --query 'InternetGateways[0].Attachments[0].State' --output text)
echo "IGW State: $IGW_STATE (should be 'available')"
# Test route tables
PUBLIC_ROUTES=$(aws ec2 describe-route-tables --route-table-ids $PUBLIC_RT_ID --query 'length(RouteTables[0].Routes)')
echo "Public Route Count: $PUBLIC_ROUTES (should be >= 2)"
# Test security groups
SG_COUNT=$(aws ec2 describe-security-groups --filters "Name=vpc-id,Values=$VPC_ID" --query 'length(SecurityGroups)')
echo "Security Group Count: $SG_COUNT (should be >= 3 including default)"
echo "=== Test Complete ==="
EOF
chmod +x test-vpc-setup.sh
./test-vpc-setup.sh

Create Resource Reference File:
# Export all resource IDs for next tasks
cat << EOF > vpc-resources.env
# VPC Resource IDs for VinaShoes Project
# Replace with actual IDs from your AWS Console
export VPC_ID=vpc-xxxxxxxxxxxxxxxxx
export PUBLIC_SUBNET_1A=subnet-xxxxxxxxxxxxxxxxx
export PUBLIC_SUBNET_1B=subnet-xxxxxxxxxxxxxxxxx
export PRIVATE_SUBNET_1A=subnet-xxxxxxxxxxxxxxxxx
export PRIVATE_SUBNET_1B=subnet-xxxxxxxxxxxxxxxxx
export IGW_ID=igw-xxxxxxxxxxxxxxxxx
export PUBLIC_RT_ID=rtb-xxxxxxxxxxxxxxxxx
export PRIVATE_RT_1A=rtb-xxxxxxxxxxxxxxxxx
export PRIVATE_RT_1B=rtb-xxxxxxxxxxxxxxxxx
export ECS_SG_ID=sg-xxxxxxxxxxxxxxxxx
export ENDPOINT_SG_ID=sg-xxxxxxxxxxxxxxxxx
# Usage: source vpc-resources.env
EOF
echo "Resource template created in vpc-resources.env"
echo "Please update with actual resource IDs from AWS Console"

Problem: Error “You may not specify an IPv4 CIDR for an existing IPv6 CIDR rule”
Root Cause: Trying to add IPv4 CIDR (10.0.0.0/16) to a rule that was created as IPv6 by default.
Solution Steps:
Identify the conflicting rule:
vinashoes-ecs-sg → Inbound rulesDelete the conflicting rule:
# List existing rules to identify the problem
aws ec2 describe-security-groups --group-ids $ECS_SG_ID
# Delete IPv6 rule if exists (replace rule-id with actual ID)
aws ec2 revoke-security-group-ingress \
--group-id $ECS_SG_ID \
--ip-permissions '[{"IpProtocol":"tcp","FromPort":3000,"ToPort":3000,"Ipv6Ranges":[{"CidrIpv6":"::/0"}]}]'
Create new IPv4 rule correctly:
Console Method:
300010.0.0.0/16CLI Method:
# Add IPv4 rule correctly
aws ec2 authorize-security-group-ingress \
--group-id $ECS_SG_ID \
--protocol tcp \
--port 3000 \
--cidr 10.0.0.0/16
Alternative approaches:
Prevention:
💰 Cost Notes:
# Script to monitor VPC-related costs
cat << 'EOF' > monitor-vpc-costs.sh
#!/bin/bash
echo "=== VPC Cost Monitoring ==="
# Check NAT Gateway costs (will be created in Task 5)
aws ce get-cost-and-usage \
--time-period Start=2025-08-01,End=2025-08-31 \
--granularity MONTHLY \
--metrics BlendedCost \
--group-by Type=DIMENSION,Key=SERVICE \
--filter '{"Dimensions":{"Key":"SERVICE","Values":["Amazon Elastic Compute Cloud - Compute"]}}'
# Check data transfer costs
aws ce get-cost-and-usage \
--time-period Start=2025-08-01,End=2025-08-31 \
--granularity MONTHLY \
--metrics BlendedCost \
--group-by Type=DIMENSION,Key=USAGE_TYPE \
--filter '{"Dimensions":{"Key":"USAGE_TYPE","Values":["DataTransfer-Out-Bytes"]}}'
echo "=== Monitoring Complete ==="
EOF
chmod +x monitor-vpc-costs.sh
🎉 Task 4 Complete! VPC foundation ready cho Task 5 (NAT Gateway) và Task 8 (ECS Fargate deployment).
💡 Preparation cho Task 5: