group inventory by instance tags as well
[awsible] / inventory / asg-inventory.py
index 1eba92e66e0b93813d267964bd5362dec2694193..d7141b6b3404d11a8ab0774d25446bda0792ec15 100755 (executable)
@@ -1,21 +1,34 @@
 #!/usr/bin/env python
 '''\
 Generate a JSON object containing the names of all the AWS Autoscaling
-Groups in an account and the IPs of the Instances within them, suitable
-for use as an Ansible inventory.
+Groups in an account, the IDs of the Instances within them, as well
+as all the Tags on those Instances, each containing a list of the IPs
+of the Instances, suitable for use as an Ansible inventory.
+
+This output is similar to the default ec2.py inventory script, but is
+far more limited in the scope of information is has to retreive, parse,
+and render.
+
+It does not currently deal with hostvars at all.
 '''
 
 import argparse
 import boto3
 import json
 import sys
+import os
 from multiprocessing.dummy import Pool as ThreadPool
 from functools import partial
 
 
-DEFAULT_REGIONS = ['us-east-1', 'us-west-2']
+#DEFAULT_REGIONS = ['us-east-1', 'us-west-2']
+DEFAULT_REGIONS = ['us-east-2']
 HOSTVARS = {}
 
+try:
+    PUBLIC = len(os.environ['INVENTORY_PUBLIC']) > 0
+except:
+    PUBLIC = False
 
 def allASGInstances(asgc):
     'Return a tuple of a dict of each ASG name listing the instance IDs within it, and a list of all instance IDs.'
@@ -52,6 +65,23 @@ def allInstanceIPs(ec2c, InstanceIds=None, publicIPs=False):
     return instances
 
 
+def tagsOfInstances(ec2c, InstanceIds=None):
+    tags = {}
+    args = {'Filters': [{'Name': 'resource-type', 'Values': ["instance"]}]}
+    if InstanceIds:
+        args['Filters'] += [{'Name': 'resource-id', 'Values': InstanceIds}]
+    while True:
+        response = ec2c.describe_tags(**args)
+        for tag in response['Tags']:
+            tagname = "tag_{}_{}".format(tag['Key'], tag['Value'])
+            tagvalue = tag['ResourceId']
+            tags.setdefault(tagname, []).append(tagvalue)
+        if 'NextToken' not in response:
+            break
+        args['NextToken'] = response['NextToken']
+    return tags
+
+
 def regionInventory(sessionArgs, publicIPs=False):
     'Return dict results for one region.'
     session = boto3.session.Session(**sessionArgs)
@@ -70,6 +100,10 @@ def regionInventory(sessionArgs, publicIPs=False):
     # add ASG dict, replacing ASG Instance Id with instance IP
     inventory.update({asg:[AllInstanceIPs[iid] for iid in ASGs[asg] if iid in AllInstanceIPs] for asg in ASGs})
 
+    # group up instance tags as well
+    tags = tagsOfInstances(ec2c, AllInstanceIds)
+    inventory.update({tag:[AllInstanceIPs[iid] for iid in tags[tag] if iid in AllInstanceIPs] for tag in tags})
+
     return inventory
 
 
@@ -84,7 +118,7 @@ def mergeDictOfLists(a, b):
 
 
 parser = argparse.ArgumentParser(description='dynamic Ansible inventory from AWS Autoscaling Groups')
-parser.add_argument('--public', action='store_true', help='inventory public IPs (default: private IPs)')
+parser.add_argument('--public', action='store_true' if not PUBLIC else 'store_false', help='inventory public IPs (default: private IPs)')
 parser.add_argument('--profile', metavar='PROFILE', dest='profile_name', help='AWS Profile (default: current IAM Role)')
 parser.add_argument('--regions', metavar='REGION', nargs='+', default=DEFAULT_REGIONS, help='AWS Regions (default: %(default)s)')
 parser.add_argument('--list', action='store_true')