Python Script to List Unused AWS Security Groups

Python Script to List Unused AWS Security Groups

Takahiro Iwasa
(岩佐 孝浩)
Takahiro Iwasa (岩佐 孝浩)
2 min read
EC2 Security Groups

I wrote a Python script to list all the unused security groups.

Create with the following code.

import argparse
import json

import boto3

ec2 = boto3.resource('ec2')

def list_security_groups() -> list:
    groups = ec2.security_groups.all()
    return list(groups)

def list_network_interfaces() -> list:
    interfaces = ec2.network_interfaces.all()
    return list(interfaces)

def extract_unused_security_groups() -> list:
    groups = list_security_groups()
    interfaces = list_network_interfaces()
    used_groups = [group for interface in interfaces for group in interface.groups]
    unused_groups = []

    for group in groups:
        if group.group_id in [used_group.get('GroupId') for used_group in used_groups]:
            # Used by network interfaces.

        if group.group_id in [pair.get('GroupId') for group in groups
                               for permission in group.ip_permissions
                               for pair in permission.get('UserIdGroupPairs', [])]:
            # Used by other security groups

    return unused_groups

def parse_args() -> argparse.Namespace:
    parser = argparse.ArgumentParser()
    parser.add_argument('--include-inbound', action=argparse.BooleanOptionalAction, default=True)
    parser.add_argument('--include-outbound', action=argparse.BooleanOptionalAction, default=True)
    parser.add_argument('--output', choices=['json', 'list'], default='json')
    return parser.parse_args()

def format_print(unused_groups: list, include_inbound: bool, include_outbound: bool, output: str):
    results = []

    for unused_group in unused_groups:
        result = {
            'group_id': unused_group.group_id,
            'group_name': unused_group.group_name,
        if include_inbound:
            result.update({'ip_permissions': unused_group.ip_permissions})
        if include_outbound:
            result.update({'ip_permissions_egress': unused_group.ip_permissions_egress})

    if output == 'json':
        print(json.dumps(results, indent=2))
    elif output == 'list':
        _ = [f"- {result.get('group_name')} ({result.get('group_id')})\n" for result in results]
        print(''.join(_), end='')

def main():
    unused_groups = extract_unused_security_groups()
    args = parse_args()
    format_print(unused_groups, args.include_inbound, args.include_outbound, args.output)

if __name__ == '__main__':

Run the script by specifying the following options. These are optional.

--include-inbound / --no-include-inboundIncludes or not inbound setting information.
--include-outbound / --no-include-outboundIncludes or not outbound setting information.
--outputSpecifies output format.
Takahiro Iwasa
(岩佐 孝浩)

Takahiro Iwasa (岩佐 孝浩)

Software Developer at iret, Inc.
Architecting and developing cloud native applications mainly with AWS. Japan AWS Top Engineers 2020-2023