EC2 Security Group Not Used
Overview
This check identifies non-default EC2 security groups that are not attached to any resources. A security group is considered unused when it has:
- Zero attached network interfaces
- No AWS Lambda function associations
- No references from other security groups
Unused security groups are essentially orphaned resources that clutter your AWS environment.
Risk
Orphaned security groups create security and operational risks:
- Accidental misuse: An unused security group with permissive rules could be attached to a new resource without proper review, accidentally granting unintended access
- Configuration drift: Stale security groups make it harder to understand your actual security posture
- Lateral movement opportunity: Attackers who gain access to your account could attach these unused groups to resources to expand their access
- Compliance gaps: Auditors may flag unused security groups as poor security hygiene
- Cost of confusion: More security groups means more complexity when troubleshooting network issues
This check is rated Low severity because unused security groups are not directly exploitable, but they represent a housekeeping issue that could lead to future problems.
Remediation Steps
Prerequisites
- AWS account access with permissions to view and delete EC2 security groups
- Verify the security group is truly unused before deletion (this guide will show you how)
Required IAM permissions
You will need the following permissions:
ec2:DescribeSecurityGroups- View security group detailsec2:DescribeNetworkInterfaces- Check if security group is attached to any ENIsec2:DeleteSecurityGroup- Delete unused security groups
AWS Console Method
Step 1: Find unused security groups
- Sign in to the AWS Management Console
- Navigate to EC2 > Security Groups (under "Network & Security" in the left sidebar)
- Make sure you are in the correct region (e.g., us-east-1)
- Add a filter: click the search box, select Associated with, and choose No resources
- This displays security groups with no attached resources
Step 2: Verify the security group is safe to delete
Before deleting, confirm the security group is not referenced elsewhere:
- Select the security group by clicking its checkbox
- In the details panel below, check the Inbound rules and Outbound rules tabs
- Look for rules that reference other security groups (these could indicate dependencies)
- Click the Network interfaces tab and confirm it shows "No network interfaces"
Step 3: Delete the unused security group
- Select the security group you want to delete
- Click Actions > Delete security groups
- In the confirmation dialog, type
deleteto confirm - Click Delete
Note: You cannot delete the default security group for a VPC. If you see it flagged, you can safely ignore it since default security groups cannot be deleted.
AWS CLI (optional)
List security groups with no attached network interfaces:
aws ec2 describe-security-groups \
--query 'SecurityGroups[?length(IpPermissions[?UserIdGroupPairs]) == `0` || IpPermissions == `[]`].[GroupId,GroupName,VpcId]' \
--output table \
--region us-east-1
Find security groups not attached to any ENI:
# Get all security group IDs in use
USED_SGS=$(aws ec2 describe-network-interfaces \
--query 'NetworkInterfaces[*].Groups[*].GroupId' \
--output text \
--region us-east-1 | tr '\t' '\n' | sort -u)
# List all non-default security groups
aws ec2 describe-security-groups \
--query 'SecurityGroups[?GroupName != `default`].[GroupId,GroupName]' \
--output text \
--region us-east-1
Delete a specific unused security group:
aws ec2 delete-security-group \
--group-id <security-group-id> \
--region us-east-1
Replace <security-group-id> with the actual security group ID (e.g., sg-0123456789abcdef0).
Bulk delete multiple unused security groups:
# First, list the groups you want to delete and review them
UNUSED_SGS="sg-0123456789abcdef0 sg-0987654321fedcba0"
# Then delete each one
for sg in $UNUSED_SGS; do
echo "Deleting $sg..."
aws ec2 delete-security-group --group-id $sg --region us-east-1
done
Warning: Always verify security groups are truly unused before bulk deletion. Some resources like Lambda functions or RDS instances may use security groups that don't show up when filtering by ENIs alone.
CloudFormation (optional)
CloudFormation does not have a built-in way to detect and delete unused security groups. However, you can prevent orphaned security groups by always managing them through CloudFormation stacks.
Best practice: Define security groups in CloudFormation
When you delete a CloudFormation stack, any security groups defined in that stack are automatically cleaned up (assuming no resources outside the stack reference them).
AWSTemplateFormatVersion: '2010-09-09'
Description: Managed security group that will be cleaned up when stack is deleted
Parameters:
VpcId:
Type: AWS::EC2::VPC::Id
Description: VPC where the security group will be created
Resources:
ManagedSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: Managed security group for application servers
GroupName: app-server-sg
VpcId: !Ref VpcId
Tags:
- Key: Name
Value: app-server-sg
- Key: ManagedBy
Value: CloudFormation
- Key: Environment
Value: Production
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 443
ToPort: 443
CidrIp: 0.0.0.0/0
Description: Allow HTTPS from anywhere
SecurityGroupEgress:
- IpProtocol: -1
CidrIp: 0.0.0.0/0
Description: Allow all outbound traffic
Outputs:
SecurityGroupId:
Description: ID of the managed security group
Value: !Ref ManagedSecurityGroup
Export:
Name: !Sub '${AWS::StackName}-SecurityGroupId'
Tagging strategy: Always tag security groups with ManagedBy: CloudFormation so you can easily identify which security groups are managed vs. orphaned.
Terraform (optional)
Prevent orphaned security groups with Terraform lifecycle management:
When you run terraform destroy or remove a security group from your configuration, Terraform will automatically delete it (assuming no external dependencies).
provider "aws" {
region = "us-east-1"
}
variable "vpc_id" {
description = "VPC ID where security group will be created"
type = string
}
resource "aws_security_group" "managed" {
name = "app-server-sg"
description = "Managed security group for application servers"
vpc_id = var.vpc_id
tags = {
Name = "app-server-sg"
ManagedBy = "Terraform"
Environment = "Production"
}
ingress {
description = "Allow HTTPS from anywhere"
from_port = 443
to_port = 443
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
egress {
description = "Allow all outbound traffic"
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
# Prevent accidental deletion if the security group is in use
lifecycle {
create_before_destroy = true
}
}
output "security_group_id" {
description = "ID of the managed security group"
value = aws_security_group.managed.id
}
To remove an unused security group managed by Terraform:
- Remove the resource block from your
.tffile - Run
terraform planto see what will be deleted - Run
terraform applyto delete the security group
For security groups not managed by Terraform, you can import and then destroy them:
# Import the orphaned security group
terraform import aws_security_group.orphaned sg-0123456789abcdef0
# Then remove from state and delete via CLI, or add to config with count = 0
terraform state rm aws_security_group.orphaned
aws ec2 delete-security-group --group-id sg-0123456789abcdef0 --region us-east-1
Verification
After deleting the unused security groups:
- Go to EC2 > Security Groups in the AWS Console
- Filter by Associated with > No resources
- Confirm the deleted security groups no longer appear
- Re-run the Prowler check to verify the issue is resolved
CLI verification commands
Verify a security group was deleted:
aws ec2 describe-security-groups \
--group-ids <security-group-id> \
--region us-east-1
This should return an error: InvalidGroup.NotFound confirming the security group no longer exists.
List all remaining unused security groups:
# Get security group IDs attached to network interfaces
USED_SGS=$(aws ec2 describe-network-interfaces \
--query 'NetworkInterfaces[*].Groups[*].GroupId' \
--output text \
--region us-east-1 | tr '\t' '\n' | sort -u)
# Get all non-default security groups
ALL_SGS=$(aws ec2 describe-security-groups \
--query 'SecurityGroups[?GroupName != `default`].GroupId' \
--output text \
--region us-east-1 | tr '\t' '\n' | sort -u)
# Show security groups not in the used list
comm -23 <(echo "$ALL_SGS") <(echo "$USED_SGS")
If this returns no output, all your non-default security groups are in use.
Additional Resources
- Amazon EC2 Security Groups
- Delete a Security Group
- AWS CLI: delete-security-group
- Security Group Rules Reference
Notes
- Cannot delete default security groups: Each VPC has a default security group that cannot be deleted. If Prowler flags it, you can ignore this specific case.
- Check for hidden dependencies: Some AWS services (Lambda, RDS, ECS, etc.) may reference security groups without creating visible network interfaces. Before deleting, search for the security group ID in CloudWatch Logs, Lambda configurations, and RDS settings.
- Deletion may fail if referenced: If you try to delete a security group that is referenced by another security group's rules, the deletion will fail. You must first remove the referencing rules.
- Implement lifecycle management: Consider implementing a policy to review and clean up unused security groups monthly. Tag security groups with creation dates and owners to make cleanup easier.
- Use Infrastructure as Code: Managing security groups through CloudFormation or Terraform prevents orphaned resources since they are cleaned up when stacks are deleted or resources are removed from configuration.
- Quarantine before deletion: If unsure whether a security group is truly unused, rename it (e.g., prefix with
TO-DELETE-) and wait a week before deleting. This gives you time to discover any hidden dependencies.