Skip to main content
The AWS Security Group Auditor requires specific IAM permissions to scan your AWS account. You can configure different permission levels depending on whether you want read-only auditing or the ability to delete unused security groups.

Permission levels

Read-only mode

Recommended for most use casesAllows the tool to audit and report on security group usage without making any changes to your account.

Delete mode

For automated cleanupIncludes read permissions plus the ability to delete unused security groups when you confirm.

Required permissions by service

The tool makes boto3 API calls to 22 AWS services. Below are all the permissions extracted from the actual source code.

Identity and account

sts:GetCallerIdentity
required
Used to retrieve your AWS account ID for log file naming.
# Source: check_sg_usage.py:33-34
sts = boto3.client('sts')
account_id = sts.get_caller_identity()["Account"]

EC2 and networking

ec2:DescribeSecurityGroups
required
Lists all security groups in your account - the foundation of the audit.
# Source: check_sg_usage.py:49
security_groups = ec2.describe_security_groups()['SecurityGroups']
ec2:DescribeInstances
required
Identifies EC2 instances using each security group and checks Elastic Beanstalk instances.
# Source: check_sg_usage.py:70-74, 216
instances = ec2.describe_instances(
    Filters=[{'Name': 'instance.group-id', 'Values': [sg_id]}]
)
ec2:DescribeVpcEndpoints
required
Checks VPC endpoints and Transfer Family server endpoints for security groups.
# Source: check_sg_usage.py:243, 313
vpc_endpoints = ec2.describe_vpc_endpoints()['VpcEndpoints']
ec2:DescribeVpnConnections
required
Scans Site-to-Site VPN connections.
# Source: check_sg_usage.py:276
vpn_connections = ec2.describe_vpn_connections()
ec2:DescribeCustomerGateways
required
Checks customer gateways associated with VPN connections.
# Source: check_sg_usage.py:282
customer_gateway = ec2.describe_customer_gateways()
ec2:DeleteSecurityGroup
optional
Only required for delete mode. Allows deletion of unused security groups.
# Source: check_sg_usage.py:356
ec2.delete_security_group(GroupId=sg_id)
elasticloadbalancing:DescribeLoadBalancers
required
Lists Classic Load Balancers (ELB) and their security groups.
# Source: check_sg_usage.py:81
elbs = elb.describe_load_balancers()['LoadBalancerDescriptions']
elasticloadbalancing:DescribeLoadBalancers
required
Lists Application and Network Load Balancers (ALB/NLB).
# Source: check_sg_usage.py:88
elbv2_load_balancers = elbv2.describe_load_balancers()

Database services

rds:DescribeDBInstances
required
Checks RDS database instances.
# Source: check_sg_usage.py:95
dbs = rds.describe_db_instances()['DBInstances']
neptune:DescribeDBInstances
required
Checks Neptune graph database instances.
# Source: check_sg_usage.py:191
neptune_instances = neptune.describe_db_instances()
docdb:DescribeDBClusters
required
Checks DocumentDB clusters.
# Source: check_sg_usage.py:199
docdb_clusters = docdb.describe_db_clusters()
redshift:DescribeClusters
required
Checks Redshift data warehouse clusters.
# Source: check_sg_usage.py:166
redshift_clusters = redshift.describe_clusters()

Container and compute services

ecs:ListClusters
required
Lists ECS clusters to scan for services.
# Source: check_sg_usage.py:107
clusters = ecs.list_clusters()['clusterArns']
ecs:ListServices
required
Lists services within each ECS cluster.
# Source: check_sg_usage.py:112-114
service_list_response = ecs.list_services(cluster=cluster_arn)
ecs:DescribeServices
required
Gets network configuration details for ECS services (handles 10 services per call).
# Source: check_sg_usage.py:124-127
detailed_services_response = ecs.describe_services(
    cluster=cluster_arn, services=service_chunk
)
eks:ListClusters
required
Lists EKS clusters.
# Source: check_sg_usage.py:143
eks_clusters = eks.list_clusters()['clusters']
eks:ListNodegroups
required
Lists node groups within each EKS cluster.
# Source: check_sg_usage.py:145
nodegroups = eks.list_nodegroups(clusterName=cluster_name)
eks:DescribeNodegroup
required
Gets security group details for EKS node groups.
# Source: check_sg_usage.py:147
nodegroup = eks.describe_nodegroup(
    clusterName=cluster_name, nodegroupName=nodegroup_name
)
elasticbeanstalk:DescribeEnvironments
required
Lists Elastic Beanstalk environments.
# Source: check_sg_usage.py:208
eb_environments = elasticbeanstalk.describe_environments()
elasticbeanstalk:DescribeEnvironmentResources
required
Gets EC2 instances managed by Elastic Beanstalk.
# Source: check_sg_usage.py:213
resources = elasticbeanstalk.describe_environment_resources(
    EnvironmentId=env['EnvironmentId']
)
codebuild:ListProjects
required
Lists CodeBuild projects.
# Source: check_sg_usage.py:155
codebuild_projects = codebuild.list_projects()['projects']
codebuild:BatchGetProjects
required
Gets VPC configuration for CodeBuild projects.
# Source: check_sg_usage.py:157
project = codebuild.batch_get_projects(names=[project_name])

Caching and messaging

elasticache:DescribeCacheClusters
required
Checks ElastiCache clusters (Redis and Memcached).
# Source: check_sg_usage.py:175
cache_clusters = elasticache.describe_cache_clusters(
    ShowCacheNodeInfo=True
)
kafka:ListClusters
required
Lists Amazon MSK (Kafka) clusters.
# Source: check_sg_usage.py:183
kafka_clusters = kafka.list_clusters()['ClusterInfoList']
mq:ListBrokers
required
Lists Amazon MQ message brokers.
# Source: check_sg_usage.py:288
mq_brokers = mq.list_brokers()['BrokerSummaries']
mq:DescribeBroker
required
Gets security group details for MQ brokers.
# Source: check_sg_usage.py:290
broker = mq.describe_broker(BrokerId=broker_summary['BrokerId'])

Analytics and machine learning

sagemaker:ListEndpoints
required
Lists SageMaker model endpoints.
# Source: check_sg_usage.py:224
sm_endpoints = sagemaker.list_endpoints()['Endpoints']
sagemaker:DescribeEndpoint
required
Gets VPC configuration for SageMaker endpoints.
# Source: check_sg_usage.py:226
endpoint_desc = sagemaker.describe_endpoint(
    EndpointName=endpoint['EndpointName']
)
glue:GetJobs
required
Lists AWS Glue ETL jobs.
# Source: check_sg_usage.py:252
glue_jobs = glue.get_jobs()['Jobs']
glue:GetConnection
required
Gets connection details for Glue jobs.
# Source: check_sg_usage.py:257
connection_info = glue.get_connection(Name=connection_name)
es:ListDomainNames
required
Lists Elasticsearch/OpenSearch domains.
# Source: check_sg_usage.py:265
es_domains = es.list_domain_names()['DomainNames']
es:DescribeElasticsearchDomain
required
Gets VPC configuration for Elasticsearch domains.
# Source: check_sg_usage.py:268
domain_config = es.describe_elasticsearch_domain(
    DomainName=domain_name
)

Storage and file transfer

fsx:DescribeFileSystems
required
Lists Amazon FSx file systems.
# Source: check_sg_usage.py:298
fsx_file_systems = fsx.describe_file_systems()
transfer:ListServers
required
Lists AWS Transfer Family servers.
# Source: check_sg_usage.py:237
transfer_servers = transfer.list_servers()['Servers']
transfer:DescribeServer
required
Gets VPC endpoint configuration for Transfer servers.
# Source: check_sg_usage.py:240
server_details = transfer.describe_server(ServerId=server_id)
workspaces:DescribeWorkspaceDirectories
required
Lists Amazon WorkSpaces directories.
# Source: check_sg_usage.py:306
workspaces_directories = workspaces.describe_workspace_directories()

IAM policy examples

This policy provides all permissions needed to audit security groups without the ability to make changes.
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "SecurityGroupAuditorReadOnly",
      "Effect": "Allow",
      "Action": [
        "ec2:DescribeSecurityGroups",
        "ec2:DescribeInstances",
        "ec2:DescribeVpcEndpoints",
        "ec2:DescribeVpnConnections",
        "ec2:DescribeCustomerGateways",
        "elasticloadbalancing:DescribeLoadBalancers",
        "rds:DescribeDBInstances",
        "ecs:ListClusters",
        "ecs:ListServices",
        "ecs:DescribeServices",
        "eks:ListClusters",
        "eks:ListNodegroups",
        "eks:DescribeNodegroup",
        "codebuild:ListProjects",
        "codebuild:BatchGetProjects",
        "redshift:DescribeClusters",
        "elasticache:DescribeCacheClusters",
        "kafka:ListClusters",
        "neptune:DescribeDBInstances",
        "docdb:DescribeDBClusters",
        "elasticbeanstalk:DescribeEnvironments",
        "elasticbeanstalk:DescribeEnvironmentResources",
        "sagemaker:ListEndpoints",
        "sagemaker:DescribeEndpoint",
        "transfer:ListServers",
        "transfer:DescribeServer",
        "glue:GetJobs",
        "glue:GetConnection",
        "es:ListDomainNames",
        "es:DescribeElasticsearchDomain",
        "mq:ListBrokers",
        "mq:DescribeBroker",
        "fsx:DescribeFileSystems",
        "workspaces:DescribeWorkspaceDirectories",
        "sts:GetCallerIdentity"
      ],
      "Resource": "*"
    }
  ]
}

Full access policy (with deletion)

This policy includes ec2:DeleteSecurityGroup which allows the tool to permanently delete security groups. Use with caution and only in environments where automated cleanup is required.
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "SecurityGroupAuditorReadOnly",
      "Effect": "Allow",
      "Action": [
        "ec2:DescribeSecurityGroups",
        "ec2:DescribeInstances",
        "ec2:DescribeVpcEndpoints",
        "ec2:DescribeVpnConnections",
        "ec2:DescribeCustomerGateways",
        "elasticloadbalancing:DescribeLoadBalancers",
        "rds:DescribeDBInstances",
        "ecs:ListClusters",
        "ecs:ListServices",
        "ecs:DescribeServices",
        "eks:ListClusters",
        "eks:ListNodegroups",
        "eks:DescribeNodegroup",
        "codebuild:ListProjects",
        "codebuild:BatchGetProjects",
        "redshift:DescribeClusters",
        "elasticache:DescribeCacheClusters",
        "kafka:ListClusters",
        "neptune:DescribeDBInstances",
        "docdb:DescribeDBClusters",
        "elasticbeanstalk:DescribeEnvironments",
        "elasticbeanstalk:DescribeEnvironmentResources",
        "sagemaker:ListEndpoints",
        "sagemaker:DescribeEndpoint",
        "transfer:ListServers",
        "transfer:DescribeServer",
        "glue:GetJobs",
        "glue:GetConnection",
        "es:ListDomainNames",
        "es:DescribeElasticsearchDomain",
        "mq:ListBrokers",
        "mq:DescribeBroker",
        "fsx:DescribeFileSystems",
        "workspaces:DescribeWorkspaceDirectories",
        "sts:GetCallerIdentity"
      ],
      "Resource": "*"
    },
    {
      "Sid": "SecurityGroupDeletion",
      "Effect": "Allow",
      "Action": [
        "ec2:DeleteSecurityGroup"
      ],
      "Resource": "*"
    }
  ]
}

Setting up permissions

Option 1: Create an IAM user

  1. Create a new IAM user in the AWS Console
  2. Attach the read-only policy above
  3. Generate access keys for the user
  4. Configure AWS CLI with the credentials:
aws configure
# Enter Access Key ID, Secret Access Key, and region
If you’re running the tool from an EC2 instance:
  1. Create an IAM role with the read-only policy
  2. Attach the role to your EC2 instance
  3. The tool will automatically use the instance role credentials

Option 3: Use AWS SSO

aws sso login --profile your-profile
export AWS_PROFILE=your-profile
python check_sg_usage.py

Permission scoping

All permissions require Resource: "*" because the tool needs to scan all resources across your account. AWS does not support resource-level permissions for most describe/list operations.
If you need to restrict the tool to specific regions, use a condition:
{
  "Condition": {
    "StringEquals": {
      "aws:RequestedRegion": ["us-east-1", "us-west-2"]
    }
  }
}

Troubleshooting permission issues

If you encounter permission errors:
  1. Check CloudTrail logs to see which specific API call failed
  2. Test individual permissions using AWS CLI:
    aws ec2 describe-security-groups
    aws ecs list-clusters
    aws rds describe-db-instances
    
  3. Verify region access - ensure your credentials work in the region you’re scanning
  4. Check service control policies (SCPs) if using AWS Organizations

Security best practices

Principle of least privilege

Start with the read-only policy. Only add deletion permissions if you need automated cleanup.

Use temporary credentials

Use AWS SSO or assume-role for time-limited access instead of long-lived access keys.

Enable CloudTrail

Monitor all API calls made by the tool to ensure compliance and troubleshooting.

Rotate credentials

If using IAM user access keys, rotate them regularly (every 90 days minimum).

Build docs developers (and LLMs) love