ЁЯПл The SchoolтА║ЁЯПл VPCтА║ЁЯУТ рдзрдбрд╛ 08 тАФ VPC рдордзрд▓реЗ DNS: campus рдЪреА phone book
ЁЯЦ╝я╕П See the drawing + lab ЁЯПа Course home ЁЯМ┐ Branch on GitHub тЬПя╕П View source
ЁЯЦ╝я╕П рдЖрдХреГрддреА рдЖрдгрд┐ labThe drawing + lab рдкреВрд░реНрдг рдкрд╛рдирд╛рд╡рд░ рдЙрдШрдбрд╛ тЖЧOpen full page тЖЧ

ЁЯУТ рдзрдбрд╛ 08 тАФ VPC рдордзрд▓реЗ DNS: campus рдЪреА phone book

ЁЯУН рддреБрдореНрд╣реА рдЗрдереЗ рдЖрд╣рд╛рдд: 12 рдкреИрдХреА рдзрдбрд╛ 08 ┬╖ рдорд╛рдЧрдЪрд╛: lesson-07-endpoints ┬╖ рдкреБрдврдЪрд╛: lesson-09-peering


ЁЯУж рдпрд╛ рдмреНрд░рдБрдЪрдордзреНрдпреЗ рдХрд╛рдп рдЖрд╣реЗ

рдзрдбреЗ 01тАУ07, рдЖрдгрд┐ рдирд╛рд╡реЗ: base + 2 рд╡рд░рдЪрд╛ VPC рдЪрд╛ рдЕрдВрдЧрднреВрдд resolver, VPC рдЪреЗ рджреЛрди DNS switches, рдЖрдгрд┐ рдПрдХ private hosted zone school.internal рдЬреА рдлрдХреНрдд рддрд┐рдЪреНрдпрд╛рд╢реА associate рдХреЗрд▓реЗрд▓реНрдпрд╛ VPCs рдордзреНрдпреЗрдЪ рдЙрддреНрддрд░ рджреЗрддреЗ. vpc/demo.py рдордзрд▓реЗ dns().

ЁЯзТ 5 рд╡рд░реНрд╖рд╛рдВрдЪреНрдпрд╛ рдореБрд▓рд╛рд▓рд╛ рд╕рдордЬрд╛рд╡рд▓реНрдпрд╛рд╕рд╛рд░рдЦреЗ

Campus рд╡рд░ рдХреБрдгрд╛рд▓рд╛рдЪ рдЦреЛрд▓реНрдпрд╛рдВрдЪреЗ рдирдВрдмрд░ рдЖрдард╡рдд рдирд╛рд╣реАрдд. рд▓реЛрдХ "рдкрд░реАрдХреНрд╖рд╛ рдХрд╛рд░реНрдпрд╛рд▓рдп" рдореНрд╣рдгрддрд╛рдд, 10.20.96.15 рдирд╛рд╣реА. рдореНрд╣рдгреВрди рдкреНрд░рддреНрдпреЗрдХ campus рд╡рд░ рдард░рд▓реЗрд▓реНрдпрд╛ рдЬрд╛рдЧреА рдПрдХ phone book ЁЯУТ рдЕрд╕рддреЗ: рдиреЗрд╣рдореА campus рдЪрд╛ рддрд┐рд╕рд░рд╛ рдирдВрдмрд░ (10.20.0.2). рддрд┐рд▓рд╛ рдирд╛рд╡ рд╡рд┐рдЪрд╛рд░рд╛, рддреА рддреБрдореНрд╣рд╛рд▓рд╛ рдирдВрдмрд░ рджреЗрддреЗ.

рд╢рд╛рд│рд╛ phone book рдордзреНрдпреЗ рд╕реНрд╡рддрдГрдЪреА рдЦрд╛рдЬрдЧреА рдкрд╛рдиреЗ рд╕реБрджреНрдзрд╛ рд▓рд┐рд╣рд┐рддреЗ: db.school.internal, app.school.internal. рд╣реА рдкрд╛рдиреЗ рдлрдХреНрдд рдпрд╛рдЪ campus рдЪреНрдпрд╛ рдкреБрд╕реНрддрдХрд╛рдд рдЕрд╕рддрд╛рдд. рднрд╛рдЧреАрджрд╛рд░ рд╢рд╛рд│реЗрдЪреЗ рд╕реНрд╡рддрдГрдЪреЗ рдкреБрд╕реНрддрдХ рдЖрд╣реЗ, рдЖрдгрд┐ рддреНрдпрд╛рдд рд╣реА рдкрд╛рдиреЗ рдирд╛рд╣реАрдд тАФ рдЬреЛрдкрд░реНрдпрдВрдд рд╢рд╛рд│рд╛ рдЬрд╛рдгреВрдирдмреБрдЬреВрди рддреНрдпрд╛рдВрдирд╛ рдПрдХ рдкреНрд░рдд рджреЗрдд рдирд╛рд╣реА (рдПрдХ association).

ЁЯЧ║я╕П рдЖрдХреГрддреА

flowchart LR
    app["ЁЯЦея╕П app 10.20.48.25"]
    res["ЁЯУТ resolver 10.20.0.2<br/>and 169.254.169.253"]
    phz["ЁЯФТ private zone school.internal<br/>associated: school VPC"]
    pub["ЁЯМН public DNS"]
    partner["ЁЯдЭ partner VPC"]
    app -->|"db.school.internal?"| res --> phz -->|"10.20.96.15"| app
    res -->|"other names"| pub
    partner -.->|"db.school.internal?"| x["no answer<br/>zone not associated"]

ЁЯЧ║я╕П рд░реЗрдЦрд╛рдЯрд▓реЗрд▓реА рдЖрд╡реГрддреНрддреА + рдПрдХ lab: https://school-edh.pages.dev/vpc/lesson-diagrams.html#l08

тЭУ рдХрд╛рдп

ЁЯдФ рдХрд╛

рдХрд╛рд░рдг рдкрддреНрддреЗ рдмрджрд▓рддрд╛рдд рдЖрдгрд┐ рдирд╛рд╡реЗ рдмрджрд▓реВ рдирдпреЗрдд. Fail over рд╣реЛрдгрд╛рд▒реНрдпрд╛ database рд▓рд╛ рдирд╡рд╛ IP рдорд┐рд│рддреЛ; db.school.internal рддрд░реАрд╣реА рдЪрд╛рд▓рддреЗ. рдЖрдгрд┐ рдХрд╛рд░рдг рдЦрд╛рдЬрдЧреА рдирд╛рд╡реЗ рдмрд╛рд╣реЗрд░ рдлреБрдЯреВ рдирдпреЗрдд: private hosted zone рддреБрдордЪрд╛ рдЕрдВрддрд░реНрдЧрдд рдирдХрд╛рд╢рд╛ рддреБрдореНрд╣реА рдирд┐рд╡рдбрд▓реЗрд▓реНрдпрд╛ VPCs рдЪреНрдпрд╛ рдЖрддрдЪ рдареЗрд╡рддреЗ.

ЁЯФз рдХрд╕реЗ (рдпрд╛ repo рдордзреНрдпреЗ)

vpc/demo.py рдордзрд▓реЗ dns() resolver ipaddress.ip_network(VPC)[2] рдЕрд╕рд╛ рдХрд╛рдврддреЗ рдЖрдгрд┐ рдПрдХрд╛ рдЫреЛрдЯреНрдпрд╛ private zone рдордзреНрдпреЗ рдирд╛рд╡реЗ рд╢реЛрдзрддреЗ. рд╢реЗрд╡рдЯрдЪреА рдУрд│ partner VPC рдордзреВрди рд╡рд┐рдЪрд╛рд░рддреЗ, рдЬреНрдпрд╛рдЪреНрдпрд╛рд╢реА zone associate рдХреЗрд▓реЗрд▓реА рдирд╛рд╣реА. рдЦрд╛рд▓рдЪрд╛ snippet рддреАрдЪ zone рд╕реНрдкрд╖реНрдЯ association list рд╕рд╣ рдмрд╛рдВрдзрддреЛ, рдореНрд╣рдгрдЬреЗ рддреБрдореНрд╣реА partner рд▓рд╛ рдЬреЛрдбреВ рд╢рдХрддрд╛.

ЁЯзк рдХрд░реВрди рдкрд╛рд╣рд╛

python3 vpc/demo.py dns
python3 - <<'EOF'
import ipaddress
for vpc in ("10.20.0.0/16", "10.30.0.0/16", "172.31.0.0/16", "192.168.4.0/22"):
    print(vpc.ljust(15), "resolver", ipaddress.ip_network(vpc)[2])
EOF
python3 - <<'EOF'
zone = {"name": "school.internal", "vpcs": {"school"}, "records": {"db.school.internal": "10.20.96.15"}}
def resolve(name, from_vpc):
    if from_vpc not in zone["vpcs"]: return "NXDOMAIN тАФ zone not associated with " + from_vpc
    return zone["records"].get(name, "NXDOMAIN тАФ no such record")
print(resolve("db.school.internal", "school"))
print(resolve("db.school.internal", "partner"))
zone["vpcs"].add("partner")                    # associate the zone with the partner VPC too
print(resolve("db.school.internal", "partner"))
EOF

тЬЕ рддрдкрд╛рд╕рд╛ тАФ рддреБрдореНрд╣рд╛рд▓рд╛ рдХрд╛рдп рджрд┐рд╕рд╛рдпрд▓рд╛ рд╣рд╡реЗ

dns рд╣реЗ рдЫрд╛рдкрддреЗ:

тФАтФА every VPC has a resolver at base + 2 = 10.20.0.2 (and 169.254.169.253); enableDnsSupport + enableDnsHostnames on
   db.school.internal                         тЖТ 10.20.96.15
   app.school.internal                        тЖТ 10.20.48.25
   db.school.internal (from the partner VPC)  тЖТ no answer тАФ private zone not associated with that VPC

рдкрд╣рд┐рд▓рд╛ snippet resolver 10.20.0.2, 10.30.0.2, 172.31.0.2 рдЖрдгрд┐ 192.168.4.2 рдЫрд╛рдкрддреЛ. рджреБрд╕рд░рд╛ рдЖрдзреА 10.20.96.15, рдордЧ NXDOMAIN тАФ zone not associated with partner, рдЖрдгрд┐ partner associate рдЭрд╛рд▓реНрдпрд╛рд╡рд░ рдкреБрдиреНрд╣рд╛ 10.20.96.15 рдЫрд╛рдкрддреЛ.

ЁЯПБ рддреБрдореНрд╣реА рдЖрддреНрддрд╛рдЪ рдХрд╛рдп рд╕рд┐рджреНрдз рдХреЗрд▓реЗ

рдХреЛрдгрддреНрдпрд╛рд╣реА VPC рдЪреА phone book рддреБрдореНрд╣реА рддреНрдпрд╛рдЪреНрдпрд╛ CIDR рд╡рд░реВрди рд╢реЛрдзреВ рд╢рдХрддрд╛, рдЖрдгрд┐ рддреБрдореНрд╣рд╛рд▓рд╛ рдорд╛рд╣реАрдд рдЖрд╣реЗ рдХреА рдЦрд╛рдЬрдЧреА рдирд╛рд╡ рдлрдХреНрдд рддрд┐рдереЗрдЪ рдЙрддреНрддрд░ рджреЗрддреЗ рдЬрд┐рдереЗ рддреНрдпрд╛рдЪреА zone associate рдХреЗрд▓реЗрд▓реА рдЕрд╕рддреЗ.

