Intermediate

AWS Networking Fundamentals

VPCs, subnets, access control, CIDR planning, endpoints, Route 53 and Elastic Load Balancing.

Table of Contents

  1. Module 1 — Introduction to the Virtual Private Cloud (VPC)
  2. Module 2 — Creating Your First VPC
  3. Module 3 — Access Control in the VPC
  4. Module 4 — Data Transfer Costs
  5. Deep Dive — CIDR Planning and Subnet Design
  6. Deep Dive — VPC Endpoints and PrivateLink
  7. Deep Dive — Advanced VPC-to-VPC Connectivity
  8. Deep Dive — Route 53 and Routing Policies
  9. Deep Dive — Elastic Load Balancing
  10. Deep Dive — Complete Multi-AZ Architecture
  11. Quick Reference — NACL vs Security Group Comparison
  12. Reserved IP Addresses in a Subnet
  13. RFC 1918 Address Spaces
  14. AWS CLI Reference — Essential Network Commands
  15. 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
ComponentDescription
Data CenterPhysical 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.
RegionComposed 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.
VPCDedicated 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:

ResourceDefault limitAdjustable
VPCs per region5Yes
Subnets per VPC200Yes
Internet Gateways per VPC1No
Route Tables per VPC200Yes
Security Groups per VPC2,500Yes
NACLs per VPC200Yes

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:

  1. An Internet Gateway (IGW) must be created and attached to the VPC
  2. A route table must contain a route sending Internet traffic to the Internet Gateway
  3. 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:

TypeDescriptionPersistence
Public IPAutomatically assigned when creating an instance in a public subnet.Temporary — changes if the instance is stopped/restarted
Elastic IPStatic 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)

OptionDescriptionBandwidthLatency
Site-to-Site VPNEncrypted IPsec tunnel over the Internet between your on-premises network and VPC.~1.25 Gbps maxVariable (depends on Internet)
AWS Direct ConnectDedicated physical network connection between your infrastructure and AWS via a partner.1, 10, 100 GbpsPredictable 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:

AttributeDescriptionDefault value
enableDnsHostnamesProvides DNS names to instances that have a public IPfalse (except default VPC)
enableDnsSupportEnables DNS resolution via Route 53 Resolver in the VPCtrue

Behavior based on configuration:

enableDnsSupportenableDnsHostnamesResult
truetrueInstances with public IP receive a public and private DNS hostname
truefalseDNS resolution active but no hostname for instances
falseN/ANo 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 = true and enable enableDnsHostnames if 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 RangeCIDRNumber of addresses
10.0.0.010.255.255.25510.0.0.0/816,777,216
172.16.0.0172.31.255.255172.16.0.0/121,048,576
192.168.0.0192.168.255.255192.168.0.0/1665,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 AddressReserved use
10.0.0.0Network address
10.0.0.1VPC router
10.0.0.2Route 53 DNS Resolver
10.0.0.3Reserved for future AWS use
10.0.0.255Broadcast address

Impact on sizing: A /24 subnet 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:

FieldValueDescription
Creation modeVPC onlyManual step-by-step creation (vs “VPC and more” wizard)
Name tagmyfirstvpcVPC name
IPv4 CIDR10.0.0.0/16VPC network range
IPv6DisabledNot used in this course
TenancyDefaultShared infrastructure (vs Dedicated for dedicated hardware)

Step 2 — Subnet Creation

First subnet (public):

FieldValue
VPCmyfirstvpc
Namemyfirstsubnet
Availability Zoneus-east-1a
IPv4 CIDR10.0.1.0/24

Second subnet:

FieldValue
VPCmyfirstvpc
Namemysecondsubnet
Availability Zoneus-east-1b
IPv4 CIDR10.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

DestinationTargetDescription
10.0.0.0/16localLocal traffic within the VPC (automatic route, non-modifiable)
0.0.0.0/0Internet Gateway myfirstvpcigwAll 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: 1024 to 65535
  • Temporary ports automatically chosen by the client OS
  • Must be explicitly allowed in NACL OUTBOUND rules

NACL Rule Structure

RuleTypeProtocolPortSource/DestAction
100SSHTCP220.0.0.0/0ALLOW
200Custom TCPTCP1024-655350.0.0.0/0ALLOW
*All TrafficAllAll0.0.0.0/0DENY

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 NACLCustom NACL
INBOUND trafficALLOW all (rule 100)DENY all (implicit default)
OUTBOUND trafficALLOW all (rule 100)DENY all (implicit default)
AssignmentAll subnets without explicit NACLExplicitly 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

ScenarioINBOUND trafficOUTBOUND traffic
Default VPC Security GroupALLOW allALLOW all
Newly created Security GroupDENY all (implicit)ALLOW all

Security Group Rules

AspectDetail
Rule typesALLOW only (no DENY possible)
EvaluationAll rules evaluated together (no numbered order)
Instances per SGMultiple instances can share the same SG
SGs per instanceMultiple SGs can be assigned (aggregated rules)
Reference to another SGPossible — SG as source/destination instead of an IP

Common INBOUND rule examples:

TypeProtocolPortSourceUse
SSHTCP2210.0.0.0/8SSH access from internal network
HTTPTCP800.0.0.0/0Public web access
HTTPSTCP4430.0.0.0/0Public secure web access
MySQL/AuroraTCP3306sg-app-xxxxxDB 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 GroupINBOUND RuleDescription
alb-sgTCP 443 from 0.0.0.0/0Internet accesses the load balancer
app-sgTCP 8080 from alb-sgOnly the LB accesses the apps
db-sgTCP 3306 from app-sgOnly 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/0 to 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:

RuleTypeProtocolPortSourceAction
100SSHTCP220.0.0.0/0ALLOW
*All trafficAllAll0.0.0.0/0DENY

OUTBOUND rules:

RuleTypeProtocolPortDestinationAction
100Custom TCPTCP1024-655350.0.0.0/0ALLOW
*All trafficAllAll0.0.0.0/0DENY

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

ScenarioCostNotes
Inbound traffic to AWS from InternetFree
Outbound traffic to Internet~$0.09/GBVaries by region
Traffic between instances in the same AZFreeIf private IPs used
Traffic between different AZs (same Region)~$0.01/GBCharged in both directions
Traffic between different Regions~$0.02/GBVaries by region
NAT Gateway — processing fees~$0.045/GB+ hourly fees $0.045/h
VPC Endpoint to S3/DynamoDBFreeAlso saves NAT fees
Direct Connect — data transfer out~$0.02/GBCheaper than Internet at volume

Network Cost Optimization

Recommendations:

  1. Use private IPs for communication between instances in the same AZ
  2. Deploy VPC Endpoints to access S3, DynamoDB without going through NAT
  3. Use Direct Connect for large volumes of traffic from on-premises
  4. 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:

  1. Never overlap CIDR ranges between VPCs if you plan Peering or Transit Gateway
  2. Plan for growth: choose a larger CIDR than necessary
  3. Segment by environment: prod, staging, dev in separate ranges
  4. Avoid 172.17.0.0/16 (Docker Bridge Network default range)
  5. Avoid 172.16.0.0/12 if 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 TypeInternet AccessRoute to IGWRoute to NAT GWTypical use
PublicInbound and outboundYes (0.0.0.0/0 → IGW)NoLoad Balancers, Bastion hosts, NAT Gateway
PrivateOutbound only via NATNoYes (0.0.0.0/0 → NATGW)Application servers, APIs
IsolatedNoneNoNoDatabases, 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:

AspectDetail
TypeManaged by AWS (fully managed)
High availabilityRedundant in an AZ — deploy one per AZ for HA
BandwidthUp to 100 Gbps (auto-scaling)
Elastic IPRequires 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.


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)

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:

AspectGateway EndpointInterface Endpoint (PrivateLink)
Type of resource createdRoute in Route TableENI with private IP in subnet
Supported servicesS3, DynamoDB only100+ AWS and third-party services
CostFreeHourly fees + per-GB fees
DNSAutomatic resolutionPrivate DNS (must be enabled)
Accessibility from on-premisesNoYes (via Direct Connect or VPN)
Accessibility from another VPCNot by defaultYes (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

CriterionVPC PeeringTransit Gateway
Connectivity modelPoint-to-point (mesh)Hub-and-spoke
TransitivityNot transitiveTransitive (controlled by route tables)
Management complexityComplex for 10+ VPCsSimple and centralized
BandwidthNot limited by peeringUp to 50 Gbps per AZ
CostInter-AZ data feesData fees + attachment fees ($0.05/h)
Inter-RegionYes (Inter-Region Peering)Yes (TGW inter-region peering)
Inter-AccountYesYes (RAM sharing)
When to use2-5 VPCs, low complexity5+ 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
PolicyDescriptionUse case
SimpleSingle DNS record, one or more IPsSimple application, no health check
WeightedDistributes traffic by weight (e.g.: 70% / 30%)Blue/green deployments, A/B testing
FailoverRoutes to primary, switches to secondary if health check failsActive/passive high availability
Latency-BasedRoutes to AWS Region with lowest latencyGlobal performance optimization
GeolocationRoutes based on user’s geographic locationGDPR compliance, localized content
GeoproximityRoutes based on location with adjustable biasFine-grained geographic routing control
Multivalue AnswerReturns up to 8 healthy IPs at randomSimple load balancing with health check
IP-BasedRoutes based on client’s source IP CIDROptimization 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

CriterionALBNLBGWLBCLB
OSI Layer7 (Application)4 (Transport)3 (Network)4 and 7
ProtocolsHTTP, HTTPS, WebSocket, gRPCTCP, UDP, TLSIPHTTP, HTTPS, TCP, SSL
LatencyMillisecondsUltra-low microsecondsDepends on applianceMilliseconds
Static IPsNo (DNS only)Yes (per AZ)YesNo
Elastic IPNoYesNoNo
Advanced routingYes (path, host, header, query)NoNoNo
SSL TerminationYesYes (TLS passthrough possible)NoYes
Target typesInstance, IP, LambdaInstance, IPInstance, IPInstance
Preserve client IPVia X-Forwarded-ForYes (natively)YesVia X-Forwarded-For
Sticky sessionsYes (cookies)YesNoYes
WebSocketsYesYesNoNo

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:

ConditionExampleAction
Path-based/api/*Forward to API Target Group
Host-basedadmin.example.comForward to Admin Target Group
Header-basedX-Version: v2Forward to v2 Target Group
Query string?env=prodForward to Prod Target Group
RedirectHTTP to HTTPSRedirect 301
Fixed response/healthReturn 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

CharacteristicNACLSecurity Group
Application levelSubnetInstance (ENI)
Stateful / StatelessStatelessStateful
Rule typesALLOW and DENYALLOW only
Evaluation orderNumbered, ascending orderAll rules evaluated together
Final ruleImplicit DENY (*)Implicit DENY
Default NACL/SG in VPCALLOW all (IN and OUT)ALLOW all (IN and OUT)
Newly created NACL/SGDENY all by defaultDENY IN, ALLOW OUT
Ephemeral portsMust be configured explicitlyHandled automatically (stateful)
ApplicationAll instances in the subnetSpecifically assigned instances
Reference to another SGNoYes
Explicit deny examplePossible (DENY specific IP)Not supported
Recommended useCoarse filtering, network layerFine-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 sizeTotal IPsAWS reserved IPsAvailable IPs
/2816511
/2732527
/2664559
/251285123
/242565251
/235125507
/221,02451,019
/204,09654,091
/1665,536565,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/8 for enterprise VPCs (large address availability)
  • Avoid 172.16.0.0/12 which 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

Interested in this course?

Contact us to book it or get a custom training plan for your team.