Table of Contents
- Module 1 — Introduction to the Virtual Private Cloud (VPC)
- Module 2 — Creating Your First VPC
- Module 3 — Access Control in the VPC
- Module 4 — Data Transfer Costs
- Deep Dive — CIDR Planning and Subnet Design
- Deep Dive — VPC Endpoints and PrivateLink
- Deep Dive — Advanced VPC-to-VPC Connectivity
- Deep Dive — Route 53 and Routing Policies
- Deep Dive — Elastic Load Balancing
- Deep Dive — Complete Multi-AZ Architecture
- Quick Reference — NACL vs Security Group Comparison
- Reserved IP Addresses in a Subnet
- RFC 1918 Address Spaces
- AWS CLI Reference — Essential Network Commands
- Infrastructure as Code — CloudFormation and Terraform
Module 1 — Introduction to the Virtual Private Cloud (VPC)
What is a Virtual Private Cloud?
AWS Infrastructure Hierarchy
Before understanding VPCs, it is essential to understand how AWS’s physical infrastructure is organized.
Data Center → Availability Zone (AZ) → Region → VPC
flowchart TD
DC1[Data Center 1] --> AZ1[Availability Zone A]
DC2[Data Center 2] --> AZ1
DC3[Data Center 3] --> AZ2[Availability Zone B]
DC4[Data Center 4] --> AZ3[Availability Zone C]
AZ1 --> R[Region\n3 AZs minimum]
AZ2 --> R
AZ3 --> R
R --> VPC[VPC\nVirtual Private Cloud]
style DC1 fill:#f9f,stroke:#333
style DC2 fill:#f9f,stroke:#333
style DC3 fill:#f9f,stroke:#333
style DC4 fill:#f9f,stroke:#333
style AZ1 fill:#bbf,stroke:#333
style AZ2 fill:#bbf,stroke:#333
style AZ3 fill:#bbf,stroke:#333
style R fill:#bfb,stroke:#333
style VPC fill:#fbb,stroke:#333
| Component | Description |
|---|---|
| Data Center | Physical building with redundant power, cooling, and ventilation. Houses AWS equipment. Designed to be independent of other data centers. |
| Availability Zone (AZ) | Collection of highly interconnected data centers. Contains one or more data centers. |
| Region | Composed of 3 or more AZs. Each AZ is separated by at least 60 miles (96 km) to reduce the risk of a disaster affecting multiple AZs. |
| VPC | Dedicated virtual private network in the AWS cloud for provisioning resources and applications. |
Important: AWS uses random AZ mapping per account. The “us-east-1a” AZ in your account may correspond to a different physical data center than “us-east-1a” in another user’s account.
VPC Definition
A Virtual Private Cloud (VPC) is a logically isolated private network in the AWS cloud. It allows you to:
- Provision resources and applications in your own dedicated network space
- Define your own IP address range (CIDR block)
- Create subnets, route tables, and network gateways
- Control inbound and outbound traffic with firewalls (NACLs and Security Groups)
- Connect your on-premises network via VPN or Direct Connect
Default VPC limits:
| Resource | Default limit | Adjustable |
|---|---|---|
| VPCs per region | 5 | Yes |
| Subnets per VPC | 200 | Yes |
| Internet Gateways per VPC | 1 | No |
| Route Tables per VPC | 200 | Yes |
| Security Groups per VPC | 2,500 | Yes |
| NACLs per VPC | 200 | Yes |
VPC Connectivity
A VPC offers several connectivity options depending on needs:
1. Internet Connectivity (Internet Gateway)
For an EC2 instance to communicate with the Internet, the following must be configured:
- An Internet Gateway (IGW) must be created and attached to the VPC
- A route table must contain a route sending Internet traffic to the Internet Gateway
- A public IP address or Elastic IP must be associated with the EC2 instance
flowchart LR
EC2[EC2 Instance\n10.0.1.10 private] -->|Outbound traffic\n10.0.1.10 to public IP| IGW[Internet Gateway\nNAT: private to public IP]
IGW -->|Inbound/outbound traffic| Internet["(Internet)"]
Internet -->|Reply to public IP| IGW
IGW -->|Forward to 10.0.1.10| EC2
style EC2 fill:#bbf,stroke:#333
style IGW fill:#ffa,stroke:#333
style Internet fill:#ddd,stroke:#333
How NAT works in the Internet Gateway:
- Outbound traffic: The instance’s private IP is translated to a public/Elastic IP before going to the Internet
- Inbound traffic: The public IP is translated to a private IP before reaching the instance
Types of public IP addresses:
| Type | Description | Persistence |
|---|---|---|
| Public IP | Automatically assigned when creating an instance in a public subnet. | Temporary — changes if the instance is stopped/restarted |
| Elastic IP | Static IP created manually and associated with an instance or ENI. | Permanent until explicitly disassociated |
2. Private Connectivity to AWS Services (VPC Endpoints)
Allows accessing AWS services (S3, DynamoDB, etc.) without going through the Internet, staying on AWS’s private network.
flowchart LR
EC2[EC2 Instance\nPrivate Subnet] -->|Via VPC Endpoint\nAWS private network| S3A["(Amazon S3\nFree)"]
EC2 -->|Without VPC Endpoint\nVia NAT + Internet| IGW[Internet Gateway] --> Internet["(Internet)"] --> S3B["(Amazon S3\nNAT charged)"]
style EC2 fill:#bbf,stroke:#333
style S3A fill:#bfb,stroke:#333
style S3B fill:#faa,stroke:#333
3. VPC-to-VPC Connectivity (VPC Peering)
Direct connection between two VPCs allowing traffic to be routed between them using private IP addresses.
flowchart LR
VPC1[VPC A\n10.0.0.0/16] <-->|VPC Peering\nPeer Connection| VPC2[VPC B\n172.16.0.0/16]
style VPC1 fill:#bbf,stroke:#333
style VPC2 fill:#bfb,stroke:#333
VPC Peering constraints:
- The two VPCs’ CIDRs must not overlap
- Peering is not transitive: if A peers with B and B peers with C, A cannot reach C via B
4. On-Premises Connectivity (VPN and Direct Connect)
| Option | Description | Bandwidth | Latency |
|---|---|---|---|
| Site-to-Site VPN | Encrypted IPsec tunnel over the Internet between your on-premises network and VPC. | ~1.25 Gbps max | Variable (depends on Internet) |
| AWS Direct Connect | Dedicated physical network connection between your infrastructure and AWS via a partner. | 1, 10, 100 Gbps | Predictable and low |
flowchart LR
OnPrem[On-Premises Network] -->|Site-to-Site VPN\nIPsec over Internet| VGW[Virtual Private Gateway]
OnPrem -->|AWS Direct Connect\nDedicated physical link| DXL[Direct Connect Location] --> VGW
VGW --> VPC[AWS VPC]
style OnPrem fill:#f9f,stroke:#333
style VGW fill:#ffa,stroke:#333
style DXL fill:#fda,stroke:#333
style VPC fill:#bbf,stroke:#333
Complete VPC Architecture — Overview
flowchart TB
subgraph Internet["Internet"]
WWW["(Internet)"]
end
subgraph AWS_VPC["AWS VPC — 10.0.0.0/16"]
IGW[Internet Gateway]
subgraph AZ_A["Availability Zone A"]
subgraph PubSub_A["Public Subnet — 10.0.1.0/24"]
EC2_Pub_A[EC2 Web\n10.0.1.10\npublic IP]
end
subgraph PrivSub_A["Private Subnet — 10.0.2.0/24"]
EC2_Priv_A[EC2 App\n10.0.2.10\nprivate IP only]
end
end
subgraph AZ_B["Availability Zone B"]
subgraph PubSub_B["Public Subnet — 10.0.3.0/24"]
EC2_Pub_B[EC2 Web\n10.0.3.10\npublic IP]
end
subgraph PrivSub_B["Private Subnet — 10.0.4.0/24"]
EC2_Priv_B[EC2 App\n10.0.4.10\nprivate IP only]
end
end
NATGW[NAT Gateway\nPublic subnet]
VGW[Virtual Private Gateway]
end
OnPrem[On-Premises] -->|VPN / Direct Connect| VGW
VGW --> AWS_VPC
IGW <--> WWW
EC2_Pub_A --> IGW
EC2_Pub_B --> IGW
EC2_Priv_A --> NATGW
EC2_Priv_B --> NATGW
NATGW --> IGW
DNS in a VPC
Route 53 and the Private Resolver
AWS offers Route 53, a complete DNS solution providing:
- Domain registration
- DNS management (public and private hosted zones)
- Traffic routing (routing policies)
The key component for VPCs is the Route 53 Private Resolver (also called Route 53 Resolver).
Route 53 Private Resolver
This is a recursive DNS resolution service that:
- Receives DNS requests from endpoints
- Finds answers and returns them
- Is always assigned to the VPC network address + 2
Example:
VPC CIDR: 10.0.0.0/16
Route 53 Resolver: 10.0.0.2
Characteristics:
- Available in all VPC subnets
- Accessible from all Availability Zones
- Resolves cloud resource names in the VPC
- Can resolve on-premises environment names via resolver rules
VPC DNS Parameters
Two attributes control DNS behavior in a VPC:
| Attribute | Description | Default value |
|---|---|---|
enableDnsHostnames | Provides DNS names to instances that have a public IP | false (except default VPC) |
enableDnsSupport | Enables DNS resolution via Route 53 Resolver in the VPC | true |
Behavior based on configuration:
enableDnsSupport | enableDnsHostnames | Result |
|---|---|---|
true | true | Instances with public IP receive a public and private DNS hostname |
true | false | DNS resolution active but no hostname for instances |
false | N/A | No DNS resolution — must use IPs directly |
AWS DNS name formats:
Public DNS: ec2-54-214-43-21.compute-1.amazonaws.com
Private DNS: ip-10-0-1-10.ec2.internal
Best practice: For most architectures, keep
enableDnsSupport = trueand enableenableDnsHostnamesif you need public DNS names for your instances.
Private Hosted Zones
A Private Hosted Zone allows using custom DNS names for resources internal to the VPC:
ec2-web-01.internal.mycompany.com → 10.0.1.10
db-primary.internal.mycompany.com → 10.0.2.20
flowchart LR
EC2[EC2 Instance] -->|DNS query\ndb.internal.corp| R53[Route 53 Resolver\n10.0.0.2]
R53 -->|Lookup in\nPrivate Hosted Zone| PHZ[Private Hosted Zone\ninternal.corp]
PHZ -->|Returns 10.0.2.20| R53
R53 -->|10.0.2.20| EC2
EC2 -->|Direct connection| DB[RDS Database\n10.0.2.20]
style EC2 fill:#bbf,stroke:#333
style R53 fill:#ffa,stroke:#333
style PHZ fill:#bfb,stroke:#333
style DB fill:#f9f,stroke:#333
Module 2 — Creating Your First VPC
VPC Configuration
VPC Creation Steps
flowchart TD
A[1. Choose the AWS Region] --> B[2. Assign a CIDR block to the VPC]
B --> C[3. Create Subnets\npublic and private]
C --> D[4. Create an Internet Gateway]
D --> E[5. Attach IGW to VPC]
E --> F[6. Create Route Tables\npublic and private]
F --> G[7. Add routes to Route Tables]
G --> H[8. Associate Subnets with Route Tables]
H --> I[9. Optional: NAT Gateway\nfor private subnets]
IP Address Ranges for the VPC
For IPv4, you can assign a range between:
- Maximum:
/16→ 65,536 IP addresses - Minimum:
/28→ 16 IP addresses
Recommendation: Use the private address ranges defined by RFC 1918:
| RFC 1918 Range | CIDR | Number of addresses |
|---|---|---|
10.0.0.0 – 10.255.255.255 | 10.0.0.0/8 | 16,777,216 |
172.16.0.0 – 172.31.255.255 | 172.16.0.0/12 | 1,048,576 |
192.168.0.0 – 192.168.255.255 | 192.168.0.0/16 | 65,536 |
Reserved IP Addresses in a Subnet
AWS reserves 5 IP addresses in every subnet. Example with a 10.0.0.0/24 subnet:
| IP Address | Reserved use |
|---|---|
10.0.0.0 | Network address |
10.0.0.1 | VPC router |
10.0.0.2 | Route 53 DNS Resolver |
10.0.0.3 | Reserved for future AWS use |
10.0.0.255 | Broadcast address |
Impact on sizing: A
/24subnet offers 256 IPs total, but only 251 usable IPs after the 5 reserved ones.
/24 Subnet = 256 total IPs
256 - 5 (AWS reserved) = 251 IPs available for your resources
Availability Zone Mapping
AWS uses random mapping per account for AZs:
Your account: us-east-1a → Physical Data Center X
Other account: us-east-1a → Physical Data Center Y
Demo: Creating Your First VPC
Step 1 — VPC Creation
AWS Console → Search "VPC" → VPC Dashboard → Your VPCs → Create VPC
Parameters:
| Field | Value | Description |
|---|---|---|
| Creation mode | VPC only | Manual step-by-step creation (vs “VPC and more” wizard) |
| Name tag | myfirstvpc | VPC name |
| IPv4 CIDR | 10.0.0.0/16 | VPC network range |
| IPv6 | Disabled | Not used in this course |
| Tenancy | Default | Shared infrastructure (vs Dedicated for dedicated hardware) |
Step 2 — Subnet Creation
First subnet (public):
| Field | Value |
|---|---|
| VPC | myfirstvpc |
| Name | myfirstsubnet |
| Availability Zone | us-east-1a |
| IPv4 CIDR | 10.0.1.0/24 |
Second subnet:
| Field | Value |
|---|---|
| VPC | myfirstvpc |
| Name | mysecondsubnet |
| Availability Zone | us-east-1b |
| IPv4 CIDR | 10.0.2.0/24 |
Best practice: Place your subnets in different AZs for high availability.
Step 3 — Creating and Attaching the Internet Gateway
VPC Dashboard → Internet Gateways → Create internet gateway
Name tag: myfirstvpcigw
→ Actions → Attach to VPC → myfirstvpc → Attach internet gateway
A VPC can only have one Internet Gateway attached.
Architecture After Creation
flowchart TB
subgraph VPC["VPC: myfirstvpc — 10.0.0.0/16"]
subgraph AZ_A[us-east-1a]
S1[myfirstsubnet\n10.0.1.0/24]
end
subgraph AZ_B[us-east-1b]
S2[mysecondsubnet\n10.0.2.0/24]
end
IGW[Internet Gateway\nmyfirstvpcigw]
end
Internet["(Internet)"] <--> IGW
Demo: Configuring VPC Routing
Creating the Route Table
VPC Dashboard → Route tables → Create route table
Name: myfirstvpcrt | VPC: myfirstvpc
Adding a Route to the Internet
| Destination | Target | Description |
|---|---|---|
10.0.0.0/16 | local | Local traffic within the VPC (automatic route, non-modifiable) |
0.0.0.0/0 | Internet Gateway myfirstvpcigw | All non-local traffic goes to Internet |
Associating Subnets with the Route Table
Route table → Subnet associations → Edit subnet associations
→ Select myfirstsubnet and mysecondsubnet → Save associations
By associating subnets with this route table, you are turning them into public subnets.
Route Tables — How They Work
flowchart LR
EC2[EC2 in\nmyfirstsubnet] --> RT{Route Table\nmyfirstvpcrt}
RT -->|Destination 10.0.0.0/16\nlongest prefix match| Local[Other EC2\nin the VPC]
RT -->|Destination 0.0.0.0/0\ndefault route| IGW[Internet Gateway]
IGW --> Internet["(Internet)"]
Route matching rule: AWS uses the “longest prefix match” rule — the most specific route is always preferred.
Traffic to 10.0.1.5 → matches 10.0.0.0/16 (local) ✓
Traffic to 8.8.8.8 → matches 0.0.0.0/0 (IGW) ✓
Enabling Automatic Public IPs
VPC Dashboard → Subnets → myfirstsubnet → Actions → Edit subnet settings
→ Enable auto-assign public IPv4 address → Save
Module 3 — Access Control in the VPC
Understanding Network Access Control Lists (NACLs)
Definition
Network Access Control Lists (NACLs) are firewalls at the subnet level that filter inbound and outbound traffic. They represent one of the defense-in-depth options for protecting your AWS cloud resources.
Key characteristic: NACLs are STATELESS
Stateless Behavior
“Stateless” means the NACL does not remember established connections. Each packet is evaluated independently.
sequenceDiagram
participant Client as Client\nport 54321
participant NACL as Subnet NACL
participant EC2 as EC2 Instance
Client->>NACL: TCP SYN to port 22
Note over NACL: INBOUND rule: TCP 22 ALLOW
NACL->>EC2: Allowed
EC2->>NACL: TCP reply to port 54321
Note over NACL: OUTBOUND rule: port 54321 absent
NACL--xClient: Blocked by implicit DENY
Note over NACL: After adding TCP 1024-65535 outbound
EC2->>NACL: TCP reply to port 54321
Note over NACL: OUTBOUND rule: TCP 1024-65535 ALLOW
NACL->>Client: Allowed
Ephemeral Ports:
- Range:
1024to65535 - Temporary ports automatically chosen by the client OS
- Must be explicitly allowed in NACL OUTBOUND rules
NACL Rule Structure
| Rule | Type | Protocol | Port | Source/Dest | Action |
|---|---|---|---|---|---|
100 | SSH | TCP | 22 | 0.0.0.0/0 | ALLOW |
200 | Custom TCP | TCP | 1024-65535 | 0.0.0.0/0 | ALLOW |
* | All Traffic | All | All | 0.0.0.0/0 | DENY |
NACL rules:
- Rules are numbered and evaluated in ascending order
- The first matching rule is applied
- An implicit DENY rule (
*) is always present at the end - Rules can be of type ALLOW or DENY
- Rules apply separately for INBOUND and OUTBOUND traffic
Default NACL vs Custom NACL
| Default NACL | Custom NACL | |
|---|---|---|
| INBOUND traffic | ALLOW all (rule 100) | DENY all (implicit default) |
| OUTBOUND traffic | ALLOW all (rule 100) | DENY all (implicit default) |
| Assignment | All subnets without explicit NACL | Explicitly associated subnets |
Understanding Security Groups
Definition
Security Groups are virtual firewalls at the EC2 instance level (ENI — Elastic Network Interface level). They control inbound and outbound traffic to an instance.
Key characteristic: Security Groups are STATEFUL
Stateful Behavior
“Stateful” means the Security Group remembers established connections. If you allow inbound traffic, return traffic is automatically allowed.
sequenceDiagram
participant Client
participant SG as Security Group
participant EC2 as EC2 Instance
Client->>SG: TCP SYN to port 22
Note over SG: INBOUND rule: TCP 22 ALLOW
SG->>EC2: Allowed
EC2->>SG: TCP reply — stateful
Note over SG: Connection known, return automatically allowed
SG->>Client: Allowed without outbound rule needed
Default Behavior
| Scenario | INBOUND traffic | OUTBOUND traffic |
|---|---|---|
| Default VPC Security Group | ALLOW all | ALLOW all |
| Newly created Security Group | DENY all (implicit) | ALLOW all |
Security Group Rules
| Aspect | Detail |
|---|---|
| Rule types | ALLOW only (no DENY possible) |
| Evaluation | All rules evaluated together (no numbered order) |
| Instances per SG | Multiple instances can share the same SG |
| SGs per instance | Multiple SGs can be assigned (aggregated rules) |
| Reference to another SG | Possible — SG as source/destination instead of an IP |
Common INBOUND rule examples:
| Type | Protocol | Port | Source | Use |
|---|---|---|---|---|
| SSH | TCP | 22 | 10.0.0.0/8 | SSH access from internal network |
| HTTP | TCP | 80 | 0.0.0.0/0 | Public web access |
| HTTPS | TCP | 443 | 0.0.0.0/0 | Public secure web access |
| MySQL/Aurora | TCP | 3306 | sg-app-xxxxx | DB accessible from app layer only |
Security Group Cross-References
flowchart TB
Internet["(Internet)"] -->|TCP 443| ALB[Load Balancer\nSG: alb-sg]
ALB -->|TCP 8080\nSource: alb-sg| EC2_App[EC2 Application\nSG: app-sg]
EC2_App -->|TCP 3306\nSource: app-sg| RDS[RDS Database\nSG: db-sg]
style Internet fill:#ddd,stroke:#333
style ALB fill:#ffa,stroke:#333
style EC2_App fill:#bbf,stroke:#333
style RDS fill:#f9f,stroke:#333
| Security Group | INBOUND Rule | Description |
|---|---|---|
alb-sg | TCP 443 from 0.0.0.0/0 | Internet accesses the load balancer |
app-sg | TCP 8080 from alb-sg | Only the LB accesses the apps |
db-sg | TCP 3306 from app-sg | Only apps access the DB |
Demo: Controlling Access to a Web Server with NACL and Security Group
Prerequisites
- A VPC with an Internet Gateway
- A public subnet with route
0.0.0.0/0to the IGW - The subnet configured for auto-assignment of public IP
Demo Steps
1. Launch an Amazon Linux EC2 instance
EC2 → Launch Instance → Amazon Linux 2 AMI
→ Choose myfirstsubnet | Create a new Security Group with no rules
2. SSH test (expected: FAILURE)
ssh -i "my-key.pem" ec2-user@<PUBLIC_IP>
# Result: Connection timed out
# Reason: Port 22 not allowed in the Security Group
3. Add TCP 22 to the Security Group
EC2 → Security Groups → Inbound rules → Edit inbound rules → Add rule
Type: SSH | Port: 22 | Source: My IP → Save rules
4. SSH test (expected: SUCCESS)
ssh -i "my-key.pem" ec2-user@<PUBLIC_IP>
# Result: Connection established
5. Create a NACL and associate it with the subnet
VPC → Network ACLs → Create network ACL
Name: myfirstnacl | VPC: myfirstvpc
→ Subnet associations → Select myfirstsubnet → Save
6. SSH test (expected: FAILURE)
# Result: Connection timed out
# Reason: The new NACL blocks all traffic by default
7. Configure NACL rules
INBOUND rules:
| Rule | Type | Protocol | Port | Source | Action |
|---|---|---|---|---|---|
100 | SSH | TCP | 22 | 0.0.0.0/0 | ALLOW |
* | All traffic | All | All | 0.0.0.0/0 | DENY |
OUTBOUND rules:
| Rule | Type | Protocol | Port | Destination | Action |
|---|---|---|---|---|---|
100 | Custom TCP | TCP | 1024-65535 | 0.0.0.0/0 | ALLOW |
* | All traffic | All | All | 0.0.0.0/0 | DENY |
8. Final SSH test (expected: SUCCESS)
ssh -i "my-key.pem" ec2-user@<PUBLIC_IP>
# Result: Connection established
Complete Architecture with NACLs and Security Groups
flowchart TB
Internet["(Internet)"] -->|TCP 22| IGW[Internet Gateway]
subgraph VPC["VPC: myfirstvpc — 10.0.0.0/16"]
IGW --> NACL
subgraph PublicSubnet["Public Subnet — 10.0.1.0/24"]
NACL{"NACL: myfirstnacl\nStateless\nIN: TCP 22 ALLOW\nOUT: TCP 1024-65535 ALLOW"}
subgraph SG["Security Group — Stateful\nIN: TCP 22 ALLOW"]
EC2[EC2 Instance\nAmazon Linux]
end
end
NACL --> SG
end
Traffic processing order:
Inbound: Internet → IGW → NACL (Inbound) → Security Group (Inbound) → EC2
Outbound: EC2 → Security Group (Outbound, stateful) → NACL (Outbound) → IGW → Internet
Module 4 — Data Transfer Costs
Overview of Data Transfer Costs in the VPC
Billed Data Flows
flowchart TB
subgraph Region_1["Region 1 — us-east-1"]
subgraph AZ_A[Availability Zone A]
EC2_A[EC2 Instance A]
end
subgraph AZ_B[Availability Zone B]
EC2_B[EC2 Instance B]
end
NATGW[NAT Gateway]
end
subgraph Region_2["Region 2 — eu-west-1"]
EC2_C[EC2 Instance C]
end
Internet["(Internet)"]
EC2_A <-->|Billed inter-AZ\n~$0.01/GB| EC2_B
EC2_A -->|Billed outbound\n~$0.09/GB| Internet
Internet -->|Free inbound| EC2_A
EC2_A <-->|Billed inter-Region| EC2_C
Complete Data Transfer Cost Table
| Scenario | Cost | Notes |
|---|---|---|
| Inbound traffic to AWS from Internet | Free | |
| Outbound traffic to Internet | ~$0.09/GB | Varies by region |
| Traffic between instances in the same AZ | Free | If private IPs used |
| Traffic between different AZs (same Region) | ~$0.01/GB | Charged in both directions |
| Traffic between different Regions | ~$0.02/GB | Varies by region |
| NAT Gateway — processing fees | ~$0.045/GB | + hourly fees $0.045/h |
| VPC Endpoint to S3/DynamoDB | Free | Also saves NAT fees |
| Direct Connect — data transfer out | ~$0.02/GB | Cheaper than Internet at volume |
Network Cost Optimization
Recommendations:
- Use private IPs for communication between instances in the same AZ
- Deploy VPC Endpoints to access S3, DynamoDB without going through NAT
- Use Direct Connect for large volumes of traffic from on-premises
- AWS Cost Explorer to analyze your actual transfer costs
Warning: Never put all your instances in a single AZ to reduce costs! Amazon’s SLAs only cover multi-AZ or multi-Region deployments.
Recommended vs Avoid Architecture:
flowchart LR
subgraph Correct["CORRECT Architecture — High availability"]
direction TB
subgraph AZ1[AZ-A]
W1[Web]
A1[App]
D1[DB Primary]
end
subgraph AZ2[AZ-B]
W2[Web]
A2[App]
D2[DB Standby]
end
end
subgraph Incorrect["Architecture TO AVOID — Single point of failure"]
direction TB
subgraph AZ3[AZ-A ONLY]
W3[Web]
A3[App]
D3[DB]
end
end
Deep Dive — CIDR Planning and Subnet Design
CIDR Planning for Enterprise VPCs
CIDR Planning Principles
Good CIDR planning is critical — mistakes are difficult to correct after creation.
Golden rules:
- Never overlap CIDR ranges between VPCs if you plan Peering or Transit Gateway
- Plan for growth: choose a larger CIDR than necessary
- Segment by environment: prod, staging, dev in separate ranges
- Avoid
172.17.0.0/16(Docker Bridge Network default range) - Avoid
172.16.0.0/12if Docker is used extensively
Multi-VPC Enterprise Address Plan
flowchart TB
subgraph Corp["Enterprise space — 10.0.0.0/8"]
subgraph Prod["Production — 10.0.0.0/13"]
P_US[VPC Prod US-East\n10.0.0.0/16]
P_EU[VPC Prod EU-West\n10.1.0.0/16]
P_AP[VPC Prod AP-SE\n10.2.0.0/16]
end
subgraph Stage["Staging — 10.8.0.0/13"]
S_US[VPC Staging US\n10.8.0.0/16]
end
subgraph Dev["Development — 10.16.0.0/13"]
D_US[VPC Dev US\n10.16.0.0/16]
end
subgraph Shared["Shared Services — 10.24.0.0/13"]
SH[VPC Shared\n10.24.0.0/16]
end
end
Detailed design of a Production VPC:
Production VPC : 10.0.0.0/16 (65,536 IPs)
├── AZ-A Public : 10.0.0.0/24 (251 IPs) → Load Balancers, NAT GW
├── AZ-B Public : 10.0.1.0/24 (251 IPs) → Load Balancers, NAT GW
├── AZ-A Private : 10.0.10.0/23 (507 IPs) → Application servers
├── AZ-B Private : 10.0.12.0/23 (507 IPs) → Application servers
├── AZ-A Database : 10.0.20.0/24 (251 IPs) → RDS, ElastiCache
├── AZ-B Database : 10.0.21.0/24 (251 IPs) → RDS, ElastiCache
└── Reserved : 10.0.128.0/17 (for future expansion)
Subnet Design Patterns
3-Tier Pattern (3-tier architecture)
flowchart TB
Internet["(Internet)"] --> IGW[Internet Gateway]
subgraph VPC["VPC — 10.0.0.0/16"]
IGW --> ALB[Application Load Balancer]
subgraph Public["Public Subnets — Web Tier"]
ALB
NATGW[NAT Gateway]
end
subgraph Private["Private Subnets — Application Tier"]
APP_A[EC2 Auto Scaling\nAZ-A\n10.0.10.x]
APP_B[EC2 Auto Scaling\nAZ-B\n10.0.11.x]
end
subgraph Data["Isolated Subnets — Data Tier"]
DB_A[RDS Primary\nAZ-A\n10.0.20.x]
DB_B[RDS Standby\nAZ-B\n10.0.21.x]
end
ALB -->|HTTP 8080| APP_A & APP_B
APP_A & APP_B -->|TCP 3306| DB_A
APP_A & APP_B -->|Updates| NATGW
NATGW --> IGW
end
Subnet Types
| Subnet Type | Internet Access | Route to IGW | Route to NAT GW | Typical use |
|---|---|---|---|---|
| Public | Inbound and outbound | Yes (0.0.0.0/0 → IGW) | No | Load Balancers, Bastion hosts, NAT Gateway |
| Private | Outbound only via NAT | No | Yes (0.0.0.0/0 → NATGW) | Application servers, APIs |
| Isolated | None | No | No | Databases, internal storage |
NAT Gateway
Why a NAT Gateway?
Instances in private subnets don’t have a public IP. The NAT Gateway allows them to initiate outbound connections (updates, external APIs) without being exposed to the Internet.
flowchart LR
subgraph Private["Private Subnet"]
EC2[EC2 Instance\n10.0.10.5]
end
subgraph Public["Public Subnet"]
NATGW[NAT Gateway\nElastic IP: 52.1.2.3]
end
IGW[Internet Gateway]
Internet["(Internet)"]
EC2 -->|1 — 10.0.10.5 to 8.8.8.8| NATGW
NATGW -->|2 — NAT: 52.1.2.3 to 8.8.8.8| IGW
IGW --> Internet
Internet -->|3 — Reply to 52.1.2.3| IGW
IGW -->|4 — De-NAT to 10.0.10.5| NATGW
NATGW -->|5 — Return| EC2
NAT Gateway characteristics:
| Aspect | Detail |
|---|---|
| Type | Managed by AWS (fully managed) |
| High availability | Redundant in an AZ — deploy one per AZ for HA |
| Bandwidth | Up to 100 Gbps (auto-scaling) |
| Elastic IP | Requires a fixed Elastic IP |
High availability deployment — One NAT Gateway per AZ:
flowchart TB
subgraph VPC[VPC]
subgraph AZ_A[AZ-A]
NATGW_A[NAT GW A\nEIP: 52.1.x.x]
PRIV_A[Private Subnet A\nRoute: 0.0.0.0/0 to NATGW-A]
end
subgraph AZ_B[AZ-B]
NATGW_B[NAT GW B\nEIP: 52.2.x.x]
PRIV_B[Private Subnet B\nRoute: 0.0.0.0/0 to NATGW-B]
end
NATGW_A --> IGW[Internet Gateway]
NATGW_B --> IGW
end
IGW --> Internet["(Internet)"]
If you only have one NAT Gateway in AZ-A and AZ-A goes down, AZ-B instances also lose Internet access.
Deep Dive — VPC Endpoints and PrivateLink
Gateway-Type VPC Endpoints
A Gateway Endpoint allows accessing S3 and DynamoDB without going through the Internet or NAT Gateway.
Supported services: Amazon S3, Amazon DynamoDB only.
flowchart LR
subgraph VPC[VPC]
EC2[EC2 in\nPrivate Subnet] -->|Via Gateway Endpoint\nAWS private network| GWE[Gateway Endpoint]
end
GWE --> S3["(Amazon S3\nor DynamoDB)"]
style GWE fill:#ffa,stroke:#333
style S3 fill:#bfb,stroke:#333
Advantages:
- Free (no additional charges)
- Eliminates the need for NAT Gateway for S3/DynamoDB
- Reduces transfer costs
- Improves security (traffic never leaves the AWS network)
Interface-Type VPC Endpoints (PrivateLink)
An Interface Endpoint creates an ENI in your subnet with a private IP representing an AWS service.
Supported services: 100+ AWS services (SSM, Secrets Manager, CloudWatch, SNS, SQS, Kinesis, etc.) + third-party services.
flowchart LR
subgraph VPC["VPC — Your network"]
EC2[EC2 Instance] --> ENI[ENI\nInterface Endpoint\nPrivate IP: 10.0.2.50]
end
subgraph AWS_Network[AWS Private Network]
ENI -->|PrivateLink| SVC[AWS Service\nor Third-party Service]
end
style ENI fill:#ffa,stroke:#333
style SVC fill:#bfb,stroke:#333
Gateway vs Interface Endpoint comparison:
| Aspect | Gateway Endpoint | Interface Endpoint (PrivateLink) |
|---|---|---|
| Type of resource created | Route in Route Table | ENI with private IP in subnet |
| Supported services | S3, DynamoDB only | 100+ AWS and third-party services |
| Cost | Free | Hourly fees + per-GB fees |
| DNS | Automatic resolution | Private DNS (must be enabled) |
| Accessibility from on-premises | No | Yes (via Direct Connect or VPN) |
| Accessibility from another VPC | Not by default | Yes (via Peering) |
Deep Dive — Advanced VPC-to-VPC Connectivity
VPC Peering
VPC Peering is a direct network connection between two VPCs using private IP addresses.
Characteristics:
- Works intra-region and inter-region
- Works across AWS accounts
- Traffic does not pass through the Internet
- Not transitive
flowchart TB
subgraph ScenA["Scenario A — Direct peering A-B and B-C"]
A1[VPC-A\n10.0.0.0/16] <-->|Peering AB| B1[VPC-B\n10.1.0.0/16]
B1 <-->|Peering BC| C1[VPC-C\n10.2.0.0/16]
A1 -.-x|No route - Not transitive| C1
end
subgraph ScenB["Scenario B — Full mesh (3 peerings)"]
A2[VPC-A] <-->|Peering AB| B2[VPC-B]
B2 <-->|Peering BC| C2[VPC-C]
A2 <-->|Peering AC| C2
end
VPC Peering limitations:
- No overlapping CIDRs allowed
- N VPCs require N×(N-1)/2 peerings for a full mesh
- 3 VPCs = 3 peerings
- 10 VPCs = 45 peerings
- 50 VPCs = 1,225 peerings
Transit Gateway
The Transit Gateway (TGW) is a central network transit hub that interconnects VPCs, VPN, and Direct Connect.
flowchart TB
subgraph TGW_Hub["Transit Gateway — Central Hub"]
TGW[Transit Gateway\nCentralized routing]
end
VPC_Prod[VPC Production\n10.0.0.0/16] <-->|Attachment| TGW
VPC_Stage[VPC Staging\n10.1.0.0/16] <-->|Attachment| TGW
VPC_Dev[VPC Dev\n10.2.0.0/16] <-->|Attachment| TGW
VPC_Shared[VPC Shared Services\n10.3.0.0/16] <-->|Attachment| TGW
VPN[Site-to-Site VPN\nOn-Premises] <-->|Attachment| TGW
DX[Direct Connect\nGateway] <-->|Attachment| TGW
style TGW fill:#ffa,stroke:#333
Isolation with multiple TGW Route Tables:
flowchart TB
VPC_Prod[VPC Production] -->|Attachment| RT_Prod[TGW Route Table\nPRODUCTION\nRoute to Shared only]
VPC_Stage[VPC Staging] -->|Attachment| RT_NonProd[TGW Route Table\nNON-PRODUCTION\nRoute to Shared only]
VPC_Dev[VPC Dev] -->|Attachment| RT_NonProd
VPC_Shared[VPC Shared Services] -->|Attachment| RT_Prod
VPC_Shared -->|Attachment| RT_NonProd
RT_Prod -.-x|Isolated — Prod cannot see Non-Prod| RT_NonProd
VPC Peering vs Transit Gateway Comparison
| Criterion | VPC Peering | Transit Gateway |
|---|---|---|
| Connectivity model | Point-to-point (mesh) | Hub-and-spoke |
| Transitivity | Not transitive | Transitive (controlled by route tables) |
| Management complexity | Complex for 10+ VPCs | Simple and centralized |
| Bandwidth | Not limited by peering | Up to 50 Gbps per AZ |
| Cost | Inter-AZ data fees | Data fees + attachment fees ($0.05/h) |
| Inter-Region | Yes (Inter-Region Peering) | Yes (TGW inter-region peering) |
| Inter-Account | Yes | Yes (RAM sharing) |
| When to use | 2-5 VPCs, low complexity | 5+ VPCs, hub-and-spoke architecture |
Decision rule:
5 VPCs or fewer with simple connections → VPC Peering
5+ VPCs, complex connections → Transit Gateway
Centralized on-premises connection → Transit Gateway
Deep Dive — Route 53 and Routing Policies
Route 53 Overview
Amazon Route 53 is AWS’s DNS service. It offers domain registration, public and private DNS resolution, and intelligent traffic routing.
Routing Policies
flowchart TB
R53[Route 53\nRouting Policy] --> Simple[Simple Routing]
R53 --> Weighted[Weighted Routing]
R53 --> Failover[Failover Routing]
R53 --> Latency[Latency-Based Routing]
R53 --> Geo[Geolocation Routing]
R53 --> GeoProx[Geoproximity Routing]
R53 --> MultiVal[Multivalue Answer]
R53 --> IPBased[IP-Based Routing]
style R53 fill:#ffa,stroke:#333
| Policy | Description | Use case |
|---|---|---|
| Simple | Single DNS record, one or more IPs | Simple application, no health check |
| Weighted | Distributes traffic by weight (e.g.: 70% / 30%) | Blue/green deployments, A/B testing |
| Failover | Routes to primary, switches to secondary if health check fails | Active/passive high availability |
| Latency-Based | Routes to AWS Region with lowest latency | Global performance optimization |
| Geolocation | Routes based on user’s geographic location | GDPR compliance, localized content |
| Geoproximity | Routes based on location with adjustable bias | Fine-grained geographic routing control |
| Multivalue Answer | Returns up to 8 healthy IPs at random | Simple load balancing with health check |
| IP-Based | Routes based on client’s source IP CIDR | Optimization for known clients |
Example — Weighted Routing (Blue/Green Deployment)
flowchart LR
User[User] -->|DNS app.example.com| R53[Route 53\nWeighted]
R53 -->|90% of traffic| BlueEnv[Blue Environment\nCurrent version]
R53 -->|10% of traffic| GreenEnv[Green Environment\nNew version]
style BlueEnv fill:#bbf,stroke:#333
style GreenEnv fill:#bfb,stroke:#333
Example — Failover Routing
sequenceDiagram
participant User as User
participant R53 as Route 53
participant Primary as Primary Region\nUS-East-1
participant Secondary as Secondary Region\nEU-West-1
participant HC as Health Check
HC->>Primary: Check every 30s
User->>R53: DNS resolution
R53->>User: Primary IP (Primary OK)
User->>Primary: Request
Note over HC,Primary: Primary goes down
HC->>Primary: Check fails 3 times
HC->>R53: Alert: Primary unhealthy
User->>R53: DNS resolution
R53->>User: Secondary IP (Failover activated)
User->>Secondary: Redirected request
Deep Dive — Elastic Load Balancing
Overview of AWS Load Balancers
AWS offers 4 types of Load Balancers:
flowchart TB
ELB[Elastic Load Balancing\nService] --> ALB[Application\nLoad Balancer]
ELB --> NLB[Network\nLoad Balancer]
ELB --> GWLB[Gateway\nLoad Balancer]
ELB --> CLB[Classic\nLoad Balancer]
ALB -->|Layer 7 HTTP/HTTPS WebSockets| App[Web Applications]
NLB -->|Layer 4 TCP/UDP/TLS| HighPerf[High Performance]
GWLB -->|Layer 3 IP| Security[Network Appliances\nFirewall/IDS/IPS]
CLB -->|Layer 4 and 7 Legacy| Legacy[Legacy EC2-Classic Apps]
style ALB fill:#bbf,stroke:#333
style NLB fill:#bfb,stroke:#333
style GWLB fill:#ffa,stroke:#333
style CLB fill:#ddd,stroke:#333
Load Balancer Comparison Table
| Criterion | ALB | NLB | GWLB | CLB |
|---|---|---|---|---|
| OSI Layer | 7 (Application) | 4 (Transport) | 3 (Network) | 4 and 7 |
| Protocols | HTTP, HTTPS, WebSocket, gRPC | TCP, UDP, TLS | IP | HTTP, HTTPS, TCP, SSL |
| Latency | Milliseconds | Ultra-low microseconds | Depends on appliance | Milliseconds |
| Static IPs | No (DNS only) | Yes (per AZ) | Yes | No |
| Elastic IP | No | Yes | No | No |
| Advanced routing | Yes (path, host, header, query) | No | No | No |
| SSL Termination | Yes | Yes (TLS passthrough possible) | No | Yes |
| Target types | Instance, IP, Lambda | Instance, IP | Instance, IP | Instance |
| Preserve client IP | Via X-Forwarded-For | Yes (natively) | Yes | Via X-Forwarded-For |
| Sticky sessions | Yes (cookies) | Yes | No | Yes |
| WebSockets | Yes | Yes | No | No |
Application Load Balancer (ALB) — Layer 7 Routing
flowchart LR
User[Client] --> ALB[ALB\napp.example.com]
ALB -->|GET /api/*| TG_API[Target Group\nAPI Servers\nPort 8080]
ALB -->|GET /images/*| TG_Static[Target Group\nStatic Servers\nPort 80]
ALB -->|Host: admin.example.com| TG_Admin[Target Group\nAdmin App\nPort 9090]
ALB -->|GET /* by default| TG_Web[Target Group\nWeb Servers\nPort 80]
style ALB fill:#bbf,stroke:#333
ALB routing rules:
| Condition | Example | Action |
|---|---|---|
| Path-based | /api/* | Forward to API Target Group |
| Host-based | admin.example.com | Forward to Admin Target Group |
| Header-based | X-Version: v2 | Forward to v2 Target Group |
| Query string | ?env=prod | Forward to Prod Target Group |
| Redirect | HTTP to HTTPS | Redirect 301 |
| Fixed response | /health | Return 200 OK without backend |
Gateway Load Balancer (GWLB) — Network Appliances
flowchart TB
Internet["(Internet)"] --> GWLB_EP[GWLB Endpoint\nProd VPC]
GWLB_EP --> GWLB[Gateway Load Balancer\nSecurity VPC]
GWLB --> FW1[Firewall Instance 1]
GWLB --> FW2[Firewall Instance 2]
FW1 -->|Inspected traffic returned| GWLB
FW2 -->|Inspected traffic returned| GWLB
GWLB --> GWLB_EP
GWLB_EP --> APP[Application\nProd VPC]
style GWLB fill:#ffa,stroke:#333
style FW1 fill:#faa,stroke:#333
style FW2 fill:#faa,stroke:#333
Multi-AZ Architecture with ALB
flowchart TB
Internet["(Internet)"] --> Route53[Route 53\napp.example.com]
Route53 --> ALB[Application Load Balancer\nMulti-AZ]
subgraph VPC["VPC — 10.0.0.0/16"]
subgraph AZ_A[Availability Zone A]
ALB_Node_A[ALB Node\n10.0.1.x]
EC2_A1[EC2 App\n10.0.10.10]
EC2_A2[EC2 App\n10.0.10.11]
end
subgraph AZ_B[Availability Zone B]
ALB_Node_B[ALB Node\n10.0.2.x]
EC2_B1[EC2 App\n10.0.11.10]
EC2_B2[EC2 App\n10.0.11.11]
end
ALB --> ALB_Node_A & ALB_Node_B
ALB_Node_A --> EC2_A1 & EC2_A2
ALB_Node_B --> EC2_B1 & EC2_B2
end
Deep Dive — Complete Multi-AZ Architecture
Reference Architecture — 3-Tier Web Application
flowchart TB
Users[Internet Users] --> R53[Route 53\nDNS + Health Checks]
R53 --> IGW[Internet Gateway]
IGW --> ALB[Application Load Balancer\nSG: TCP 443 from 0.0.0.0/0]
subgraph VPC["VPC — 10.0.0.0/16"]
subgraph Public_A["Public Subnet AZ-A — 10.0.1.0/24"]
ALB_A[ALB Node]
NATGW_A[NAT GW A]
end
subgraph Public_B["Public Subnet AZ-B — 10.0.2.0/24"]
ALB_B[ALB Node]
NATGW_B[NAT GW B]
end
subgraph Private_A["Private Subnet AZ-A — 10.0.10.0/24"]
APP_A[EC2 Auto Scaling\nSG: TCP 8080 from alb-sg]
end
subgraph Private_B["Private Subnet AZ-B — 10.0.11.0/24"]
APP_B[EC2 Auto Scaling\nSG: TCP 8080 from alb-sg]
end
subgraph DB_A["DB Subnet AZ-A — 10.0.20.0/24"]
RDS_Primary[RDS Primary\nSG: TCP 3306 from app-sg]
end
subgraph DB_B["DB Subnet AZ-B — 10.0.21.0/24"]
RDS_Standby[RDS Standby\nMulti-AZ Failover]
end
VPC_EP[VPC Endpoint\nS3 Gateway]
end
ALB --> APP_A & APP_B
APP_A & APP_B -->|Updates via NAT| NATGW_A & NATGW_B
NATGW_A & NATGW_B --> IGW
APP_A & APP_B --> RDS_Primary
RDS_Primary <-->|Sync replication| RDS_Standby
APP_A & APP_B --> VPC_EP
VPC_EP --> S3["(Amazon S3)"]
Quick Reference — NACL vs Security Group Comparison
| Characteristic | NACL | Security Group |
|---|---|---|
| Application level | Subnet | Instance (ENI) |
| Stateful / Stateless | Stateless | Stateful |
| Rule types | ALLOW and DENY | ALLOW only |
| Evaluation order | Numbered, ascending order | All rules evaluated together |
| Final rule | Implicit DENY (*) | Implicit DENY |
| Default NACL/SG in VPC | ALLOW all (IN and OUT) | ALLOW all (IN and OUT) |
| Newly created NACL/SG | DENY all by default | DENY IN, ALLOW OUT |
| Ephemeral ports | Must be configured explicitly | Handled automatically (stateful) |
| Application | All instances in the subnet | Specifically assigned instances |
| Reference to another SG | No | Yes |
| Explicit deny example | Possible (DENY specific IP) | Not supported |
| Recommended use | Coarse filtering, network layer | Fine-grained control at instance level |
Complete Traffic Flow
flowchart LR
Internet["(Internet)"] --> IGW[Internet Gateway]
IGW --> NACL_IN{NACL\nInbound}
NACL_IN -->|ALLOW| SG_IN{Security Group\nInbound}
SG_IN -->|ALLOW| EC2[EC2 Instance]
EC2 --> SG_OUT{Security Group\nOutbound\nStateful: auto if connection known}
SG_OUT --> NACL_OUT{NACL\nOutbound}
NACL_OUT -->|ALLOW| IGW
style NACL_IN fill:#ffa,stroke:#333
style NACL_OUT fill:#ffa,stroke:#333
style SG_IN fill:#bbf,stroke:#333
style SG_OUT fill:#bbf,stroke:#333
Defense-in-Depth Strategy
Layer 1: NACLs → Broad rules at subnet level (block malicious IPs, unused ports)
Layer 2: SGs → Fine-grained control at instance level (allow only what's necessary)
Layer 3: WAF/App → Application filtering, authentication, authorization in the application
Reserved IP Addresses in a Subnet
For every AWS subnet, 5 addresses are always reserved.
Example with 10.0.0.0/24:
10.0.0.0 → Network address
10.0.0.1 → VPC router (AWS)
10.0.0.2 → Route 53 DNS Resolver (VPC network + 2)
10.0.0.3 → Reserved by AWS (future use)
10.0.0.4 → First available address
...
10.0.0.254 → Last available address
10.0.0.255 → Broadcast address
Available IPs calculation:
| Subnet size | Total IPs | AWS reserved IPs | Available IPs |
|---|---|---|---|
/28 | 16 | 5 | 11 |
/27 | 32 | 5 | 27 |
/26 | 64 | 5 | 59 |
/25 | 128 | 5 | 123 |
/24 | 256 | 5 | 251 |
/23 | 512 | 5 | 507 |
/22 | 1,024 | 5 | 1,019 |
/20 | 4,096 | 5 | 4,091 |
/16 | 65,536 | 5 | 65,531 |
RFC 1918 Address Spaces
Private IP addresses defined by RFC 1918 are not routable on the public Internet.
Class A: 10.0.0.0 - 10.255.255.255 (CIDR: 10.0.0.0/8) 16,777,216 addresses
Class B: 172.16.0.0 - 172.31.255.255 (CIDR: 172.16.0.0/12) 1,048,576 addresses
Class C: 192.168.0.0 - 192.168.255.255 (CIDR: 192.168.0.0/16) 65,536 addresses
Recommendations for AWS:
- Use
10.0.0.0/8for enterprise VPCs (large address availability) - Avoid
172.16.0.0/12which can conflict with Docker’s default range (172.17.0.0/16) - Plan your CIDRs to avoid overlaps if you plan VPC Peering or Transit Gateway
Multi-VPC address plan for an enterprise:
Total enterprise space: 10.0.0.0/8
Production (10.0.0.0/13):
VPC Production US-East : 10.0.0.0/16 (65,536 IPs)
VPC Production EU-West : 10.1.0.0/16 (65,536 IPs)
VPC Production AP-SE : 10.2.0.0/16 (65,536 IPs)
Production Reserved : 10.3.0.0/14 (for expansion)
Staging (10.8.0.0/13):
VPC Staging US : 10.8.0.0/16 (65,536 IPs)
Development (10.16.0.0/13):
VPC Dev US : 10.16.0.0/16 (65,536 IPs)
Shared Services (10.24.0.0/13):
VPC Shared Services : 10.24.0.0/16 (65,536 IPs)
VPC Security/Audit : 10.25.0.0/16 (65,536 IPs)
AWS CLI Reference — Essential Network Commands
VPC Management
# Create a VPC
aws ec2 create-vpc \
--cidr-block 10.0.0.0/16 \
--tag-specifications 'ResourceType=vpc,Tags=[{Key=Name,Value=myfirstvpc}]'
# List VPCs
aws ec2 describe-vpcs \
--query 'Vpcs[*].{VpcId:VpcId,CIDR:CidrBlock,Name:Tags[?Key==`Name`].Value|[0]}'
# Enable DNS support
aws ec2 modify-vpc-attribute \
--vpc-id vpc-xxxxxxxx \
--enable-dns-support "{\"Value\":true}"
# Enable DNS hostnames
aws ec2 modify-vpc-attribute \
--vpc-id vpc-xxxxxxxx \
--enable-dns-hostnames "{\"Value\":true}"
Subnet Management
# Create a public subnet
aws ec2 create-subnet \
--vpc-id vpc-xxxxxxxx \
--cidr-block 10.0.1.0/24 \
--availability-zone us-east-1a \
--tag-specifications 'ResourceType=subnet,Tags=[{Key=Name,Value=public-subnet-a}]'
# Enable auto-assignment of public IP
aws ec2 modify-subnet-attribute \
--subnet-id subnet-xxxxxxxx \
--map-public-ip-on-launch
# List subnets of a VPC
aws ec2 describe-subnets \
--filters "Name=vpc-id,Values=vpc-xxxxxxxx" \
--query 'Subnets[*].{SubnetId:SubnetId,CIDR:CidrBlock,AZ:AvailabilityZone}'
Internet Gateway
# Create and attach an Internet Gateway
IGW_ID=$(aws ec2 create-internet-gateway \
--tag-specifications 'ResourceType=internet-gateway,Tags=[{Key=Name,Value=my-igw}]' \
--query 'InternetGateway.InternetGatewayId' --output text)
aws ec2 attach-internet-gateway \
--internet-gateway-id $IGW_ID \
--vpc-id vpc-xxxxxxxx
Route Tables
# Create a route table
aws ec2 create-route-table \
--vpc-id vpc-xxxxxxxx \
--tag-specifications 'ResourceType=route-table,Tags=[{Key=Name,Value=public-rt}]'
# Add a route to Internet
aws ec2 create-route \
--route-table-id rtb-xxxxxxxx \
--destination-cidr-block 0.0.0.0/0 \
--gateway-id igw-xxxxxxxx
# Associate a subnet with a route table
aws ec2 associate-route-table \
--route-table-id rtb-xxxxxxxx \
--subnet-id subnet-xxxxxxxx
Security Groups
# Create a Security Group
aws ec2 create-security-group \
--group-name web-sg \
--description "Security group for web servers" \
--vpc-id vpc-xxxxxxxx
# Add an INBOUND HTTPS rule
aws ec2 authorize-security-group-ingress \
--group-id sg-xxxxxxxx \
--protocol tcp \
--port 443 \
--cidr 0.0.0.0/0
# Add an INBOUND SSH rule from a specific IP
aws ec2 authorize-security-group-ingress \
--group-id sg-xxxxxxxx \
--protocol tcp \
--port 22 \
--cidr 203.0.113.0/32
# List rules of a Security Group
aws ec2 describe-security-groups \
--group-ids sg-xxxxxxxx \
--query 'SecurityGroups[*].{Name:GroupName,InboundRules:IpPermissions}'
Network ACLs
# Create a NACL
aws ec2 create-network-acl \
--vpc-id vpc-xxxxxxxx \
--tag-specifications 'ResourceType=network-acl,Tags=[{Key=Name,Value=my-nacl}]'
# Add an INBOUND ALLOW SSH rule
aws ec2 create-network-acl-entry \
--network-acl-id acl-xxxxxxxx \
--ingress \
--rule-number 100 \
--protocol 6 \
--port-range From=22,To=22 \
--cidr-block 0.0.0.0/0 \
--rule-action allow
# Add an OUTBOUND ALLOW ephemeral ports rule
aws ec2 create-network-acl-entry \
--network-acl-id acl-xxxxxxxx \
--egress \
--rule-number 100 \
--protocol 6 \
--port-range From=1024,To=65535 \
--cidr-block 0.0.0.0/0 \
--rule-action allow
NAT Gateway
# Allocate an Elastic IP
EIP_ID=$(aws ec2 allocate-address \
--domain vpc \
--query 'AllocationId' --output text)
# Create a NAT Gateway in a public subnet
NATGW_ID=$(aws ec2 create-nat-gateway \
--subnet-id subnet-xxxxxxxx \
--allocation-id $EIP_ID \
--tag-specifications 'ResourceType=natgateway,Tags=[{Key=Name,Value=nat-gw-a}]' \
--query 'NatGateway.NatGatewayId' --output text)
# Wait for the NAT Gateway to be available
aws ec2 wait nat-gateway-available --nat-gateway-ids $NATGW_ID
# Add the route in the private route table
aws ec2 create-route \
--route-table-id rtb-private-xxxxxxxx \
--destination-cidr-block 0.0.0.0/0 \
--nat-gateway-id $NATGW_ID
Infrastructure as Code — CloudFormation and Terraform
CloudFormation — VPC with Public and Private Subnets
AWSTemplateFormatVersion: '2010-09-09'
Description: VPC with public and private subnets in multiple AZs
Parameters:
VpcCidr:
Type: String
Default: 10.0.0.0/16
EnvironmentName:
Type: String
Default: Production
Resources:
# VPC
VPC:
Type: AWS::EC2::VPC
Properties:
CidrBlock: !Ref VpcCidr
EnableDnsHostnames: true
EnableDnsSupport: true
Tags:
- Key: Name
Value: !Sub ${EnvironmentName}-VPC
# Internet Gateway
InternetGateway:
Type: AWS::EC2::InternetGateway
Properties:
Tags:
- Key: Name
Value: !Sub ${EnvironmentName}-IGW
IGWAttachment:
Type: AWS::EC2::VPCGatewayAttachment
Properties:
VpcId: !Ref VPC
InternetGatewayId: !Ref InternetGateway
# Public Subnets
PublicSubnetA:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref VPC
CidrBlock: 10.0.1.0/24
AvailabilityZone: !Select [0, !GetAZs '']
MapPublicIpOnLaunch: true
Tags:
- Key: Name
Value: !Sub ${EnvironmentName}-Public-A
PublicSubnetB:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref VPC
CidrBlock: 10.0.2.0/24
AvailabilityZone: !Select [1, !GetAZs '']
MapPublicIpOnLaunch: true
Tags:
- Key: Name
Value: !Sub ${EnvironmentName}-Public-B
# Private Subnets
PrivateSubnetA:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref VPC
CidrBlock: 10.0.10.0/24
AvailabilityZone: !Select [0, !GetAZs '']
Tags:
- Key: Name
Value: !Sub ${EnvironmentName}-Private-A
PrivateSubnetB:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref VPC
CidrBlock: 10.0.11.0/24
AvailabilityZone: !Select [1, !GetAZs '']
Tags:
- Key: Name
Value: !Sub ${EnvironmentName}-Private-B
# Elastic IPs for NAT Gateways
EIP_A:
Type: AWS::EC2::EIP
DependsOn: IGWAttachment
Properties:
Domain: vpc
EIP_B:
Type: AWS::EC2::EIP
DependsOn: IGWAttachment
Properties:
Domain: vpc
# NAT Gateways (one per AZ for HA)
NatGatewayA:
Type: AWS::EC2::NatGateway
Properties:
AllocationId: !GetAtt EIP_A.AllocationId
SubnetId: !Ref PublicSubnetA
Tags:
- Key: Name
Value: !Sub ${EnvironmentName}-NATGW-A
NatGatewayB:
Type: AWS::EC2::NatGateway
Properties:
AllocationId: !GetAtt EIP_B.AllocationId
SubnetId: !Ref PublicSubnetB
Tags:
- Key: Name
Value: !Sub ${EnvironmentName}-NATGW-B
# Public Route Table
PublicRouteTable:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref VPC
Tags:
- Key: Name
Value: !Sub ${EnvironmentName}-Public-RT
PublicRoute:
Type: AWS::EC2::Route
DependsOn: IGWAttachment
Properties:
RouteTableId: !Ref PublicRouteTable
DestinationCidrBlock: 0.0.0.0/0
GatewayId: !Ref InternetGateway
PublicSubnetARouteAssoc:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref PublicSubnetA
RouteTableId: !Ref PublicRouteTable
PublicSubnetBRouteAssoc:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref PublicSubnetB
RouteTableId: !Ref PublicRouteTable
# Private Route Tables
PrivateRouteTableA:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref VPC
Tags:
- Key: Name
Value: !Sub ${EnvironmentName}-Private-RT-A
PrivateRouteA:
Type: AWS::EC2::Route
Properties:
RouteTableId: !Ref PrivateRouteTableA
DestinationCidrBlock: 0.0.0.0/0
NatGatewayId: !Ref NatGatewayA
PrivateSubnetARouteAssoc:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref PrivateSubnetA
RouteTableId: !Ref PrivateRouteTableA
PrivateRouteTableB:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref VPC
Tags:
- Key: Name
Value: !Sub ${EnvironmentName}-Private-RT-B
PrivateRouteB:
Type: AWS::EC2::Route
Properties:
RouteTableId: !Ref PrivateRouteTableB
DestinationCidrBlock: 0.0.0.0/0
NatGatewayId: !Ref NatGatewayB
PrivateSubnetBRouteAssoc:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref PrivateSubnetB
RouteTableId: !Ref PrivateRouteTableB
# VPC Endpoint for S3 (free, avoids NAT Gateway)
S3VpcEndpoint:
Type: AWS::EC2::VPCEndpoint
Properties:
VpcId: !Ref VPC
ServiceName: !Sub com.amazonaws.${AWS::Region}.s3
VpcEndpointType: Gateway
RouteTableIds:
- !Ref PrivateRouteTableA
- !Ref PrivateRouteTableB
Outputs:
VpcId:
Value: !Ref VPC
Export:
Name: !Sub ${EnvironmentName}-VpcId
PublicSubnetIds:
Value: !Join [',', [!Ref PublicSubnetA, !Ref PublicSubnetB]]
Export:
Name: !Sub ${EnvironmentName}-PublicSubnetIds
PrivateSubnetIds:
Value: !Join [',', [!Ref PrivateSubnetA, !Ref PrivateSubnetB]]
Export:
Name: !Sub ${EnvironmentName}-PrivateSubnetIds
Terraform — VPC with Modules
# variables.tf
variable "vpc_cidr" {
description = "CIDR block for the VPC"
type = string
default = "10.0.0.0/16"
}
variable "environment" {
description = "Environment name"
type = string
default = "production"
}
variable "availability_zones" {
description = "List of AZs to use"
type = list(string)
default = ["us-east-1a", "us-east-1b"]
}
# main.tf
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
}
provider "aws" {
region = "us-east-1"
}
resource "aws_vpc" "main" {
cidr_block = var.vpc_cidr
enable_dns_hostnames = true
enable_dns_support = true
tags = {
Name = "${var.environment}-vpc"
Environment = var.environment
}
}
resource "aws_internet_gateway" "main" {
vpc_id = aws_vpc.main.id
tags = { Name = "${var.environment}-igw" }
}
resource "aws_subnet" "public" {
count = length(var.availability_zones)
vpc_id = aws_vpc.main.id
cidr_block = cidrsubnet(var.vpc_cidr, 8, count.index + 1)
availability_zone = var.availability_zones[count.index]
map_public_ip_on_launch = true
tags = {
Name = "${var.environment}-public-${var.availability_zones[count.index]}"
Type = "public"
}
}
resource "aws_subnet" "private" {
count = length(var.availability_zones)
vpc_id = aws_vpc.main.id
cidr_block = cidrsubnet(var.vpc_cidr, 8, count.index + 10)
availability_zone = var.availability_zones[count.index]
tags = {
Name = "${var.environment}-private-${var.availability_zones[count.index]}"
Type = "private"
}
}
resource "aws_eip" "nat" {
count = length(var.availability_zones)
domain = "vpc"
tags = { Name = "${var.environment}-eip-natgw-${count.index + 1}" }
}
resource "aws_nat_gateway" "main" {
count = length(var.availability_zones)
allocation_id = aws_eip.nat[count.index].id
subnet_id = aws_subnet.public[count.index].id
depends_on = [aws_internet_gateway.main]
tags = { Name = "${var.environment}-natgw-${var.availability_zones[count.index]}" }
}
resource "aws_route_table" "public" {
vpc_id = aws_vpc.main.id
route {
cidr_block = "0.0.0.0/0"
gateway_id = aws_internet_gateway.main.id
}
tags = { Name = "${var.environment}-public-rt" }
}
resource "aws_route_table_association" "public" {
count = length(var.availability_zones)
subnet_id = aws_subnet.public[count.index].id
route_table_id = aws_route_table.public.id
}
resource "aws_route_table" "private" {
count = length(var.availability_zones)
vpc_id = aws_vpc.main.id
route {
cidr_block = "0.0.0.0/0"
nat_gateway_id = aws_nat_gateway.main[count.index].id
}
tags = { Name = "${var.environment}-private-rt-${var.availability_zones[count.index]}" }
}
resource "aws_route_table_association" "private" {
count = length(var.availability_zones)
subnet_id = aws_subnet.private[count.index].id
route_table_id = aws_route_table.private[count.index].id
}
# S3 Gateway Endpoint (free)
resource "aws_vpc_endpoint" "s3" {
vpc_id = aws_vpc.main.id
service_name = "com.amazonaws.us-east-1.s3"
vpc_endpoint_type = "Gateway"
route_table_ids = aws_route_table.private[*].id
tags = { Name = "${var.environment}-s3-endpoint" }
}
# Layered Security Groups
resource "aws_security_group" "web" {
name = "${var.environment}-web-sg"
description = "Security group for web servers"
vpc_id = aws_vpc.main.id
ingress {
from_port = 443
to_port = 443
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
description = "HTTPS from Internet"
}
ingress {
from_port = 80
to_port = 80
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
description = "HTTP from Internet"
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
tags = { Name = "${var.environment}-web-sg" }
}
resource "aws_security_group" "app" {
name = "${var.environment}-app-sg"
description = "Security group for application servers"
vpc_id = aws_vpc.main.id
ingress {
from_port = 8080
to_port = 8080
protocol = "tcp"
security_groups = [aws_security_group.web.id]
description = "HTTP from web-sg only"
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
tags = { Name = "${var.environment}-app-sg" }
}
resource "aws_security_group" "db" {
name = "${var.environment}-db-sg"
description = "Security group for databases"
vpc_id = aws_vpc.main.id
ingress {
from_port = 3306
to_port = 3306
protocol = "tcp"
security_groups = [aws_security_group.app.id]
description = "MySQL from app-sg only"
}
tags = { Name = "${var.environment}-db-sg" }
}
# outputs.tf
output "vpc_id" {
value = aws_vpc.main.id
description = "VPC ID"
}
output "public_subnet_ids" {
value = aws_subnet.public[*].id
description = "Public subnet IDs"
}
output "private_subnet_ids" {
value = aws_subnet.private[*].id
description = "Private subnet IDs"
}
output "nat_gateway_ips" {
value = aws_eip.nat[*].public_ip
description = "Public IPs of NAT Gateways"
}
Search Terms
aws · networking · fundamentals · core · services · amazon · web · vpc · gateway · route · architecture · private · security · subnet · connectivity · deep · dive · routing · load · network · creation · data · endpoints · group