Non-default EC2 security group is in use
EC2 security groups, except default, are assessed for unused status: zero attached network interfaces, no AWS Lambda associations, no AWS Batch compute environment associations, and no references from other security groups.
Risk
Orphaned security groups may later be attached with overly permissive rules without review, enabling unintended inbound or lateral access that compromises confidentiality and integrity. They also create configuration drift, increasing the chance of misapplied access controls.
Run this check with Prowler
Run in Prowler Cloudprowler aws --checks ec2_securitygroup_not_usedRecommendation
Apply least privilege and strong lifecycle management: delete or quarantine unused security groups, enforce ownership tags and retention policies, review regularly, and manage changes via IaC with approvals. Restrict who can attach groups and use guardrails to prevent reuse of stale or permissive groups.
Remediation
CLI
aws ec2 delete-security-group --group-id <SECURITY_GROUP_ID>Terraform
# Destroy the unused non-default security group
resource "aws_security_group" "<example_resource_name>" {
count = 0 # Critical: setting count=0 removes/destroys this unused SG
}Other
Check Code
from prowler.lib.check.models import Check, Check_Report_AWS
from prowler.providers.aws.services.awslambda.awslambda_client import awslambda_client
from prowler.providers.aws.services.batch.batch_client import batch_client
from prowler.providers.aws.services.ec2.ec2_client import ec2_client
class ec2_securitygroup_not_used(Check):
def execute(self):
findings = []
for security_group_arn, security_group in ec2_client.security_groups.items():
# Default security groups can not be deleted, so ignore them
if security_group.name != "default":
report = Check_Report_AWS(
metadata=self.metadata(), resource=security_group
)
report.resource_details = security_group.name
report.status = "PASS"
report.status_extended = f"Security group {security_group.name} ({security_group.id}) it is being used."
sg_in_lambda = (
security_group.id in awslambda_client.security_groups_in_use
)
# A Batch compute environment scaled down to zero instances
# keeps its security groups in configuration without any ENI
sg_in_batch = security_group.id in batch_client.security_groups_in_use
sg_associated = False
for sg in ec2_client.security_groups.values():
if security_group.id in sg.associated_sgs:
sg_associated = True
if (
len(security_group.network_interfaces) == 0
and not sg_in_lambda
and not sg_in_batch
and not sg_associated
):
# Compute environments failing to list leaves their security
# group associations unknown, not absent, so reporting the
# group as unused would be a guess. Not being able to read
# the compute environments is a lack of visibility, not a
# misconfiguration, so report MANUAL rather than asserting a
# status either way.
if (
security_group.region
in batch_client.compute_environment_lookup_failed_regions
):
report.status = "MANUAL"
report.status_extended = f"Security group {security_group.name} ({security_group.id}) usage could not be verified because AWS Batch compute environments could not be listed in region {security_group.region}; grant batch:DescribeComputeEnvironments and run the check again."
else:
report.status = "FAIL"
report.status_extended = f"Security group {security_group.name} ({security_group.id}) it is not being used."
findings.append(report)
return findings