Your AWS account has hit the limit for security groups in a VPC or region. This happens when creating too many security groups or when Terraform state becomes out of sync with AWS. You can request a limit increase, consolidate security groups, or clean up unused resources.
AWS enforces quotas (limits) on the number of resources you can create. For security groups, the default limits are: - 500 security groups per VPC - 2,500 security groups per region - 60 inbound rules + 60 outbound rules per security group When Terraform tries to create a new security group and you've hit one of these limits, AWS returns the "LimitExceeded" error. This is a hard architectural limit to prevent runaway resource creation and ensure API performance. The error prevents the security group from being created until you either increase your quota or remove unused security groups.
First, determine if you've actually hit the limit:
# List all security groups and count them
aws ec2 describe-security-groups --query 'SecurityGroups[*].[GroupId,GroupName]' --output text | wc -l
# Get detailed quota information
aws service-quotas get-service-quota \
--service-code vpc \
--quota-code L-E79EC296 \
--region us-east-1If the count is close to 500 (per VPC) or 2,500 (per region), you've hit the limit.
Before requesting a quota increase, remove any orphaned security groups:
# List security groups not attached to any resources
aws ec2 describe-security-groups --query \
'SecurityGroups[?length(IpPermissions) == `0` && length(IpPermissionsEgress) <= `1`].[GroupId,GroupName]'
# Delete a specific unused security group (cannot delete if attached to instances)
aws ec2 delete-security-group --group-id sg-xxxxxxxxxNote: You cannot delete the default security group for a VPC. Also, security groups attached to running instances cannot be deleted.
If you need more security groups, request a quota increase:
1. Go to the AWS Console: Service Quotas → VPC
2. Search for "Security groups per VPC" (quota code L-E79EC296)
3. Click the quota name
4. Click "Request quota increase"
5. Enter the new desired count (e.g., 1000)
6. Click "Request quota increase"
AWS typically approves quota increases within minutes to hours. You'll receive an email when the quota is increased.
Alternatively, use the AWS CLI:
aws service-quotas request-service-quota-increase \
--service-code vpc \
--quota-code L-E79EC296 \
--desired-value 1000 \
--region us-east-1Instead of creating a new security group, consolidate rules into existing ones:
# BEFORE: Creating a new security group for each rule set (inefficient)
resource "aws_security_group" "app_http" {
vpc_id = aws_vpc.main.id
ingress {
from_port = 80
to_port = 80
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
}
resource "aws_security_group" "app_https" {
vpc_id = aws_vpc.main.id
ingress {
from_port = 443
to_port = 443
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
}
# AFTER: Single security group with multiple rules
resource "aws_security_group" "app" {
vpc_id = aws_vpc.main.id
ingress {
from_port = 80
to_port = 80
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
ingress {
from_port = 443
to_port = 443
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
}State drift can cause Terraform to try creating duplicates:
# Import any manually created security groups
terraform import aws_security_group.existing sg-xxxxxxxxx
# Refresh state to sync with AWS
terraform refresh
# Validate the plan before applying
terraform plan
# If there are orphaned resources in state, remove them
terraform state rm aws_security_group.old_broken_sgReview the plan output carefully. If you see unexpected resource creation attempts, there's likely state drift.
If you have many similar rules, use AWS managed prefix lists instead:
# Define rules using a single prefix list instead of multiple CIDR blocks
resource "aws_security_group" "app" {
vpc_id = aws_vpc.main.id
# Instead of 20 separate rules for AWS service IPs, use one prefix list
ingress {
from_port = 443
to_port = 443
protocol = "tcp"
prefix_list_ids = ["pl-12345678"] # AWS managed prefix list for CloudFront
}
}AWS provides managed prefix lists for common services (CloudFront, S3, DynamoDB, etc.). This reduces rule count without limiting functionality.
Multi-Region Considerations: Each AWS region has its own quota of 2,500 security groups. If you're deploying across regions, hitting the limit in one region doesn't affect others. However, if you're using a single VPC spanning regions (not typical), consolidate your approach.
Service-Specific Limits: Some AWS services like ECS, RDS, and EKS automatically create security groups. If using these services heavily, they can consume your quota quickly. Consider using service-linked security groups or having dedicated VPCs per environment.
Terraform State Considerations: If your Terraform state has orphaned security groups (created but not attached to instances), they still count against your quota. Regular terraform plan and cleanup prevents quota exhaustion.
Quota Increase Timing: Quota increase requests are usually approved within minutes. However, during extreme demand periods, approval might take hours. Plan infrastructure changes early if you anticipate needing quota increases.
Error: Error installing helm release: cannot re-use a name that is still in use
How to fix "release name in use" error in Terraform with Helm
Error: Error creating GKE Cluster: BadRequest
BadRequest error creating GKE cluster in Terraform
Error: External program failed to produce valid JSON
External program failed to produce valid JSON
Error: Unsupported argument in child module call
How to fix "Unsupported argument in child module call" in Terraform
Error: network is unreachable
How to fix "network is unreachable" in Terraform