-
-
Save TomRyan-321/0cf6e48937cbe9513afc50117d6ffd6f to your computer and use it in GitHub Desktop.
#!/usr/bin/env python | |
import boto3 | |
import argparse | |
def lookup_by_id(sgid): | |
sg = ec2.get_all_security_groups(group_ids=sgid) | |
return sg[0].name | |
# get a full list of the available regions | |
client = boto3.client('ec2') | |
regions_dict = client.describe_regions() | |
region_list = [region['RegionName'] for region in regions_dict['Regions']] | |
# parse arguments | |
parser = argparse.ArgumentParser(description="Show unused security groups") | |
parser.add_argument("-r", "--region", type=str, default="us-east-1", | |
help="The default region is us-east-1. The list of available regions are as follows: %s" % sorted( | |
region_list)) | |
parser.add_argument("-d", "--delete", help="delete security groups from AWS", action="store_true") | |
args = parser.parse_args() | |
client = boto3.client('ec2', region_name=args.region) | |
ec2 = boto3.resource('ec2', region_name=args.region) | |
all_groups = [] | |
security_groups_in_use = [] | |
# Get ALL security groups names | |
security_groups_dict = client.describe_security_groups() | |
security_groups = security_groups_dict['SecurityGroups'] | |
for groupobj in security_groups: | |
if groupobj['GroupName'] == 'default' or groupobj['GroupName'].startswith('d-') or groupobj['GroupName'].startswith('AWS-OpsWorks-'): | |
security_groups_in_use.append(groupobj['GroupId']) | |
all_groups.append(groupobj['GroupId']) | |
# Get all security groups used by instances | |
instances_dict = client.describe_instances() | |
reservations = instances_dict['Reservations'] | |
network_interface_count = 0 | |
for i in reservations: | |
for j in i['Instances']: | |
for k in j['SecurityGroups']: | |
if k['GroupId'] not in security_groups_in_use: | |
security_groups_in_use.append(k['GroupId']) | |
# Security Groups in use by Network Interfaces | |
eni_client = boto3.client('ec2', region_name=args.region) | |
eni_dict = eni_client.describe_network_interfaces() | |
for i in eni_dict['NetworkInterfaces']: | |
for j in i['Groups']: | |
if j['GroupId'] not in security_groups_in_use: | |
security_groups_in_use.append(j['GroupId']) | |
# Security groups used by classic ELBs | |
elb_client = boto3.client('elb', region_name=args.region) | |
elb_dict = elb_client.describe_load_balancers() | |
for i in elb_dict['LoadBalancerDescriptions']: | |
for j in i['SecurityGroups']: | |
if j not in security_groups_in_use: | |
security_groups_in_use.append(j) | |
# Security groups used by ALBs | |
elb2_client = boto3.client('elbv2', region_name=args.region) | |
elb2_dict = elb2_client.describe_load_balancers() | |
for i in elb2_dict['LoadBalancers']: | |
for j in i['SecurityGroups']: | |
if j not in security_groups_in_use: | |
security_groups_in_use.append(j) | |
# Security groups used by RDS | |
rds_client = boto3.client('rds', region_name=args.region) | |
rds_dict = rds_client.describe_db_instances() | |
for i in rds_dict['DBInstances']: | |
for j in i['VpcSecurityGroups']: | |
if j['VpcSecurityGroupId'] not in security_groups_in_use: | |
security_groups_in_use.append(j['VpcSecurityGroupId']) | |
delete_candidates = [] | |
for group in all_groups: | |
if group not in security_groups_in_use: | |
delete_candidates.append(group) | |
if args.delete: | |
print("We will now delete security groups identified to not be in use.") | |
for group in delete_candidates: | |
security_group = ec2.SecurityGroup(group) | |
try: | |
security_group.delete() | |
except Exception as e: | |
print(e) | |
print("{0} requires manual remediation.".format(security_group.group_name)) | |
else: | |
print("The list of security groups to be removed is below.") | |
print("Run this again with `-d` to remove them") | |
for group in sorted(delete_candidates): | |
print(" " + group) | |
print("---------------") | |
print("Activity Report") | |
print("---------------") | |
print(u"Total number of Security Groups evaluated: {0:d}".format(len(all_groups))) | |
print(u"Total number of EC2 Instances evaluated: {0:d}".format(len(reservations))) | |
print(u"Total number of Load Balancers evaluated: {0:d}".format(len(elb_dict['LoadBalancerDescriptions']) + | |
len(elb2_dict['LoadBalancers']))) | |
print(u"Total number of RDS Instances evaluated: {0:d}".format(len(rds_dict['DBInstances']))) | |
print(u"Total number of Network Interfaces evaluated: {0:d}".format(len(eni_dict['NetworkInterfaces']))) | |
print(u"Total number of Security Groups in-use evaluated: {0:d}".format(len(security_groups_in_use))) | |
if args.delete: | |
print(u"Total number of Unused Security Groups deleted: {0:d}".format(len(delete_candidates))) | |
else: | |
print(u"Total number of Unused Security Groups targeted for removal: {0:d}".format(len(delete_candidates))) | |
# For each security group in the total list, if not in the "used" list, flag for deletion | |
# If running with a "--delete" flag, delete the ones flagged. |
Fixed checks for Security groups that start with 'AWS-OpsWorks-' & 'd-' (directory service).
Changed Network Interface check to directly check all network interfaces rather than just instance attached interfaces.
Thanks a lot!
Great script...I did find one issue around line 67 and dealing with elbv2 and security groups.
We have a number of 'network' load balancers, and these do not have any Security Groups associated with them, and thus would fail on your for loop. I added the following and all is well.
# Security groups used by ALBs
elb2_client = boto3.client('elbv2', region_name=args.region)
elb2_dict = elb2_client.describe_load_balancers()
for i in elb2_dict['LoadBalancers']:
try:
for j in i['SecurityGroups']:
if j not in security_groups_in_use:
security_groups_in_use.append(j)
except KeyError:
pass
Nice script, works great! Thanks.
Thanks for the code!
Small improvement. Most AWS users may set AWS_DEFALUT_REGION in the environmental variables.
So,
import os
# .... snip ....
try:
default_region = os.environ["AWS_DEFAULT_REGION"]
except:
default_region = "us-east-1"
# parse arguments
parser = argparse.ArgumentParser(description="Show unused security groups")
parser.add_argument("-r", "--region", type=str, default=default_region,
help="The default region is us-east-1. The list of available regions are as follows: %s" % sorted(
region_list))
may be good.
network loadbalancer not have SecurityGroups..
# Security groups used by ALBs
elb2_client = boto3.client('elbv2', region_name=args.region)
elb2_dict = elb2_client.describe_load_balancers()
for i in elb2_dict['LoadBalancers']:
if i['Type']=='network':
continue
for j in i['SecurityGroups']:
if j not in security_groups_in_use:
security_groups_in_use.append(j)
Thanks for ce code very best, but security group lamdba check :
Security groups used by lambda
lambda_client = boto3.client('lambda', region_name=args.region)
lambda_functions = lambda_client.list_functions()
while True:
if "NextMarker" in lambda_functions:
nextMarker = lambda_functions["NextMarker"]
else:
nextMarker = ""
for function in lambda_functions["Functions"]:
functionName = function["FunctionName"]
print ("name"+functionName)
functionVpcConfig=""
functionSecurityGroupIds=""
try:
functionVpcConfig = function["VpcConfig"]
functionSecurityGroupIds = functionVpcConfig["SecurityGroupIds"]
for j in functionSecurityGroupIds:
if j not in security_groups_in_use:
security_groups_in_use.append(j)
except KeyError:
continue
finally:
print (functionSecurityGroupIds)
if nextMarker == "":
break
else:
lambda_functions = lambda_client.list_functions(
Marker= nextMarker
)
Hey thank you for this, I added a few more checks and options if you're interested:
https://gist.github.com/snixon/059b0a0edf87e9a34d020bb2c9546874
Great script...I did find one issue around line 67 and dealing with elbv2 and security groups. We have a number of 'network' load balancers, and these do not have any Security Groups associated with them, and thus would fail on your for loop. I added the following and all is well.
# Security groups used by ALBs elb2_client = boto3.client('elbv2', region_name=args.region) elb2_dict = elb2_client.describe_load_balancers() for i in elb2_dict['LoadBalancers']: try: for j in i['SecurityGroups']: if j not in security_groups_in_use: security_groups_in_use.append(j) except KeyError: pass
or we can do
for i in elb2_dict['LoadBalancers']:
if "SecurityGroups" in i:
for j in i["SecurityGroups"]:
if j not in security_groups_in_use:
security_groups_in_use.append(j)
else:
print(f"ALB -> {i['LoadBalancerName']} didn't use any security group")
Updated activity report to include total number of sec groups evaluated.