Skip to content

Instantly share code, notes, and snippets.

@amgill
Last active June 18, 2024 10:12
Show Gist options
  • Save amgill/353225152495d2dce52501fa70c2528a to your computer and use it in GitHub Desktop.
Save amgill/353225152495d2dce52501fa70c2528a to your computer and use it in GitHub Desktop.
Generate AWS Security Groups Rules Report of all the Security Groups, as Seen via AWS Web Console. Uses python 3 with boto3 to generate CSV.
#!/usr/local/bin/python3
######################################################################################################################
# Purpose: Generate rules report of all the security groups #
# Input Params: None [Make sure to set AWS CLI session by populating env. vars. with keys.] #
# Usage: ./ec2_sg_rules.py > account-date.csv [python ./ec2_sg_rules.py > gill-dev-sg-2018-04-11.csv] #
# Author: Abdul Gill #
# Doc. Ref: http://boto3.readthedocs.io/en/latest/reference/services/ec2.html#EC2.Client.describe_security_groups#
######################################################################################################################
from __future__ import print_function
import json
import boto3
#Explicitly declaring variables here grants them global scope
cidr_block = ""
ip_protpcol = ""
from_port = ""
to_port = ""
from_source = ""
print("%s,%s,%s,%s,%s,%s" % ("Group-Name","Group-ID","In/Out","Protocol","Port","Source/Destination"))
for region in ["us-east-1","us-west-1", "us-west-2"]:
ec2=boto3.client('ec2', region )
sgs = ec2.describe_security_groups()["SecurityGroups"]
for sg in sgs:
group_name = sg['GroupName']
group_id = sg['GroupId']
print("%s,%s" % (group_name,group_id))
# InBound permissions ##########################################
inbound = sg['IpPermissions']
print("%s,%s,%s" % ("","","Inbound"))
for rule in inbound:
if rule['IpProtocol'] == "-1":
traffic_type="All Trafic"
ip_protpcol="All"
to_port="All"
else:
ip_protpcol = rule['IpProtocol']
from_port=rule['FromPort']
to_port=rule['ToPort']
#If ICMP, report "N/A" for port #
if to_port == -1:
to_port = "N/A"
#Is source/target an IP v4?
if len(rule['IpRanges']) > 0:
for ip_range in rule['IpRanges']:
cidr_block = ip_range['CidrIp']
print("%s,%s,%s,%s,%s,%s" % ("", "", "", ip_protpcol, to_port, cidr_block))
#Is source/target an IP v6?
if len(rule['Ipv6Ranges']) > 0:
for ip_range in rule['Ipv6Ranges']:
cidr_block = ip_range['CidrIpv6']
print("%s,%s,%s,%s,%s,%s" % ("", "", "", ip_protpcol, to_port, cidr_block))
#Is source/target a security group?
if len(rule['UserIdGroupPairs']) > 0:
for source in rule['UserIdGroupPairs']:
from_source = source['GroupId']
print("%s,%s,%s,%s,%s,%s" % ("", "", "", ip_protpcol, to_port, from_source))
# OutBound permissions ##########################################
outbound = sg['IpPermissionsEgress']
print("%s,%s,%s" % ("","","Outbound"))
for rule in outbound:
if rule['IpProtocol'] == "-1":
traffic_type="All Trafic"
ip_protpcol="All"
to_port="All"
else:
ip_protpcol = rule['IpProtocol']
from_port=rule['FromPort']
to_port=rule['ToPort']
#If ICMP, report "N/A" for port #
if to_port == -1:
to_port = "N/A"
#Is source/target an IP v4?
if len(rule['IpRanges']) > 0:
for ip_range in rule['IpRanges']:
cidr_block = ip_range['CidrIp']
print("%s,%s,%s,%s,%s,%s" % ("", "", "", ip_protpcol, to_port, cidr_block))
#Is source/target an IP v6?
if len(rule['Ipv6Ranges']) > 0:
for ip_range in rule['Ipv6Ranges']:
cidr_block = ip_range['CidrIpv6']
print("%s,%s,%s,%s,%s,%s" % ("", "", "", ip_protpcol, to_port, cidr_block))
#Is source/target a security group?
if len(rule['UserIdGroupPairs']) > 0:
for source in rule['UserIdGroupPairs']:
from_source = source['GroupId']
print("%s,%s,%s,%s,%s,%s" % ("", "", "", ip_protpcol, to_port, from_source))
We can make this file beautiful and searchable if this error is corrected: It looks like row 2 should actually have 6 columns, instead of 2 in line 1.
Group-Name,Group-ID,In/Out,Protocol,Port,Source/Destination
default,sg-0990507b
,,Inbound
,,,All,All,sg-0990507b
,,Outbound
,,,All,All,0.0.0.0/0
default,sg-156c2e62
,,Inbound
,,,All,All,0.0.0.0/0
,,,All,All,sg-156c2e62
,,,tcp,22,0.0.0.0/0
,,Outbound
,,,All,All,0.0.0.0/0
transit-spoke-app-sg,sg-ebb8fa9c
,,Inbound
,,,tcp,80,175.29.255.240/28
,,,tcp,80,15.0.0.0/8
,,,icmp,N/A,15.0.0.0/8
,,,tcp,22,175.29.255.240/28
,,,tcp,22,15.0.0.0/8
,,,udp,3389,175.29.255.240/28
,,,udp,3389,15.0.0.0/8
,,,tcp,3389,175.29.255.240/28
,,,tcp,3389,15.0.0.0/8
,,Outbound
,,,tcp,80,0.0.0.0/0
,,,tcp,80,::/0
,,,All,All,0.0.0.0/0
,,,All,All,::/0
default,sg-b291bfd4
,,Inbound
,,,All,All,sg-b291bfd4
,,Outbound
,,,All,All,0.0.0.0/0
default,sg-b82d6dc5
,,Inbound
,,,All,All,sg-b82d6dc5
,,Outbound
,,,All,All,0.0.0.0/0
@watcher-60
Copy link

slight issue where on the All traffic I was incorrectly getting port info egon the sections where it catches any port (need the same for outbound)
for rule in inbound: if rule['IpProtocol'] == "-1": traffic_type="All Trafic" ip_protpcol="All" to_port="All"

Updated to :
for rule in inbound: if rule['IpProtocol'] == "-1": traffic_type="All Trafic" ip_protpcol="All" to_port="All" from_port="All" port_range="All"

Probably doesnt need both the additions

@burebabuna
Copy link

burebabuna commented Aug 12, 2021

@amgill Thanks for sharing! I suggest to change to_port in print function with to_port if (to_port == "All" or from_port == to_port) else ''+str(from_port)+'-'+str(to_port) as shown in the snippet below to include security groups with different from_port and to_port values.

print(%s,%s,%s,%s,%s,%s" % ("", "", "", ip_protpcol, to_port if (to_port == "All" or from_port == to_port) else ''+str(from_port)+'-'+str(to_port), cidr_block))

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment