EC2 Security Group from Launch Wizard
Overview
This check identifies EC2 security groups whose names contain "launch-wizard," which indicates they were auto-generated by the EC2 Launch Wizard rather than created through controlled, baseline security practices.
When you launch an EC2 instance through the Console and click "Edit" on the security group settings, AWS automatically creates a security group named "launch-wizard-X" (where X is a number). These wizard-generated groups are intended for quick testing but often violate security best practices.
Risk
Wizard-generated security groups pose significant security risks:
- Overly permissive rules: Launch-wizard groups typically include broad access rules such as
0.0.0.0/0(anywhere) to administrative ports like SSH (22) or RDP (3389) - Port scanning exposure: Public access to admin ports makes instances easy targets for port scanning and brute-force attacks
- Lateral movement: Once attackers gain entry, they can move laterally through your network
- Data exfiltration: Broad egress rules can facilitate command-and-control communications
- No change control: These groups bypass organizational security baselines and change control procedures
This check is rated Medium severity because it indicates a lack of security governance rather than a direct public exposure.
Remediation Steps
Prerequisites
- AWS account access with permissions to modify EC2 security groups
- Knowledge of which instances are using the launch-wizard security groups
- A replacement security group that follows your organization's security baseline
Required IAM permissions
You will need the following permissions:
ec2:DescribeSecurityGroups- List and view security group detailsec2:DescribeSecurityGroupRules- View security group rulesec2:DescribeInstances- See which instances use the security groupsec2:ModifyInstanceAttribute- Change an instance's security groupsec2:DeleteSecurityGroup- Remove the launch-wizard security groupec2:CreateSecurityGroup- Create a replacement security group (if needed)ec2:AuthorizeSecurityGroupIngress- Add inbound rules to new groupsec2:AuthorizeSecurityGroupEgress- Add outbound rules to new groups
AWS Console Method
Step 1: Find launch-wizard 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)
- In the search box, type
launch-wizardto filter the list - Note the security group IDs and which resources are using them
Step 2: Identify attached resources
Before deleting a security group, you need to know what's using it:
- Click on a launch-wizard security group
- Scroll down to the Associated resources section
- Note any instances, network interfaces, or other resources listed
- You must remove the security group from these resources before you can delete it
Step 3: Create a replacement security group (if needed)
If you don't have an existing security group that follows your security baseline:
- Click Create security group
- Enter a descriptive Name (e.g.,
web-server-sgorbastion-ssh-restricted) - Enter a Description explaining its purpose
- Select the appropriate VPC
- Add Inbound rules following least-privilege principles:
- Restrict source IPs to known ranges (avoid
0.0.0.0/0) - Only open required ports
- For SSH, consider using AWS Systems Manager Session Manager instead
- Restrict source IPs to known ranges (avoid
- Add Outbound rules as needed (default allows all outbound)
- Click Create security group
Step 4: Replace the security group on instances
- Navigate to EC2 > Instances
- Select an instance using the launch-wizard security group
- Click Actions > Security > Change security groups
- Remove the launch-wizard security group
- Add your baseline security group
- Click Save
- Repeat for all affected instances
Step 5: Delete the launch-wizard security group
Once no resources are attached:
- Navigate to EC2 > Security Groups
- Select the launch-wizard security group
- Click Actions > Delete security groups
- Confirm the deletion
AWS CLI (optional)
Find all launch-wizard security groups:
aws ec2 describe-security-groups \
--filters "Name=group-name,Values=*launch-wizard*" \
--query 'SecurityGroups[*].[GroupId,GroupName,VpcId]' \
--output table \
--region us-east-1
See what's using a security group:
aws ec2 describe-network-interfaces \
--filters "Name=group-id,Values=sg-0123456789abcdef0" \
--query 'NetworkInterfaces[*].[NetworkInterfaceId,Attachment.InstanceId,Description]' \
--output table \
--region us-east-1
Replace sg-0123456789abcdef0 with your security group ID.
Create a replacement security group:
aws ec2 create-security-group \
--group-name "web-server-sg" \
--description "Security group for web servers with restricted access" \
--vpc-id vpc-0123456789abcdef0 \
--region us-east-1
Add restricted SSH access (example: from a specific IP):
aws ec2 authorize-security-group-ingress \
--group-id sg-NEW_GROUP_ID \
--protocol tcp \
--port 22 \
--cidr 203.0.113.0/24 \
--region us-east-1
Replace 203.0.113.0/24 with your organization's IP range.
Update an instance to use the new security group:
aws ec2 modify-instance-attribute \
--instance-id i-0123456789abcdef0 \
--groups sg-NEW_GROUP_ID \
--region us-east-1
Delete the launch-wizard security group:
aws ec2 delete-security-group \
--group-id sg-0123456789abcdef0 \
--region us-east-1
This command will fail with DependencyViolation if the security group is still attached to any resources.
CloudFormation (optional)
Use CloudFormation to create properly configured security groups that follow your security baseline. This ensures all security groups are managed through infrastructure-as-code.
AWSTemplateFormatVersion: '2010-09-09'
Description: Baseline security groups to replace launch-wizard groups
Parameters:
VpcId:
Type: AWS::EC2::VPC::Id
Description: VPC where security groups will be created
AllowedSSHCidr:
Type: String
Description: CIDR block allowed for SSH access (e.g., your office IP range)
Default: 10.0.0.0/8
AllowedPattern: '^(\d{1,3}\.){3}\d{1,3}/\d{1,2}$'
Resources:
BastionSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupName: bastion-host-sg
GroupDescription: Security group for bastion hosts with restricted SSH access
VpcId: !Ref VpcId
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 22
ToPort: 22
CidrIp: !Ref AllowedSSHCidr
Description: SSH access from allowed CIDR range
Tags:
- Key: Name
Value: bastion-host-sg
- Key: ManagedBy
Value: CloudFormation
WebServerSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupName: web-server-sg
GroupDescription: Security group for web servers
VpcId: !Ref VpcId
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 443
ToPort: 443
CidrIp: 0.0.0.0/0
Description: HTTPS from anywhere
- IpProtocol: tcp
FromPort: 80
ToPort: 80
CidrIp: 0.0.0.0/0
Description: HTTP from anywhere (redirect to HTTPS)
Tags:
- Key: Name
Value: web-server-sg
- Key: ManagedBy
Value: CloudFormation
InternalAppSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupName: internal-app-sg
GroupDescription: Security group for internal applications
VpcId: !Ref VpcId
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 22
ToPort: 22
SourceSecurityGroupId: !Ref BastionSecurityGroup
Description: SSH from bastion hosts only
Tags:
- Key: Name
Value: internal-app-sg
- Key: ManagedBy
Value: CloudFormation
Outputs:
BastionSecurityGroupId:
Description: Security group ID for bastion hosts
Value: !Ref BastionSecurityGroup
Export:
Name: !Sub '${AWS::StackName}-BastionSG'
WebServerSecurityGroupId:
Description: Security group ID for web servers
Value: !Ref WebServerSecurityGroup
Export:
Name: !Sub '${AWS::StackName}-WebServerSG'
InternalAppSecurityGroupId:
Description: Security group ID for internal applications
Value: !Ref InternalAppSecurityGroup
Export:
Name: !Sub '${AWS::StackName}-InternalAppSG'
Deploy the stack:
aws cloudformation create-stack \
--stack-name baseline-security-groups \
--template-body file://security-groups.yaml \
--parameters ParameterKey=VpcId,ParameterValue=vpc-0123456789abcdef0 \
ParameterKey=AllowedSSHCidr,ParameterValue=10.0.0.0/8 \
--region us-east-1
Terraform (optional)
Use Terraform to create and manage security groups that follow your security baseline.
provider "aws" {
region = "us-east-1"
}
variable "vpc_id" {
description = "VPC ID where security groups will be created"
type = string
}
variable "allowed_ssh_cidr" {
description = "CIDR block allowed for SSH access"
type = string
default = "10.0.0.0/8"
}
# Bastion host security group - restricted SSH access
resource "aws_security_group" "bastion" {
name = "bastion-host-sg"
description = "Security group for bastion hosts with restricted SSH access"
vpc_id = var.vpc_id
ingress {
description = "SSH from allowed CIDR range"
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = [var.allowed_ssh_cidr]
}
egress {
description = "Allow all outbound traffic"
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
tags = {
Name = "bastion-host-sg"
ManagedBy = "Terraform"
}
}
# Web server security group
resource "aws_security_group" "web_server" {
name = "web-server-sg"
description = "Security group for web servers"
vpc_id = var.vpc_id
ingress {
description = "HTTPS from anywhere"
from_port = 443
to_port = 443
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
ingress {
description = "HTTP from anywhere (redirect to HTTPS)"
from_port = 80
to_port = 80
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"]
}
tags = {
Name = "web-server-sg"
ManagedBy = "Terraform"
}
}
# Internal application security group
resource "aws_security_group" "internal_app" {
name = "internal-app-sg"
description = "Security group for internal applications"
vpc_id = var.vpc_id
ingress {
description = "SSH from bastion hosts only"
from_port = 22
to_port = 22
protocol = "tcp"
security_groups = [aws_security_group.bastion.id]
}
egress {
description = "Allow all outbound traffic"
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
tags = {
Name = "internal-app-sg"
ManagedBy = "Terraform"
}
}
output "bastion_security_group_id" {
description = "Security group ID for bastion hosts"
value = aws_security_group.bastion.id
}
output "web_server_security_group_id" {
description = "Security group ID for web servers"
value = aws_security_group.web_server.id
}
output "internal_app_security_group_id" {
description = "Security group ID for internal applications"
value = aws_security_group.internal_app.id
}
Apply the configuration:
terraform init
terraform plan -var="vpc_id=vpc-0123456789abcdef0"
terraform apply -var="vpc_id=vpc-0123456789abcdef0"
Verification
After completing the remediation:
- Go to EC2 > Security Groups in the AWS Console
- Search for
launch-wizard- no results should appear - Verify your instances are using the new baseline security groups
- Re-run the Prowler check to confirm the issue is resolved
CLI verification commands
Verify no launch-wizard security groups remain:
aws ec2 describe-security-groups \
--filters "Name=group-name,Values=*launch-wizard*" \
--query 'SecurityGroups[*].GroupId' \
--output text \
--region us-east-1
This command should return no results.
Verify an instance is using the correct security group:
aws ec2 describe-instances \
--instance-ids i-0123456789abcdef0 \
--query 'Reservations[0].Instances[0].SecurityGroups[*].[GroupId,GroupName]' \
--output table \
--region us-east-1
The output should show your baseline security groups, not any launch-wizard groups.
Additional Resources
- Amazon EC2 Security Groups
- Security Group Rules Reference
- AWS Systems Manager Session Manager - Alternative to SSH that doesn't require open inbound ports
- VPC Security Best Practices
- AWS Well-Architected Framework - Security Pillar
Notes
- Avoid 0.0.0.0/0 for admin ports: Never allow SSH (22), RDP (3389), or database ports from anywhere. Use specific IP ranges or security group references.
- Use Session Manager instead of SSH: AWS Systems Manager Session Manager provides secure shell access without requiring inbound ports or SSH keys.
- Implement change control: All security group changes should go through your organization's change management process and be tracked in version control.
- Tag your security groups: Use consistent tagging to identify purpose, owner, and environment for each security group.
- Regular audits: Periodically review all security groups to ensure they follow least-privilege principles and remove unused groups.
- Use VPC Flow Logs: Enable VPC Flow Logs to monitor traffic and detect potential security issues.
- Consider AWS Firewall Manager: For organizations with multiple accounts, use AWS Firewall Manager to centrally manage security groups across accounts.