Ansible Dynamic Inventory from SharePoint
June 29, 2017
Out in Enterprise-land, SharePoint is a fact of life. If you are using a SharePoint list as the source of truth for your equipment documentation, then you can use an Ansible Dynamic Inventory Python script to generate your Ansible inventory. Basically, instead of trying to keep a file like /etc/ansible/hosts up to date, you can specify a python script that will generate your inventory dynamically from your source of truth, in this case SharePoint.
SharePoint 2013 includes a REST API that you can use to grab data in JSON or ATOM formats, but we will use JSON since the output from the dynamic inventory script must also be JSON. In order to interact with SharePoint from Python, we will use the “requests” Python module along with “requests_ntlm” to handle NTLM authentication to SharePoint. We will download the data and store it in a dictionary object. Also, for the sake of simplicity, let’s say you have a SharePoint list called of devices called “Devices” with the columns “Title” and “Model.” The Title column for each device is also a valid hostname for the device so that Ansible can use it to connect to the device.
First of all, the dynamic inventory script must support being called in two ways:
ansible_dynamic_inventory.py --list
ansible_dynamic_inventory.py --host <hostname>
Using –list should print out the entire inventory in JSON, and using –host
#!/bin/python
import os, sys, getopt
import json
import requests
from requests_ntlm import HttpNtlmAuth
username = 'sharepointuser'
password = os.environ['SHAREPOINTPASSWORD']
try:
opts, args = getopt.getopt(sys.argv[1:],"h",["list","host"])
except getopt.GetoptError:
print "%s [--list]" % __file__
print "%s [--host <hostname>]" % __file__
sys.exit(2)
if len(sys.argv) < 2:
print "%s [--list]" % __file__
print "%s [--host <hostname>]" % __file__
sys.exit(2)
for opt, arg in opts:
if opt == '-h':
print "%s [--list]" % __file__
print "%s [--host <hostname>]" % __file__
sys.exit()
elif opt == '--host':
print "{"
print "}"
sys.exit()
# else must be --list, so go on with script
Notice above that there is a username and password variable that you must set. The password variable as it is is set from the SHAREPOINTPASSWORD environment variable. After that, we are ready to connect to SharePoint and get our data.
headers = {'Accept': 'application/json;odata=verbose'}
response = requests.get("https://sharepoint.example.com/department/it/_api/web/lists/GetByTitle('Devices')/Items",auth=HttpNtlmAuth("ad\\" + username, password), headers=headers)
data = response.json()
The code above connects to SharePoint, authenticates, asks for the list data in JSON format, and saves it in a JSON object called “data.” Make sure you use your actual AD domain if it’s not “AD.”
Next, let’s use a Python dictionary to prepare our output. We will create groups by model and set a “netos” var for each group. Setting up the dictionary is a little verbose since we have to initialize each level of the hierarchy as a dict or a list as appropriate:
inventory = {}
inventory['c6500s'] = {}
inventory['c4500s'] = {}
inventory['c3850s'] = {}
inventory['c3750Xs'] = {}
inventory['c3560s'] = {}
inventory['c5516Xs'] = {}
inventory['c9372s'] = {}
inventory['c6500s']['hosts'] = []
inventory['c4500s']['hosts'] = []
inventory['c3850s']['hosts'] = []
inventory['c3750Xs']['hosts'] = []
inventory['c3560s']['hosts'] = []
inventory['c5516Xs']['hosts'] = []
inventory['c9372s']['hosts'] = []
inventory['c6500s']['vars'] = {'netos': 'ios'}
inventory['c4500s']['vars'] = {'netos': 'ios'}
inventory['c3850s']['vars'] = {'netos': 'ios'}
inventory['c3750Xs']['vars'] = {'netos': 'ios'}
inventory['c3560s']['vars'] = {'netos': 'ios'}
inventory['c5516Xs']['vars'] = {'netos': 'asa'}
inventory['c9372s']['vars'] = {'netos': 'nxos'}
inventory['c6500s']['children'] = []
inventory['c4500s']['children'] = []
inventory['c3850s']['children'] = []
inventory['c3750Xs']['children'] = []
inventory['c3560s']['children'] = []
inventory['c5516Xs']['children'] = []
inventory['c9372s']['children'] = []
Next we use the data from SharePoint to fill out the dictionary we just set up. The JSON data from SharePoint is in data[‘d’][‘results’], don’t ask me what the significance of the “d” is.
for device in data['d']['results']:
if device['Model'] == 'Catalyst 6509-E':
inventory['c6500s']['hosts'].append(device['Title'])
elif device['Model'] == 'Catalyst 4507R+E':
inventory['c4500s']['hosts'].append(device['Title'])
elif device['Model'] == 'Catalyst 3850':
inventory['c3850s']['hosts'].append(device['Title'])
elif device['Model'] == 'Catalyst 3750X':
inventory['c3750Xs']['hosts'].append(device['Title'])
elif device['Model'] == 'Catalyst 3560' or device['Model'] == 'Catalyst 3560G':
inventory['c3560s']['hosts'].append(device['Title'])
elif device['Model'] == 'ASA 5516-X':
inventory['c5516Xs']['hosts'].append(device['Title'])
elif device['Model'] == 'Nexus 9372':
inventory['c9372s']['hosts'].append(device['Title'])
Finally, we output our inventory in JSON:
print json.dumps(inventory, indent=4, separators=(',', ': '))
Sample output:
{
"c4500s": {
"hosts": [
"dist-switch1",
"dist-switch2",
"dist-switch3"
],
"children": [],
"vars": {
"netos": "ios"
}
},
"c3750Xs": {
"hosts": [
"room10-switch1",
"room20-switch1",
"room30-switch2"
],
"children": [],
"vars": {
"netos": "ios"
}
},
"c5516Xs": {
"hosts": [
"firewall1",
"firewall2",
"firewall3"
],
"children": [],
"vars": {
"netos": "asa"
}
}
}
Usage with ansible-playbook:
export SHAREPOINTPASSWORD='hithere'
ansible-playbook main.yml -vvvv -f 10 -i /etc/ansible/ansible_dyn_inventory.py --extra-vars '{"cli": {"username": "thisguy","password": "SneakyPassword11","host": "","timeout": "60"}, "asacli": {"username": "thisguy","password": "SneakyPassword11","host": "","timeout": "60","authorize": "yes","auth_pass": "SneakyPassword11"}}'
Full script:
#!/bin/python
import os, sys, getopt
import json
import requests
from requests_ntlm import HttpNtlmAuth
username = 'sharepointuser'
password = os.environ['SHAREPOINTPASSWORD']
try:
opts, args = getopt.getopt(sys.argv[1:],"h",["list","host"])
except getopt.GetoptError:
print "%s [--list]" % __file__
print "%s [--host <hostname>]" % __file__
sys.exit(2)
if len(sys.argv) < 2:
print "%s [--list]" % __file__
print "%s [--host <hostname>]" % __file__
sys.exit(2)
for opt, arg in opts:
if opt == '-h':
print "%s [--list]" % __file__
print "%s [--host <hostname>]" % __file__
sys.exit()
elif opt == '--host':
print "{"
print "}"
sys.exit()
# else must be --list, so go on with script
headers = {'Accept': 'application/json;odata=verbose'}
response = requests.get("https://sharepoint.example.com/department/it/_api/web/lists/GetByTitle('Devices')/Items",auth=HttpNtlmAuth("ad\\" + username, password), headers=headers)
data = response.json()
inventory = {}
inventory['c6500s'] = {}
inventory['c4500s'] = {}
inventory['c3850s'] = {}
inventory['c3750Xs'] = {}
inventory['c3560s'] = {}
inventory['c5516Xs'] = {}
inventory['c9372s'] = {}
inventory['c6500s']['hosts'] = []
inventory['c4500s']['hosts'] = []
inventory['c3850s']['hosts'] = []
inventory['c3750Xs']['hosts'] = []
inventory['c3560s']['hosts'] = []
inventory['c5516Xs']['hosts'] = []
inventory['c9372s']['hosts'] = []
inventory['c6500s']['vars'] = {'netos': 'ios'}
inventory['c4500s']['vars'] = {'netos': 'ios'}
inventory['c3850s']['vars'] = {'netos': 'ios'}
inventory['c3750Xs']['vars'] = {'netos': 'ios'}
inventory['c3560s']['vars'] = {'netos': 'ios'}
inventory['c5516Xs']['vars'] = {'netos': 'asa'}
inventory['c9372s']['vars'] = {'netos': 'nxos'}
inventory['c6500s']['children'] = []
inventory['c4500s']['children'] = []
inventory['c3850s']['children'] = []
inventory['c3750Xs']['children'] = []
inventory['c3560s']['children'] = []
inventory['c5516Xs']['children'] = []
inventory['c9372s']['children'] = []
for device in data['d']['results']:
if device['Model'] == 'Catalyst 6509-E':
inventory['c6500s']['hosts'].append(device['Title'])
elif device['Model'] == 'Catalyst 4507R+E':
inventory['c4500s']['hosts'].append(device['Title'])
elif device['Model'] == 'Catalyst 3850':
inventory['c3850s']['hosts'].append(device['Title'])
elif device['Model'] == 'Catalyst 3750X':
inventory['c3750Xs']['hosts'].append(device['Title'])
elif device['Model'] == 'Catalyst 3560' or device['Model'] == 'Catalyst 3560G':
inventory['c3560s']['hosts'].append(device['Title'])
elif device['Model'] == 'ASA 5516-X':
inventory['c5516Xs']['hosts'].append(device['Title'])
elif device['Model'] == 'Nexus 9372':
inventory['c9372s']['hosts'].append(device['Title'])
print json.dumps(inventory, indent=4, separators=(',', ': '))
Links:
Comments