Route 53 via SDK Basics
Six short examples that take you from an empty account to a working DNS zone: create a hosted zone, add A and CNAME records, read them back, wait for propagation, and clean up.
Busque em todas as páginas da documentação
Six short examples that take you from an empty account to a working DNS zone: create a hosted zone, add A and CNAME records, read them back, wait for propagation, and clean up.
Each example shows the same operation in both Python (boto3) and TypeScript (AWS SDK for JavaScript v3), so the mechanics translate one to one. Route 53 is a global service - you construct the client without a meaningful region, though the v3 client still wants region: "us-east-1".
pip install boto3 (boto3 1.43.x, Python 3.10+). TypeScript: npm install @aws-sdk/client-route-53 (Node.js 18+).aws configure, environment variables, or an IAM role with route53:* on the relevant zones.CreateHostedZone needs a domain name and a unique CallerReference. It returns the zone id and the four name servers to delegate to at your registrar.
# --- Python (boto3) ---
import boto3, time
r53 = boto3.client("route53")
resp = r53.create_hosted_zone(
Name="example.com",
CallerReference=f"example-{int(time.time())}", # must be unique
HostedZoneConfig={"Comment": "app zone", "PrivateZone": False},
)
zone_id = resp["HostedZone"]["Id"].split("/")[-1] # strip /hostedzone/
print(zone_id, resp["DelegationSet"]["NameServers"])// --- TypeScript (AWS SDK v3) ---
import { Route53Client, CreateHostedZoneCommand } from "@aws-sdk/client-route-53";
const r53 = new Route53Client({ region: "us-east-1" });
const resp = await r53.send(new CreateHostedZoneCommand({
Name: "example.com",
CallerReference: `example-${Date.now()}`, // must be unique
HostedZoneConfig: { Comment: "app zone", PrivateZone: false },
}));
const zoneId = resp.HostedZone?.Id?.split("/").pop(); // strip /hostedzone/
console.log(zoneId, resp.DelegationSet?.NameServers);CallerReference is an idempotency token; reusing one that already made a zone errors instead of duplicating.Id is prefixed with /hostedzone/; most other calls want the bare id, so split it off.NS and SOA records already created for you.NameServers as the domain's authoritative servers.An A record maps a name to an IPv4 address. Use UPSERT so re-running the script overwrites rather than fails.
# --- Python (boto3) ---
r53.change_resource_record_sets(
HostedZoneId=zone_id,
ChangeBatch={"Changes": [{
"Action": "UPSERT",
"ResourceRecordSet": {
"Name": "app.example.com",
"Type": "A",
"TTL": 300,
"ResourceRecords": [{"Value": "203.0.113.25"}],
},
}]},
)// --- TypeScript (AWS SDK v3) ---
import { ChangeResourceRecordSetsCommand } from "@aws-sdk/client-route-53";
await r53.send(new ChangeResourceRecordSetsCommand({
HostedZoneId: zoneId,
ChangeBatch: { Changes: [{
Action: "UPSERT",
ResourceRecordSet: {
Name: "app.example.com",
Type: "A",
TTL: 300,
ResourceRecords: [{ Value: "203.0.113.25" }],
},
}]},
}));ResourceRecords is a list, so several IPs under one name give simple round-robin.TTL is in seconds and controls how long resolvers cache the answer.UPSERT creates the record or overwrites an existing one with the same name and type.PENDING; the record is not global yet (see example 5).A CNAME points one hostname at another. It cannot live at the zone apex and cannot coexist with other record types on the same name.
# --- Python (boto3) ---
r53.change_resource_record_sets(
HostedZoneId=zone_id,
ChangeBatch={"Changes": [{
"Action": "UPSERT",
"ResourceRecordSet": {
"Name": "www.example.com",
"Type": "CNAME",
"TTL": 300,
"ResourceRecords": [{"Value": "app.example.com"}],
},
}]},
)// --- TypeScript (AWS SDK v3) ---
await r53.send(new ChangeResourceRecordSetsCommand({
HostedZoneId: zoneId,
ChangeBatch: { Changes: [{
Action: "UPSERT",
ResourceRecordSet: {
Name: "www.example.com",
Type: "CNAME",
TTL: 300,
ResourceRecords: [{ Value: "app.example.com" }],
},
}]},
}));www.example.com now resolves to whatever app.example.com resolves to.CNAME at the apex (example.com itself) is invalid; use an alias record there instead.CNAME value is allowed, and no other type may share the name.CNAME.Related: Record Types & Alias Records - when to reach for an alias instead of a CNAME.
ListResourceRecordSets reads back everything in the zone, including the NS and SOA records created with it.
# --- Python (boto3) ---
paginator = r53.get_paginator("list_resource_record_sets")
for page in paginator.paginate(HostedZoneId=zone_id):
for rrset in page["ResourceRecordSets"]:
print(rrset["Name"], rrset["Type"])// --- TypeScript (AWS SDK v3) ---
import { paginateListResourceRecordSets } from "@aws-sdk/client-route-53";
for await (const page of paginateListResourceRecordSets(
{ client: r53 }, { HostedZoneId: zoneId })) {
for (const rrset of page.ResourceRecordSets ?? []) {
console.log(rrset.Name, rrset.Type);
}
}NS and SOA records are managed for you; do not delete them.ResourceRecordSet is the exact shape you pass back to DELETE.Writes are asynchronous. Capture the change id and wait for INSYNC before trusting resolution.
# --- Python (boto3) ---
resp = r53.change_resource_record_sets(
HostedZoneId=zone_id,
ChangeBatch={"Changes": [{
"Action": "UPSERT",
"ResourceRecordSet": {
"Name": "api.example.com", "Type": "A", "TTL": 60,
"ResourceRecords": [{"Value": "203.0.113.40"}],
},
}]},
)
change_id = resp["ChangeInfo"]["Id"]
r53.get_waiter("resource_record_sets_changed").wait(Id=change_id)
print("now INSYNC")// --- TypeScript (AWS SDK v3) ---
import { waitUntilResourceRecordSetsChanged } from "@aws-sdk/client-route-53";
const resp = await r53.send(new ChangeResourceRecordSetsCommand({
HostedZoneId: zoneId,
ChangeBatch: { Changes: [{
Action: "UPSERT",
ResourceRecordSet: {
Name: "api.example.com", Type: "A", TTL: 60,
ResourceRecords: [{ Value: "203.0.113.40" }],
},
}]},
}));
const changeId = resp.ChangeInfo?.Id;
await waitUntilResourceRecordSetsChanged({ client: r53, maxWaitTime: 300 }, { Id: changeId });
console.log("now INSYNC");GetChange until the status flips from PENDING to INSYNC.INSYNC means Route 53's servers agree; resolver caches still honor the old TTL.DELETE needs the record's exact current values, so read it first or reconstruct it precisely.
# --- Python (boto3) ---
r53.change_resource_record_sets(
HostedZoneId=zone_id,
ChangeBatch={"Changes": [{
"Action": "DELETE",
"ResourceRecordSet": {
"Name": "www.example.com", "Type": "CNAME", "TTL": 300,
"ResourceRecords": [{"Value": "app.example.com"}],
},
}]},
)// --- TypeScript (AWS SDK v3) ---
await r53.send(new ChangeResourceRecordSetsCommand({
HostedZoneId: zoneId,
ChangeBatch: { Changes: [{
Action: "DELETE",
ResourceRecordSet: {
Name: "www.example.com", Type: "CNAME", TTL: 300,
ResourceRecords: [{ Value: "app.example.com" }],
},
}]},
}));DELETE fails unless every field - name, type, TTL, and values - matches the live record.ListResourceRecordSets, find the record, and pass it straight back.NS/SOA records first, then DeleteHostedZone.INSYNC on its own schedule.Stack versions: This page was written for boto3 1.43.x (Python 3.10+) and the AWS SDK for JavaScript v3 (Node.js 18+).
Revisado por Chris St. John·Última atualização: 23 de jul. de 2026