List Deleteable Security Groups
I inherited an AWS VPC that grew organically. This meant about 150 security
groups with no clear way to know which ones are important. Until I learned about
the describe_network_interfaces
function, creating a report about security
group usage seemed like too much work.
Even with knowledge of that function, this bit of python was more work than I thought. Therefore, I’m sharing it.
import boto3
security_groups = find_deleteable_security_groups(boto3.client('config'), boto3.client('cloudformation'))
for security_group in security_groups:
print(security_group)
def find_deleteable_security_groups(ec2Client, cfmClient):
# Will hold security group information.
security_groups = {}
# Will hold network interface information.
network_interfaces = {}
# Will hold backward references of security group. For example, securiy group A
# might have no assets but still be referenced by security group B.
backward_references = {}
for security_group in ec2Client.describe_security_groups()['SecurityGroups']:
sg_id = security_group['GroupId']
network_interfaces[sg_id] = ec2Client.describe_network_interfaces(Filters=[{'Name': 'group-id', 'Values': [ sg_id ] }])['NetworkInterfaces']
security_group['referenced_groups'] = {}
for permission in security_group['IpPermissions']:
userid_group_pairs = permission['UserIdGroupPairs']
for group in userid_group_pairs:
group_id = group['GroupId']
security_group['referenced_groups'][group_id] = 1
if group_id not in backward_references:
backward_references[group_id] = {}
backward_references[group_id][sg_id] = 1
security_groups[sg_id] = security_group
results = {}
for sg_id in sorted(security_groups):
network_interface = network_interfaces[sg_id]
interface_count = len(network_interface)
if security_groups[sg_id]['GroupName'] == 'default':
results[sg_id] = { 'Count' : interface_count, 'Message' : 'Default' }
else:
printed = False
sg = security_groups[sg_id]
referenced_group_count = len(sg['referenced_groups'])
backward_reference_count = 0
if sg_id in backward_references:
backward_reference_count = len(backward_references[sg_id])
if interface_count > 0:
results[sg_id] = { 'Count' : interface_count }
printed = True
else:
response = ec2Client.describe_tags(Filters=[ { 'Name': 'resource-id', 'Values': [ sg_id ] }, { 'Name': 'key', 'Values': [ 'aws:cloudformation:stack-id' ] } ])
if 'Tags' in response:
if len(response['Tags']) > 0:
components = response['Tags'][0]['Value'].split('/')
stack_name = components[1]
stack_id = cfmClient.Stack(stack_name).stack_id
results[sg_id] = { 'Count' : interface_count, 'StackName' : stack_name, 'StackId' : stack_id }
printed = True
if printed == False and referenced_group_count > 0:
results[sg_id] = { 'Count' : interface_count, 'References' : sg['referenced_groups'] }
printed = True
if printed == False and backward_reference_count > 0:
results[sg_id] = { 'Count' : interface_count, 'ReferencedBy' : backward_references[sg_id] }
printed = True
if printed == False:
results[sg_id] = { 'Count' : interface_count, 'Message' : 'Deleteable' }
return results