Last active
October 3, 2021 17:32
-
-
Save wparad/2f5ec426b73eaab903c037101fab49f1 to your computer and use it in GitHub Desktop.
Find Route53 records pointing to unowned IP Addresses
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
// This is also a config rule: https://eu-west-2.console.aws.amazon.com/lambda/home?region=eu-west-2#/create/app?applicationId=arn:aws:serverlessrepo:eu-west-1:922723803004:applications/Elastic-IP-Config-Rule | |
const { Route53, EC2, config } = require('aws-sdk'); | |
config.region = 'eu-west-1'; | |
async function findDanglingElasticIpAddresses(dryRun = true) { | |
const route53 = new Route53(); | |
let hostedZoneIds; | |
try { | |
console.log('Looking up hosted zones:'); | |
hostedZoneIds = (await route53.listHostedZones({ }).promise()).HostedZones.map(hz => hz.Id); | |
console.log(' :', hostedZoneIds); | |
} catch (error) { | |
console.log(' Failed to retrieve hosted zones', error); | |
process.exit(1); | |
} | |
const addresses = []; | |
const regions = await new EC2().describeRegions().promise().then(data => data.Regions.map(r => r.RegionName)); | |
await Promise.all(regions.map(async region => { | |
const regionalEc2Client = new EC2({ region }); | |
addresses.push(...(await regionalEc2Client.describeAddresses().promise()).Addresses.map(a => a.PublicIp)); | |
})); | |
const recordAddressMap = {}; | |
await Promise.all(hostedZoneIds.map(async hostedZoneId => { | |
console.log('Fetching records for zone: ', hostedZoneId); | |
const params = { HostedZoneId: hostedZoneId }; | |
try { | |
do { | |
const response = await route53.listResourceRecordSets(params).promise(); | |
params.StartRecordIdentifier = response.NextRecordIdentifier; | |
response.ResourceRecordSets | |
.filter(t => !t.AliasTarget && t.Type === 'A') | |
.map(r => ({ hostedZoneId, name: r.Name, type: r.Type, originalRecord: r })) | |
.forEach(r => { | |
r.originalRecord.ResourceRecords.map(rr => rr.Value).forEach(address => { | |
if (!recordAddressMap[address]) { | |
recordAddressMap[address] = []; | |
} | |
recordAddressMap[address].push(r); | |
}); | |
}); | |
} while (params.StartRecordIdentifier); | |
} catch (error) { | |
console.log(' Failed to get records for zone', hostedZoneId, error); | |
} | |
if (!Object.keys(recordAddressMap).length) { | |
return; | |
} | |
addresses.forEach(a => { | |
delete recordAddressMap[a]; | |
}); | |
console.log(Object.keys(recordAddressMap)); | |
console.log('Extraneous Ip Addresses:', Object.keys(recordAddressMap)); | |
if (!dryRun) { | |
console.log(' Updating extraneous records'); | |
const changeSetParams = { | |
HostedZoneId: hostedZoneId, | |
ChangeBatch: { | |
Changes: Object.values(recordAddressMap).flat(1).map(record => { | |
const deleteRecord = record.originalRecord.ResourceRecords.every(r => recordAddressMap[r.Value]); | |
const change = { | |
Action: deleteRecord ? 'DELETE' : 'UPSERT', | |
ResourceRecordSet: record.originalRecord | |
}; | |
if (!deleteRecord) { | |
change.ResourceRecordSet.ResourceRecords = change.ResourceRecordSet.ResourceRecords.filter(r => !recordAddressMap[r.Value]); | |
} | |
return change; | |
}) | |
} | |
}; | |
console.log(JSON.stringify(changeSetParams, null, 2)); | |
await route53.changeResourceRecordSets(changeSetParams).promise(); | |
} | |
})); | |
} | |
findDanglingElasticIpAddresses(true); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This is now a config rule: https://eu-west-2.console.aws.amazon.com/lambda/home?region=eu-west-2#/create/app?applicationId=arn:aws:serverlessrepo:eu-west-1:922723803004:applications/Elastic-IP-Config-Rule