Created
February 7, 2024 12:56
-
-
Save JohnLBevan/19c1fee89fc13660452381a615a9b17f to your computer and use it in GitHub Desktop.
AWS Route53 Zone Migration
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<# | |
.SYNOPSIS | |
Used to help migrate R53 zones by converting the JSON obtained by | |
extracting all record sets from a zone to the JSON required to upload | |
these recordsets to another zone. | |
.DESCRIPTION | |
Covers those tasks described in step 4 of [Migrating an AWS Zone](https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/hosted-zones-migrating.html#hosted-zones-migrating-create-file) | |
i.e. to convert the output of `aws route53 list-resource-record-sets --hosted-zone-id <hosted-zone-id>` | |
... to the input of `aws route53 change-resource-record-sets --hosted-zone-id id-of-new-hosted-zone --change-batch file://path-to-file-that-contains-records` | |
.PARAMETER Path | |
Path to the JSON file containing the ResourceRecordSets output by | |
`aws route53 list-resource-record-sets --hosted-zone-id <hosted-zone-id>` | |
.PARAMETER InputObject | |
The JSON ResourceRecordSets data, converted to a PSCustomObject. | |
.EXAMPLE | |
``` | |
$sourceHostedZoneId = 'XXXXXX' | |
$destinationHostedZoneId = 'YYYYYY' | |
$sourceJsonFile = 'c:\temp\sourceZone.json' | |
$destinationJsonFile = 'c:\temp\destinationZone.json' | |
aws route53 list-resource-record-sets --hosted-zone-id $sourceHostedZoneId > $sourceJsonFile | |
ConvertTo-AwsR53RecordSetChange -Path $sourceJsonFile -Destination $destinationJsonFile -SourceZone $sourceHostedZoneId -DestinationZone $destinationHostedZoneId | |
aws route53 change-resource-record-sets --hosted-zone-id $destinationHostedZoneId --change-batch $destinationJsonFile | |
``` | |
.NOTES | |
This doesn't chunk the file into sets of 1000 records / 32Kb (step 5 | |
of [Migrating an AWS Zone](https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/hosted-zones-migrating.html#hosted-zones-migrating-create-file)) as this was YAGNI for my requirements. | |
#> | |
function ConvertTo-AwsR53RecordSetChange { | |
[CmdletBinding(DefaultParameterSetName = 'ByPath')] | |
Param ( | |
[Parameter(ParameterSetName='ByPath', Mandatory)] | |
[Parameter(ParameterSetName='ByPathPassthru', Mandatory)] | |
[string]$Path | |
, | |
[Parameter(ParameterSetName='ByJsonObject', Mandatory)] | |
[PSCustomObject]$InputObject | |
, | |
[Parameter(ParameterSetName='ByPath', Mandatory)] | |
[Parameter(ParameterSetName='ByJsonObject', Mandatory)] | |
[string]$Destination | |
, | |
[Parameter(Mandatory)] | |
[string]$SourceZone | |
, | |
[Parameter(Mandatory)] | |
[string]$DestinationZone | |
, | |
[Parameter()] | |
[string]$Comment = '' | |
, | |
[Parameter(ParameterSetName='ByPathPassthru', Mandatory)] | |
[Parameter(ParameterSetName='ByJsonObjectPassthru', Mandatory)] | |
[Switch]$Passthru | |
) | |
if ($PSCmdlet.ParameterSetName -eq 'ByPath') { | |
$InputObject = Get-Content -Path $Path -Raw | ConvertFrom-Json | |
} | |
$DomainRoot = $InputObject.ResourceRecordSets.Name | Sort-Object -Property @{E={$_.Length}} | Select-Object -First 1 | |
[PSCustomObject[]]$records = $InputObject.ResourceRecordSets | | |
Skip-AwsR53RootSoaAndNsRecords -DomainRoot $DomainRoot | | |
Convert-AwsR53AliasZoneId -OldZoneId $SourceZone -NewZoneId $DestinationZone | | |
New-AwsR53ChangeResourceRecord | |
$result = [PSCustomObject]@{ | |
Comment = $Comment | |
Changes = $records | |
} | |
if ($Passthru.IsPresent) { | |
$result | |
} else { | |
[System.IO.File]::WriteAllLines($Destination,( | |
$result | ConvertTo-Json -Depth 10 | |
), [System.Text.Encoding]::UTF8) | |
} | |
} | |
<# | |
.SYNOPSIS | |
Ensures we don't copy the SOA and NS records of the zone being copied; as these will be created by AWS when the target zone is created. | |
However, we need to ensure that if we've got child NS records (e.g. to point to a different nameserver for managing a subdomain) those | |
records are included. | |
.PARAMETER DomainRoot | |
The domain associated with our zone. Note: this entry should be in the same format as the `Name` value in the AWS recordsets; i.e. should | |
include a terminating `.`; e.g. `example.com.`. | |
.PARAMETER InputObject | |
This should accept individual ResourceRecordSet elements. | |
#> | |
Function Skip-AwsR53RootSoaAndNsRecords { | |
[CmdletBinding()] | |
Param ( | |
[Parameter(Mandatory)] | |
[string]$DomainRoot | |
, | |
[Parameter(ValueFromPipeline, Mandatory)] | |
[PSCustomObject]$InputObject | |
) | |
Process { | |
if (!( | |
($InputObject.Type -in ('SOA', 'NS')) -and | |
($InputObject.Name -eq $DomainRoot) | |
)) { | |
$InputObject | |
} | |
} | |
} | |
<# | |
.SYNOPSIS | |
For any alias records, checks if the alias had been referring to a record in the old hosted zone, and where that's the case, update it | |
to target our new zone's ID. All other zone references should not be touched. | |
.PARAMETER OldZoneId | |
The AWS ID of the old hosted zone (i.e. the value provided to parameter `hosted-zone-id` in `aws route53 list-resource-record-sets --hosted-zone-id ...`) | |
.PARAMETER NewZoneId | |
The AWS ID of the new hosted zone (i.e. the value provided to parameter `hosted-zone-id` in `aws route53 change-resource-record-sets --hosted-zone-id ... --change-batch ...`) | |
.PARAMETER InputObject | |
This should accept individual ResourceRecordSet elements. | |
#> | |
Function Convert-AwsR53AliasZoneId { | |
Param ( | |
[Parameter(Mandatory)] | |
[string]$OldZoneId | |
, | |
[Parameter(Mandatory)] | |
[string]$NewZoneId | |
, | |
[Parameter(ValueFromPipeline, Mandatory)] | |
[PSCustomObject]$InputObject | |
) | |
Process { | |
if ("$($InputObject.AliasTarget.HostedZoneId)" -eq $OldZoneId) { | |
$InputObject.AliasTarget.HostedZoneId = $NewZoneId | |
} | |
$InputObject | |
} | |
} | |
<# | |
.SYNOPSIS | |
Converts a ResourceRecordSet element as output by `list-resource-record-sets` to the format required | |
to be used in `change-resource-record-sets`. | |
.PARAMETER Action | |
What action should be given for this record; e.g. `CREATE`, `DELETE, or `UPSERT` | |
See https://docs.aws.amazon.com/cli/latest/reference/route53/change-resource-record-sets.html for more | |
.PARAMETER InputObject | |
This should accept individual ResourceRecordSet elements. | |
#> | |
Function New-AwsR53ChangeResourceRecord { | |
[CmdletBinding()] | |
Param ( | |
[Parameter()] | |
[string]$Action = 'UPSERT' | |
, | |
[Parameter(ValueFromPipeline, Mandatory)] | |
[PSCustomObject]$InputObject | |
) | |
Process { | |
([PSCustomObject]@{ | |
Action = $Action | |
ResourceRecordSet = $InputObject | |
}) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment