3 Generate a JSON object containing the names of all the AWS Autoscaling
4 Groups in an account and the IPs of the Instances within them, suitable
5 for use as an Ansible inventory.
12 from multiprocessing
.dummy
import Pool
as ThreadPool
13 from functools
import partial
16 DEFAULT_REGIONS
= ['us-east-1', 'us-west-2']
20 def allASGInstances(asgc
):
21 'Return a tuple of a dict of each ASG name listing the instance IDs within it, and a list of all instance IDs.'
26 response
= asgc
.describe_auto_scaling_groups(**args
)
27 for asg
in response
['AutoScalingGroups']:
28 asgs
[asg
['AutoScalingGroupName']] = [i
['InstanceId'] for i
in asg
['Instances']]
29 instanceIds
+= asgs
[asg
['AutoScalingGroupName']]
30 if 'NextToken' not in response
:
32 args
= {'NextToken': response['NextToken']}
33 return (asgs
, instanceIds
)
36 def allInstanceIPs(ec2c
, InstanceIds
=None, publicIPs
=False):
37 'Return a dict of each Instance ID with its addresses.'
40 IPType
= 'PublicIpAddress' if publicIPs
else 'PrivateIpAddress'
41 if InstanceIds
is not None:
42 args
['InstanceIds'] = InstanceIds
44 response
= ec2c
.describe_instances(**args
)
45 for resv
in response
['Reservations']:
46 for inst
in resv
['Instances']:
48 instances
[inst
['InstanceId']] = inst
[IPType
]
49 if 'NextToken' not in response
:
51 args
= {'NextToken': response['NextToken']}
55 def regionInventory(sessionArgs
, publicIPs
=False):
56 'Return dict results for one region.'
57 session
= boto3
.session
.Session(**sessionArgs
)
58 asgc
= session
.client('autoscaling')
59 ec2c
= session
.client('ec2')
61 # get dict of ASG names and associated Instance Ids, plus list of all Instance Ids referenced by ASGs
62 (ASGs
, AllInstanceIds
) = allASGInstances(asgc
)
64 # get list of instance IPs for all instance Ids used by ASGs
65 AllInstanceIPs
= allInstanceIPs(ec2c
, InstanceIds
=AllInstanceIds
, publicIPs
=publicIPs
)
67 # a group for every Instance Id
68 inventory
= {iid:[AllInstanceIPs[iid]] for iid in AllInstanceIPs}
70 # add ASG dict, replacing ASG Instance Id with instance IP
71 inventory
.update({asg:[AllInstanceIPs[iid] for iid in ASGs[asg] if iid in AllInstanceIPs] for asg in ASGs}
)
76 def mergeDictOfLists(a
, b
):
77 'There is likely a better way of doing this, but right now I have a headache.'
86 parser
= argparse
.ArgumentParser(description
='dynamic Ansible inventory from AWS Autoscaling Groups')
87 parser
.add_argument('--public', action
='store_true', help='inventory public IPs (default: private IPs)')
88 parser
.add_argument('--profile', metavar
='PROFILE', dest
='profile_name', help='AWS Profile (default: current IAM Role)')
89 parser
.add_argument('--regions', metavar
='REGION', nargs
='+', default
=DEFAULT_REGIONS
, help='AWS Regions (default: %(default)s)')
90 parser
.add_argument('--list', action
='store_true')
91 parser
.add_argument('--host', nargs
=1)
92 args
= parser
.parse_args()
95 print(json
.dumps(HOSTVARS
))
98 # create sessionArgs for each region
99 regionArgs
= [{'region_name': region}
for region
in args
.regions
]
100 if args
.profile_name
:
101 for arg
in regionArgs
:
102 arg
.update({'profile_name': args.profile_name}
)
104 # pin the non-variant option
105 invf
= partial(regionInventory
, publicIPs
=args
.public
)
107 # query regions concurrently
108 pool
= ThreadPool(len(regionArgs
))
109 regionInventories
= pool
.map(invf
, regionArgs
)
114 inventory
= reduce(mergeDictOfLists
, regionInventories
, {})
115 inventory
['_meta'] = {'hostvars': HOSTVARS}
117 print(json
.dumps(inventory
))