тЪая╕П рдиреЗрд╣рдореАрдЪреНрдпрд╛ рдЪреБрдХрд╛

ЁЯПн рдкреНрд░рддреНрдпрдХреНрд╖ рд╡рд╛рдкрд░рд╛рдд

Teams рдкреНрд░рддреНрдпреЗрдХ environment рд╕рд╛рдареА рдПрдХ private zone рд╡рд╛рдкрд░рддрд╛рдд (prod.school.internal), рддреА рдЧрд░рдЬ рдЕрд╕рд▓реЗрд▓реНрдпрд╛ рдкреНрд░рддреНрдпреЗрдХ VPC рд╢реА associate рдХрд░рддрд╛рдд, рдЖрдгрд┐ office рд╕реЛрдмрдд share рд╣реЛрдгрд╛рд▒реНрдпрд╛ рдирд╛рд╡рд╛рдВрд╕рд╛рдареА Resolver endpoints рд╡рд╛рдкрд░рддрд╛рдд.

рдЦрд▒реНрдпрд╛ account рд╡рд░ (AWS CLI рдЖрдгрд┐ credentials рд▓рд╛рдЧрддрд╛рдд; IDs рдЙрджрд╛рд╣рд░рдгрд╛рджрд╛рдЦрд▓ рдЖрд╣реЗрдд):

aws ec2 modify-vpc-attribute --vpc-id vpc-0123456789abcdef0 --enable-dns-support '{"Value":true}'
aws ec2 modify-vpc-attribute --vpc-id vpc-0123456789abcdef0 --enable-dns-hostnames '{"Value":true}'
aws route53 create-hosted-zone --name school.internal --caller-reference school-2026-09-27 \
    --vpc VPCRegion=ap-south-1,VPCId=vpc-0123456789abcdef0
aws route53 associate-vpc-with-hosted-zone --hosted-zone-id Z0123456789EXAMPLE \
    --vpc VPCRegion=ap-south-1,VPCId=vpc-0partner0000000a

тПня╕П рдкреБрдвреЗ

рднрд╛рдЧреАрджрд╛рд░ рд╢рд╛рд│реЗрд▓рд╛ рдЖрдкрд▓реНрдпрд╛ phone book рдкреЗрдХреНрд╖рд╛ рдЬрд╛рд╕реНрдд рдХрд╛рд╣реАрддрд░реА рд╣рд╡реЗ тАФ рддрд┐рд▓рд╛ рдПрдХ corridor рд╣рд╡рд╛. VPC peering рджреЛрди campuses рдЬреЛрдбрддреЗ.

git checkout lesson-09-peering

ЁЯУТ Lesson 08 тАФ DNS inside the VPC: the campus phone book

ЁЯУН You are here: Lesson 08 of 12 ┬╖ Previous: lesson-07-endpoints ┬╖ Next: lesson-09-peering


ЁЯУж What's in this branch

Lessons 01тАУ07, plus names: the VPC's built-in resolver at base + 2, the two VPC DNS switches, and a private hosted zone school.internal that answers only inside the VPCs it is associated with. dns() in vpc/demo.py.

ЁЯзТ Explain like I'm 5

Nobody on campus remembers room numbers. They say "the exam office", not 10.20.96.15. So every campus has a phone book ЁЯУТ at a fixed place: always the third number of the campus (10.20.0.2). Ask it a name, it gives you the number.

The school also writes its own private pages in the phone book: db.school.internal, app.school.internal. These pages are only in this campus's book. The partner school has its own book, and those pages are not in it тАФ unless the school hands them a copy on purpose (an association).

ЁЯЧ║я╕П Diagram

flowchart LR
    app["ЁЯЦея╕П app 10.20.48.25"]
    res["ЁЯУТ resolver 10.20.0.2<br/>and 169.254.169.253"]
    phz["ЁЯФТ private zone school.internal<br/>associated: school VPC"]
    pub["ЁЯМН public DNS"]
    partner["ЁЯдЭ partner VPC"]
    app -->|"db.school.internal?"| res --> phz -->|"10.20.96.15"| app
    res -->|"other names"| pub
    partner -.->|"db.school.internal?"| x["no answer<br/>zone not associated"]

ЁЯЧ║я╕П Drawn version + a lab: https://school-edh.pages.dev/vpc/lesson-diagrams.html#l08

тЭУ What

ЁЯдФ Why

Because addresses change and names should not. A database that fails over gets a new IP; db.school.internal still works. And because private names must not leak: a private hosted zone keeps your internal map inside the VPCs you choose.

ЁЯФз How (in this repo)

dns() in vpc/demo.py computes the resolver as ipaddress.ip_network(VPC)[2] and looks names up in a small private zone. The last line asks from the partner VPC, which the zone is not associated with. The snippet below builds the same zone with an explicit association list, so you can add the partner.

ЁЯзк Try it

python3 vpc/demo.py dns
python3 - <<'EOF'
import ipaddress
for vpc in ("10.20.0.0/16", "10.30.0.0/16", "172.31.0.0/16", "192.168.4.0/22"):
    print(vpc.ljust(15), "resolver", ipaddress.ip_network(vpc)[2])
EOF
python3 - <<'EOF'
zone = {"name": "school.internal", "vpcs": {"school"}, "records": {"db.school.internal": "10.20.96.15"}}
def resolve(name, from_vpc):
    if from_vpc not in zone["vpcs"]: return "NXDOMAIN тАФ zone not associated with " + from_vpc
    return zone["records"].get(name, "NXDOMAIN тАФ no such record")
print(resolve("db.school.internal", "school"))
print(resolve("db.school.internal", "partner"))
zone["vpcs"].add("partner")                    # associate the zone with the partner VPC too
print(resolve("db.school.internal", "partner"))
EOF

тЬЕ Verify тАФ what you should see

dns prints:

тФАтФА every VPC has a resolver at base + 2 = 10.20.0.2 (and 169.254.169.253); enableDnsSupport + enableDnsHostnames on
   db.school.internal                         тЖТ 10.20.96.15
   app.school.internal                        тЖТ 10.20.48.25
   db.school.internal (from the partner VPC)  тЖТ no answer тАФ private zone not associated with that VPC

The first snippet prints resolver 10.20.0.2, 10.30.0.2, 172.31.0.2 and 192.168.4.2. The second prints 10.20.96.15, then NXDOMAIN тАФ zone not associated with partner, then 10.20.96.15 once the partner is associated.

ЁЯПБ What you just proved

You can find any VPC's phone book from its CIDR, and you know that a private name answers only where its zone is associated.

тЪая╕П Common mistakes

ЁЯПн In production

Teams use one private zone per environment (prod.school.internal), associate it with every VPC that needs it, and use Resolver endpoints for names shared with the office.

On a real account (needs the AWS CLI and credentials; IDs are examples):

aws ec2 modify-vpc-attribute --vpc-id vpc-0123456789abcdef0 --enable-dns-support '{"Value":true}'
aws ec2 modify-vpc-attribute --vpc-id vpc-0123456789abcdef0 --enable-dns-hostnames '{"Value":true}'
aws route53 create-hosted-zone --name school.internal --caller-reference school-2026-09-27 \
    --vpc VPCRegion=ap-south-1,VPCId=vpc-0123456789abcdef0
aws route53 associate-vpc-with-hosted-zone --hosted-zone-id Z0123456789EXAMPLE \
    --vpc VPCRegion=ap-south-1,VPCId=vpc-0partner0000000a

тПня╕П Next

The partner school needs more than our phone book тАФ it needs a corridor. VPC peering joins two campuses.

git checkout lesson-09-peering
тЖР PreviousendpointsNext тЖТpeering

This page is the lesson's README from the lesson-08-dns branch, shown here so the whole School stays on one site. Code files open on GitHub at the same branch.