Skip to content

Instantly share code, notes, and snippets.

@imcitius
Created July 15, 2021 09:06
Show Gist options
  • Save imcitius/407d9be69dafad6dcfc9a6b257a1b8f2 to your computer and use it in GitHub Desktop.
Save imcitius/407d9be69dafad6dcfc9a6b257a1b8f2 to your computer and use it in GitHub Desktop.
Ansible dynamic inventory script for terraform
#!/usr/bin/env python3
from __future__ import print_function
import base64
import os
import sys
import json
from collections import OrderedDict
import requests
from requests.auth import HTTPBasicAuth
import re
import traceback
tf_state_path = os.environ['TF_STATE_PATH']
project = os.environ['ANSIBLE_PROJECT']
if tf_state_path == '' or project == '':
sys.stderr.write('TF state path or ansible project name not specified\n')
sys.exit(1)
def _init_inventory():
return OrderedDict({
"all": {
"hosts": [],
"vars": {},
"children": []
},
"_meta": {
"hostvars": {}
}
})
def _is_ip_private(ip):
# https://en.wikipedia.org/wiki/Private_network
priv_lo = re.compile("^127\.\d{1,3}\.\d{1,3}\.\d{1,3}$")
priv_24 = re.compile("^10\.\d{1,3}\.\d{1,3}\.\d{1,3}$")
priv_20 = re.compile("^192\.168\.\d{1,3}.\d{1,3}$")
priv_16 = re.compile("^172.(1[6-9]|2[0-9]|3[0-1]).[0-9]{1,3}.[0-9]{1,3}$")
res = priv_lo.match(ip) or priv_24.match(ip) or priv_20.match(ip) or priv_16.match(ip)
return res is not None
def parse_hosts():
resources = tfstate["resources"]
inv = _init_inventory()
for resource in resources:
if resource["mode"] == "managed" and resource["type"] == "vsphere_virtual_machine":
group_name = resource["module"].replace("module.", "")
normalized_group_name = resource["module"].replace("module.", "").replace("_", "-")
if normalized_group_name not in inv['all']['children']:
inv['all']['children'].append(normalized_group_name)
if normalized_group_name not in inv:
inv[normalized_group_name] = {}
inv[normalized_group_name]['hosts'] = []
inv[normalized_group_name]['vars'] = {}
for instance in resource['instances']:
if "attributes" in instance:
attrs = instance['attributes']
else:
raise KeyError("instance has no attributes, module %s, index key: %s" % (module, instance['index_key']))
if ('default_ip_address' in attrs) and (_is_ip_private(attrs['default_ip_address'])):
host = attrs['default_ip_address']
elif ('guest_ip_addresses.0' in attrs) and (_is_ip_private(attrs['guest_ip_addresses.0'])):
host = attrs['guest_ip_addresses.0']
elif ('guest_ip_addresses.1' in attrs) and (_is_ip_private(attrs['guest_ip_addresses.1'])):
host = attrs['guest_ip_addresses.1']
# if host not in inv['all']['hosts']:
# inv['all']['hosts'].append(host)
if host not in inv[normalized_group_name]['hosts']:
inv[normalized_group_name]['hosts'].append(host)
if host not in inv['_meta']['hostvars']:
inv['_meta']['hostvars'][host] = {}
inv['_meta']['hostvars'][host]['hostname'] = attrs['name']
inv['_meta']['hostvars'][host]['group'] = normalized_group_name
return inv
def _processing(tfstate, inventory):
if 'resources' not in tfstate:
raise KeyError("resources not found in tfstate")
resources = tfstate["resources"]
outputs = tfstate["outputs"]
inventory = parse_hosts()
inventory['all']['vars']['project'] = project
# inventory['all']['hosts'] = parse_hosts()['all']['hosts']
for group in outputs:
normalized_group_name = group.replace("_", "-")
# we may have outputs for groups withous VM's
if normalized_group_name in inventory:
inventory[normalized_group_name]['vars'] = outputs[group]['value']['meta']
return inventory
def get_tfstate():
# trying to get state from http backend
url = 'http://' + 'terraform-state-store.service.ks-1.consul' + '/v1/state/' + project
webUser = tf_state_path.split('/')[2]
webPass = 'any'
r = requests.get(url, auth=HTTPBasicAuth(webUser, webPass))
if r.status_code == 200:
# looks like we get it
return r.json()
# trying to get from consul if http backend failed
url = 'http://' + 'consul.service.infra1.consul' + ':8500/v1/kv/' + tf_state_path + '%3A' + project
r = requests.get(url)
return json.loads(base64.b64decode(r.json()[0]['Value']))
try:
tfstate = get_tfstate()
inventory = _processing(tfstate, _init_inventory())
sys.stdout.write(json.dumps(inventory, indent=2))
except Exception as e:
print(str(e), file=sys.stderr)
print(traceback.format_exc())
sys.exit(1)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment