Last active
December 2, 2020 18:03
-
-
Save remie/2a40516af604518233ae to your computer and use it in GitHub Desktop.
This file contains 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
#!/usr/bin/env node | |
########################################################################################### | |
# Dynamic inventory for Ansible with DNS | |
# Author Remie Bolte (https://nl.linkedin.com/in/remiebolte) | |
# | |
# This NodeJS script generates a dynamic inventory based on DNS TXT records. | |
# | |
# If you use the “--inventory” switch when calling Ansible, you can provide the | |
# path to a directory which includes inventory files. Ansible will automtically | |
# run any executable script in that directory to see if provides the JSON output | |
# describing a dynamic inventory. | |
# | |
# Using the following NodeJS script, you can read the information from your TXT | |
# record and provide the JSON output that Ansible requires. | |
# | |
# This script will query the DNS server for the value of the TXT record associated | |
# with “host._ansible.yourdomain.com”. | |
# | |
# It expects to retrieve a string-based value of: | |
# “hostname=web-host-01.yourdomain.com;sections=webserver,boston” | |
# | |
# The second TXT record might be: | |
# “hostname=db-host-01.yourdomain.com;sections=database,atlanta” | |
# | |
# The script will parse these TXT records and output the following JSON: | |
# { “webserver”: { “hosts”: [ “web-host-01.yourdomain.com” ] }, “boston”: { “hosts”: [ “web-host-01.yourdomain.com” ] }, “database”: { “hosts”: [ “db-host-01.yourdomain.com” ] }, “atlanta”: { “hosts”: [ “db-host-01.yourdomain.com” ] } | |
# | |
# Now you only need to create a script that updates your DNS when provisiong or | |
# decommissioning a server, by either adding or removing the TXT record for that | |
# specific host. | |
# | |
# TODO: | |
# - Add host variables | |
# - Add encryption/decription of TXT record metadata | |
# | |
# Copyright (c) 2016 Remie Bolte | |
# This snippet is license with the MIT open source license | |
# https://opensource.org/licenses/MIT | |
# | |
# Permission is hereby granted, free of charge, to any person obtaining a copy of | |
# this software and associated documentation files (the "Software"), to deal in the | |
# Software without restriction, including without limitation the rights to use, copy, | |
# modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, | |
# and to permit persons to whom the Software is furnished to do so, subject to the | |
# following conditions: | |
# | |
# The above copyright notice and this permission notice shall be included in all copies | |
# or substantial portions of the Software. | |
# | |
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, | |
# INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A | |
# PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT | |
# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF | |
# CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE | |
# OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |
# | |
########################################################################################### | |
// ------------------------------------------------------------------------------------------ Dependencies | |
var dns = require('dns'); | |
var _ = require('lodash'); | |
var program = require('commander'); | |
// ------------------------------------------------------------------------------------------ Variables | |
// Replace 'yourdomain.com' in this variable with your DNS domain name | |
var domain = 'host._ansible.yourdomain.com'; | |
// ------------------------------------------------------------------------------------------ Command-line parser | |
program | |
.option('-l, --list', 'JSON list of sections/hosts') | |
.option('-h, --host [hostname]', 'retrieve variables for specified host (not supported)') | |
.parse(process.argv); | |
generateDynamicInventoryFromDNS(domain, function(inventory) { | |
if(program.list) { | |
// Return list of hosts | |
return console.log(JSON.stringify(inventory)); | |
} else if (program.host) { | |
// Host variables are currently not supported, returning empty object | |
return console.log({}); | |
} else { | |
// Wait... what? | |
program.outputHelp(); | |
} | |
}); | |
// ------------------------------------------------------------------------------------------ Private functions | |
function generateDynamicInventoryFromDNS(domain, callback) { | |
// Retrieve TXT records from DNS | |
dns.resolveTxt(domain, function(error, records) { | |
// Variable that will contain the inventory | |
var inventory = {}; | |
// Iterate over TXT records | |
records.forEach(function(record) { | |
// Find each key/value pair in TXT record | |
var data = record[0].split(';'); | |
// temporary object to store key/value pairs | |
var store = Array(); | |
_.each(data, function(items) { | |
// Parse key/value pair and store them in temporary object | |
var item = items.split("="); | |
var key=item[0]; | |
var value=item[1]; | |
store[key] = value; | |
}); | |
// Check if this host has sections or is ungrouped | |
if(_.has(store, 'sections')) { | |
// Iterate over the sections this host is assigned to | |
_.each(store['sections'].split(','), function(section) { | |
// Initialize section if it does not already exist | |
if(!_.has(inventory, section)) { | |
inventory[section] = {} | |
inventory[section]['hosts'] = Array(); | |
}; | |
// Add host to inventory (unless it exists) | |
if(!_.includes(inventory[section]['hosts'], store['hostname'])) { | |
inventory[section]['hosts'].push(store['hostname']); | |
} | |
}); | |
} else { | |
// Host is ungrouped | |
// Initialize ungrouped section if it does not already exist | |
if(!_.has(inventory, 'ungrouped')) { | |
inventory['ungrouped'] = {}; | |
inventory['ungrouped']['hosts'] = Array(); | |
} | |
// Add host to inventory (unless it exists) | |
if(!_.includes(inventory['ungrouped']['hosts'], store['hostname'])) { | |
inventory['ungrouped']['hosts'].push(store['hostname']); | |
} | |
}; | |
}); | |
callback(inventory); | |
}); | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment