When initially setting up automated DNS management in AWS Route53, many developers (myself included) start with permissive IAM policies using Resource: "*"
. This works fine with one or two hosted zones, but becomes problematic when you need to:
- Add new zones that shouldn't be modified by existing automation
- Implement least-privilege security practices
- Separate production and development DNS environments
The ARN format for Route53 hosted zones is:
arn:aws:route53:::hostedzone/<hosted-zone-id>
To get these values:
- Via AWS Console:
1. Open Route53 dashboard 2. Navigate to "Hosted zones" 3. Note the "Hosted zone ID" column (format: ZXXXXXXXXXXXX)
- Via AWS CLI:
aws route53 list-hosted-zones --query 'HostedZones[*].[Id,Name]' --output text
Here's an example policy that allows DNS updates only for specific hosted zones:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"route53:ChangeResourceRecordSets",
"route53:ListResourceRecordSets"
],
"Resource": [
"arn:aws:route53:::hostedzone/Z123456789ABC",
"arn:aws:route53:::hostedzone/ZDEF987654321"
]
},
{
"Effect": "Allow",
"Action": "route53:ListHostedZones",
"Resource": "*"
}
]
}
For automation scripts that need to discover zones dynamically, you can use AWS SDKs. Here's a Python example using Boto3:
import boto3
def get_hosted_zone_arn(domain_name):
client = boto3.client('route53')
response = client.list_hosted_zones()
for zone in response['HostedZones']:
if zone['Name'] == domain_name:
return f"arn:aws:route53:::hostedzone/{zone['Id'].split('/')[-1]}"
raise ValueError(f"Hosted zone not found for {domain_name}")
# Usage:
print(get_hosted_zone_arn("example.com.")) # Note: trailing dot required
When working with AWS Route53 hosted zones in IAM policies, you'll need to specify the Amazon Resource Name (ARN) in the format:
arn:aws:route53:::/
The complete ARN pattern for Route53 hosted zones follows this structure:
arn:aws:route53:::hostedzone/[HOSTED_ZONE_ID]
You can find your hosted zone ID through several methods:
Using AWS Management Console
Navigate to Route53 service > Hosted zones. The ID appears in the format similar to:
Z1PA6795UKMFR9
Using AWS CLI
Run this command to list all hosted zones:
aws route53 list-hosted-zones
The output will include entries like:
{
"Id": "/hostedzone/Z1PA6795UKMFR9",
"Name": "example.com.",
...
}
Once you have the hosted zone ID, construct the ARN by combining it with the static prefix:
arn:aws:route53:::hostedzone/Z1PA6795UKMFR9
Here's how to restrict access to specific hosted zones in your IAM policy:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"route53:ChangeResourceRecordSets"
],
"Resource": [
"arn:aws:route53:::hostedzone/Z1PA6795UKMFR9",
"arn:aws:route53:::hostedzone/Z2PA6795XKMFQ8"
]
}
]
}
For more granular control, you can combine hosted zone restrictions with record set conditions:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"route53:ChangeResourceRecordSets"
],
"Resource": "arn:aws:route53:::hostedzone/Z1PA6795UKMFR9",
"Condition": {
"StringLike": {
"route53:ChangeResourceRecordSetsNormalizedRecordNames": [
"*.dev.example.com"
]
}
}
}
]
}
If you encounter permission issues:
- Verify the hosted zone ID is correct (case-sensitive)
- Ensure the ARN format matches exactly
- Check for typos in the policy JSON
- Remember that changes to IAM policies may take time to propagate