in python/AMI_OUTDATED_CHECK/AMI_OUTDATED_CHECK.py [0:0]
def evaluate_compliance(event, configuration_item, valid_rule_parameters):
"""Form the evaluation(s) to be return to Config Rules
Return either:
None -- when no result needs to be displayed
a string -- either COMPLIANT, NON_COMPLIANT or NOT_APPLICABLE
a dictionary -- the evaluation dictionary, usually built by build_evaluation_from_config_item()
a list of dictionary -- a list of evaluation dictionary , usually built by build_evaluation()
Keyword arguments:
event -- the event variable given in the lambda handler
configuration_item -- the configurationItem dictionary in the invokingEvent
valid_rule_parameters -- the output of the evaluate_parameters() representing validated
parameters of the Config Rule
Advanced Notes:
1 -- if a resource is deleted and generate a configuration change with ResourceDeleted status,
the Boilerplate code will put a NOT_APPLICABLE on this resource automatically.
2 -- if a None or a list of dictionary is returned, the old evaluation(s) which are not
returned in the new evaluation list are returned as NOT_APPLICABLE by the Boilerplate code
3 -- if None or an empty string, list or dict is returned, the Boilerplate code will put a
"shadow" evaluation to feedback that the evaluation took place properly
"""
ec2_client = get_client('ec2', event)
evaluations = []
if configuration_item:
ami_result = ec2_client.describe_images(
ImageIds=[configuration_item['configuration']['imageId']]
)
print(ami_result)
if ami_result['Images']:
#print("AMI result:")
#print(ami_result)
status, annotation = evaluate_image(
ami_result['Images'][0],
configuration_item['configuration']['instanceId'],
valid_rule_parameters
)
evaluations.append(
build_evaluation_from_config_item(
configuration_item,
status,
annotation=annotation
)
)
else:
#Scenario 1 : No Private AMI for the Instance
evaluations.append(
build_evaluation_from_config_item(
configuration_item,
"NOT_APPLICABLE",
""
)
)
else:
# First get all of the instances, paging through them if we have to.
image_id_array = []
instance_array = []
instance_results = ec2_client.describe_instances()
while True:
for res in instance_results['Reservations']:
for instance in res['Instances']:
image_id_array.append(instance['ImageId'])
instance_array.append(instance)
if 'NextToken' in instance_results:
next_token = instance_results['NextToken']
instance_results = ec2_client.describe_instances(NextToken=next_token)
else:
break
print(image_id_array)
print(instance_array)
# Use set() to get a list of just the unique image ID's.
unique_image_ids = set(image_id_array)
print(unique_image_ids)
# Make as few API calls as possible to get the AMI data. A simpler loop in
# which we calling the EC2 API for every instance would be easier, but would
# result in a _lot_ more API activity and could cause throttling.
# Create a lookup dict so that we can evaluate compliance for each instance.
image_lookup = {}
image_results = ec2_client.describe_images(
ImageIds=list(unique_image_ids)
)
while True:
for image in image_results['Images']:
image_lookup[image['ImageId']] = image
if 'NextToken' in image_results:
next_token = image_results['NextToken']
image_results = ec2_client.describe_images(
ImageIds=list(unique_image_ids),
NextToken=next_token
)
else:
break
print(image_lookup)
# Now loop through the instances again and determine the compliance status,
# appending it to our evaluations list.
for instance in instance_array:
if instance['ImageId'] in image_lookup:
status, annotation = evaluate_image(
image_lookup[instance['ImageId']],
instance['InstanceId'],
valid_rule_parameters
)
evaluations.append(
build_evaluation(
instance['InstanceId'],
status,
event,
"AWS::EC2::Instance",
annotation
)
)
else:
#Scenario 1 : No Private AMIs in the account then no resources in scope
evaluations.append(
build_evaluation(
instance['InstanceId'],
'NOT_APPLICABLE',
event,
"AWS::EC2::Instance"
)
)
return evaluations