Finding Missing AMIs with AWS Lambda
Problem Statement
This article will give you code and an IAM policy that will allow you to audit all of your Auto Scaling groups to find their launch configurations and it will verify that the underlying AMI is still available. If an AMI is not available, it will notify a SNS topic that will contact the people on your team who can fix the problem. A missing AMI is sometimes the fault of someone on the account, sometimes it’s not.
How could an AMI disappear?
There are a few ways:
- Whoever created and shared it, made it private or deleted it.
- You built your own AMI and accidentally deleted it.
- You are using an AWS AMI that was pulled for one reason or another
It happens to the best of us.
AWS Service Descriptions
AWS Lambda
AWS Lambda allows you to provide Java, Node, or Python code, and just call it. The largest benefit here is that you don’t have to run any servers. At work, we try to put things into Lambda as frequently as possible so that we don’t have to manage any additional servers. In this example, you could schedule the function to be run on a daily or weekly basis to audit your environments and report out if there are failures.
AWS SNS
We will use AWS SNS to setup a list of people to notify when an AMI goes missing. When you create your SNS policy, save the Amazon Resource Number (ARN). You will use it in the IAM policy you’ll create below as well as in the configuration of your function. Any users that are subscribed to your SNS topic will need to accept the invitation.
The Lambda setup
IAM Policy
Here’s an example IAM policy you can use to attach to your Lambda executor role. Take the ARN that you received from setting up your SNS topic and substitute it in below:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"autoscaling:DescribeAutoScalingGroups",
"autoscaling:DescribeLaunchConfigurations"
],
"Resource": [
"*"
]
},
{
"Effect": "Allow",
"Action": [
"ec2:DescribeImages",
"ec2:DescribeRegions"
],
"Resource": [
"*"
]
},
{
"Effect": "Allow",
"Action": [
"sns:Publish"
],
"Resource": [
"<< YOUR SNS ARN >>"
]
},
{
"Effect": "Allow",
"Action": [
"sns:ListTopics"
],
"Resource": [
"*"
]
}
]
}
Lambda function
At a high level, this function:
- Gets a list of all EC2 regions
- Goes through the regions and pulls the Auto Scaling groups
- Investigates the Launch Configuration for each AutoScaling group
- Verifies the AMI used in the Launch Configuration is available
- If there are AMIs that are not available, notify a SNS Topic.
Follow the guide for setting up a Python 2.7 function and use the code below.
# -*- coding: utf-8 -*-
import boto3
# SNS Config
sns_topic_arn = "<< YOUR SNS ARN >>"
sns_subject = "ASGs Missing AMIs"
def lambda_handler(event, context):
failures = collect_launch_configs(get_regions())
if bool(failures):
send_sns_message(failures)
def get_regions():
regions = {}
for region in boto3.client('ec2').describe_regions()['Regions']:
regions[region['RegionName']] = []
return regions
def message(content):
return "This is a report of regions that have Auto Scaling groups "\
"deployed that include a launch configuration that points to an "\
"AMI that is not available:\n\n%s" % (content)
def send_sns_message(failures):
client = boto3.client('sns')
print client.list_topics()
client.publish(
TopicArn=sns_topic_arn,
Message=message(failures),
Subject=sns_subject
)
def collect_launch_configs(regions):
launch_configs = regions
failures = {}
for region in regions:
asg_client = boto3.client('autoscaling', region_name=region)
ec2_client = boto3.resource('ec2', region_name=region)
asgs = asg_client.describe_auto_scaling_groups()['AutoScalingGroups']
for asg in asgs:
launch_configs[region].append(asg['LaunchConfigurationName'])
lcs = asg_client.describe_launch_configurations(
LaunchConfigurationNames=launch_configs[region])
for lc in lcs['LaunchConfigurations']:
image = ec2_client.Image(lc['ImageId'])
try:
image.state
except AttributeError:
if region in failures:
failures[region].append({"LaunchConfigurationName":
lc['LaunchConfigurationName'],
"ImageId": lc['ImageId']})
else:
failures[region] = [{"LaunchConfigurationName":
lc['LaunchConfigurationName'],
"ImageId": lc['ImageId']}]
print "ERROR: Image %s is not available in %s." % \
(lc['ImageId'], region)
return failures
The result
Assuming you get everything setup properly, you will get an email anytime there is a situation where a launch configuration is missing it’s underlying AMI. Here’s an example:
From: AWS Notifications <no-reply@sns.amazonaws.com> 7:53 PM
To: Me
This is a report of regions that have Auto Scaling groups deployed that include
a launch configuration that points to an AMI that is not available:
{'us-east-1': [{'ImageId': 'ami-98aabbf2', 'LaunchConfigurationName': 'brint-demo-nginx'}]}
--
If you wish to stop receiving notifications from this topic, please click or
visit the link below to unsubscribe: ...
Please do not reply directly to this email. If you have any questions or
comments regarding this email, please contact us at
https://aws.amazon.com/support