Speaking of Cloud Environment where auto
- scaling, self-healing servers have replaced the more conservative way of
scaling – We can’t have classic manual way of DNS management. I.e. Each new instance launched by auto
scaling group should assign itself a human-readable hostname. In these cases Instance
itself should be equipped enough to add its corresponding entry under Route 53.
We can make use of Route53 and PowerShell to impalement this automation from Windows
machine.
What Is Route 53?
Route 53 is a complete DNS solution provided by Amazon that
lets you control every aspect of the name resolution process for your domain. You
choose a registrar to reserve your domain, as common practice
they often be managing all the DNSs for your domain. But if you are
looking for more granular control over this Route 53 is one of the services you
should be interested in. A details description of Route 53 is beyond the scope
of this article, but you can check at AWS Route 53
Automation Script
I have wrapped the whole script into a function.We have used PowerShell V3 , consider that as prerequisite.Having Amazon cmdlets loaded should be done before expecting it to work.Pointers related to it's functioning :
I have wrapped the whole script into a function.We have used PowerShell V3 , consider that as prerequisite.Having Amazon cmdlets loaded should be done before expecting it to work.Pointers related to it's functioning :
- Execution of this script will only materialize given working pair of Secret Key & Access Key
- It validates the Record type with values mentioned under ValidateSet
- If wait is specified, the function will wait until AWS confirms that the change is in sync. To be more specific it verifies whether entry has been propagated under specified Zone. By default i have kept it as false
- If you have mentioned wait as true, waitinterval specifies the interval in milliseconds between each polling
- Every parameter can be overridden why calling this function
Function Create-AwsRoute53Record
{
[CmdletBinding(SupportsShouldProcess=$true)]
Param (
[String]$AccessKeyID="****************",
[String]$SecretAccessKeyID="*****************",
[String]$Region="us-east-1",
[Parameter(Mandatory=$true)] $Zone,
[ValidateSet("A", "SOA", "PTR", "MX", "CNAME","TXT","SRV","SPF","AAAA","NS")]
[String]$RecordType,
[Parameter(Mandatory=$true)]
[String]$Name,
[Parameter(Mandatory=$true)]$Value,
$TTL,
$wait = $false,
[int]$waitinterval = 1000
)
Process
{
Set-AWSCredentials -AccessKey $InfoObject.AccessKey -SecretKey $InfoObject.SecretKey
Set-DefaultAWSRegion -Region $region
$zoneEntry = (Get-R53HostedZones) | ? {$_.Name -eq "$($Zone)."}
$hostedZone = $zoneEntry.Id
if (@($zoneEntry).count -eq 1) {
$Record = new-object Amazon.Route53.Model.ResourceRecord
$record.Value = $Value
#add the trailing dot
if (!($Name.EndsWith(".")) -and $Name)
{$Name += "."}
$ResourceRecordSet = New-Object Amazon.Route53.Model.ResourceRecordSet
$ResourceRecordSet.Type = $RecordType
$ResourceRecordSet.ResourceRecords = $Record
$ResourceRecordSet.Name = $Name
$ResourceRecordSet.TTL = $TTL
$change = New-Object Amazon.Route53.Model.Change
$change.Action = "Create"
$change.ResourceRecordSet = $ResourceRecordSet
$Changes = (New-Object -TypeName System.Collections.ArrayList($null))
$Changes.Add($Change)
Try
{
$result = Edit-R53ResourceRecordSet -ChangeBatch_Changes $Changes -HostedZoneId $hostedZone
}
Catch [system.Exception]
{
Write-error $error[0]
}
if ($result)
{
if ($wait)
{
#Keep polling the result until it is no longer pending
Do
{
#get change status
if ($SecondPoll)
{Start-Sleep -Milliseconds $waitinterval}
$status=Get-R53Change -Id $result.Id
$SecondPoll = $true
Write-verbose "Waiting for changes to sync. Current status is $($status.Status.Value)"
}
Until ($status.Status.Value -eq "INSYNC")
}
$Status
}
}
}
}
{
[CmdletBinding(SupportsShouldProcess=$true)]
Param (
[String]$AccessKeyID="****************",
[String]$SecretAccessKeyID="*****************",
[String]$Region="us-east-1",
[Parameter(Mandatory=$true)] $Zone,
[ValidateSet("A", "SOA", "PTR", "MX", "CNAME","TXT","SRV","SPF","AAAA","NS")]
[String]$RecordType,
[Parameter(Mandatory=$true)]
[String]$Name,
[Parameter(Mandatory=$true)]$Value,
$TTL,
$wait = $false,
[int]$waitinterval = 1000
)
Process
{
Set-AWSCredentials -AccessKey $InfoObject.AccessKey -SecretKey $InfoObject.SecretKey
Set-DefaultAWSRegion -Region $region
$zoneEntry = (Get-R53HostedZones) | ? {$_.Name -eq "$($Zone)."}
$hostedZone = $zoneEntry.Id
if (@($zoneEntry).count -eq 1) {
$Record = new-object Amazon.Route53.Model.ResourceRecord
$record.Value = $Value
#add the trailing dot
if (!($Name.EndsWith(".")) -and $Name)
{$Name += "."}
$ResourceRecordSet = New-Object Amazon.Route53.Model.ResourceRecordSet
$ResourceRecordSet.Type = $RecordType
$ResourceRecordSet.ResourceRecords = $Record
$ResourceRecordSet.Name = $Name
$ResourceRecordSet.TTL = $TTL
$change = New-Object Amazon.Route53.Model.Change
$change.Action = "Create"
$change.ResourceRecordSet = $ResourceRecordSet
$Changes = (New-Object -TypeName System.Collections.ArrayList($null))
$Changes.Add($Change)
Try
{
$result = Edit-R53ResourceRecordSet -ChangeBatch_Changes $Changes -HostedZoneId $hostedZone
}
Catch [system.Exception]
{
Write-error $error[0]
}
if ($result)
{
if ($wait)
{
#Keep polling the result until it is no longer pending
Do
{
#get change status
if ($SecondPoll)
{Start-Sleep -Milliseconds $waitinterval}
$status=Get-R53Change -Id $result.Id
$SecondPoll = $true
Write-verbose "Waiting for changes to sync. Current status is $($status.Status.Value)"
}
Until ($status.Status.Value -eq "INSYNC")
}
$Status
}
}
}
}
Script Execution
Below we have a sample execution which captures the Public IP of AWS instance , and subsequently add it to specified Zone as A Name record Type.
# Change the values as per your specification i.e Zone , Name , RecordType and TTL. Wait is enabled to provide added control.
$LocalIP = Invoke-RestMethod -Uri http://169.254.169.254/latest/meta-data/local-ipv4 -Method Get
$LocalIP=$LocalIP.Trim()
Create-AwsRoute53Record -Zone xyz.com -RecordType A -Name testing.xyz.com -Value $LocalIP -TTL 500 -wait $true