Manage EC2 Security Groups and Key Pairs
Learn to manage EC2 security groups and key pairs in this AWS Cloud & DevOps with Python tutorial — hands-on steps, troubleshooting, and what to study next.
Focus: manage ec2 security groups and key pairs
You've built a Python app, launched an EC2 instance with boto3, and maybe even pushed an AMI to production. Then, in the middle of the night, your pager goes off: the instance is unreachable. You check the console and realize port 22 is open to the world, or worse, your key pair is missing and you can't SSH in at all. This is the pain that managing EC2 security groups and key pairs poorly causes — a silent, preventable infrastructure nightmare. In this lesson, you'll move from just launching instances to truly managing their network access and authentication, using Python to automate what every DevOps engineer must master: security groups as your firewall and key pairs as your keys to the kingdom.
The Problem This Lesson Solves
Every EC2 instance is, by default, a fortress with no doors — no inbound traffic is allowed until you explicitly open it. But many developers make one of two mistakes: they either leave ports wide open (e.g., 0.0.0.0/0 on port 22) for convenience, or they forget to create a key pair entirely, locking themselves out of SSH. The real-world pain is threefold:
- Security breaches: An open SSH port invites brute-force attacks; a misconfigured security group can expose your database or app to the internet.
- Operational downtime: Without a key pair, you can't SSH into your instance to fix a broken service or deploy a hotfix.
- Manual chaos: Clicking through the AWS console to update rules or import keys is slow, error-prone, and impossible to scale across dozens of instances.
This lesson solves that pain by showing you how to use Python (with boto3) to create, update, and clean up security groups and key pairs programmatically — turning a risky manual chore into a repeatable, auditable script.
Core Concept / Mental Model
Think of a security group as a stateful firewall attached to your instance. Think of a key pair as the front-door key for your instance's SSH access. Together, they form the entry control for your cloud perimeter.
- Security groups are stateful: if you allow inbound traffic on port 80, the outbound response is automatically allowed. Rules are defined by protocol (e.g., TCP), port range (e.g., 80), and source (an IP range or another security group).
- Key pairs are cryptographic: AWS stores the public key, you keep the private key (a
.pemfile). You use the private key to authenticate when you SSH into the instance.
In Python, you manage these via the boto3 library — the AWS SDK. The mental model is simple: you call service methods like create_security_group, authorize_security_group_ingress, and create_key_pair, and boto3 translates them into HTTPS API calls to AWS. You don't need to click through the console; your code becomes the source of truth.
How It Works Step by Step
Whether you're creating a new security group or setting up key pairs, the process follows a clear, logical sequence. Here's the mental checklist:
- Create the security group: Define a name and a description, and note the VPC ID (or use the default VPC).
- Add inbound rules: Authorize specific protocols, ports, and source IP ranges. For example, allow SSH from your office IP only.
- Add outbound rules (optional): By default, all outbound traffic is allowed. You can restrict it if needed.
- Attach the security group to your instance: This happens at launch time, but you can also modify it later (which we'll cover in troubleshooting).
- Create a key pair: Generate a new key pair, and immediately save the private key to a secure
.pemfile. You cannot download it again — this is a common gotcha. - Use the key pair: When launching an instance, specify the key pair name. Then use the
.pemfile to SSH in. - Clean up: Delete security groups and key pairs when they're no longer needed to avoid clutter and cost.
Hands-On Walkthrough
Now let's put theory into practice. We'll write Python scripts using boto3 that you can run on any machine with AWS credentials configured (via aws configure or environment variables).
Prerequisites
pip install boto3
And make sure your AWS credentials are set:
export AWS_ACCESS_KEY_ID=your_access_key
export AWS_SECRET_ACCESS_KEY=your_secret_key
export AWS_DEFAULT_REGION=us-east-1
Example 1: Create a Security Group with SSH Access
import boto3
from botocore.exceptions import ClientError
# Use the default VPC (or specify one)
ec2 = boto3.client('ec2')
def get_default_vpc_id():
response = ec2.describe_vpcs(Filters=[{'Name': 'isDefault', 'Values': ['true']}])
return response['Vpcs'][0]['VpcId']
def create_security_group(name, description):
vpc_id = get_default_vpc_id()
try:
response = ec2.create_security_group(
GroupName=name,
Description=description,
VpcId=vpc_id
)
sg_id = response['GroupId']
print(f'Created security group {name} with ID {sg_id}')
return sg_id
except ClientError as e:
# Handle duplicate name error
if 'InvalidGroup.Duplicate' in str(e):
print(f'Security group {name} already exists. Fetching ID...')
sg = ec2.describe_security_groups(GroupNames=[name])['SecurityGroups'][0]
return sg['GroupId']
else:
raise
# Add an inbound rule for SSH from your IP (replace with your public IP)
def authorize_ssh(sg_id, ip):
ec2.authorize_security_group_ingress(
GroupId=sg_id,
IpPermissions=[{
'IpProtocol': 'tcp',
'FromPort': 22,
'ToPort': 22,
'IpRanges': [{'CidrIp': f'{ip}/32', 'Description': 'SSH from home'}]}
])
print(f'SSH access allowed from {ip}/32')
# Run it
sg_id = create_security_group('MyWebServerSG', 'Web server security group')
authorize_ssh(sg_id, '203.0.113.10')
Expected output:
Created security group MyWebServerSG with ID sg-0123456789abcdef0
SSH access allowed from 203.0.113.10/32
Pro tip: Always restrict SSH to your own IP or a VPN CIDR. Using
0.0.0.0/0is the #1 cause of security breaches on AWS.
Example 2: Create and Save a Key Pair
import boto3
import os
from botocore.exceptions import ClientError
# Create a key pair
ec2 = boto3.client('ec2')
key_name = 'my-dev-key'
def create_key_pair(key_name):
try:
response = ec2.create_key_pair(KeyName=key_name)
private_key = response['KeyMaterial']
# Save the private key to a file with restricted permissions
with open(f'{key_name}.pem', 'w') as f:
f.write(private_key)
os.chmod(f'{key_name}.pem', 0o400) # Read/write for owner only
print(f'Key pair {key_name} created and saved to {key_name}.pem')
except ClientError as e:
if 'Duplicate' in str(e):
print(f'Key pair {key_name} already exists. Reuse it or delete it first.')
else:
raise
create_key_pair(key_name)
Expected output:
Key pair my-dev-key created and saved to my-dev-key.pem
Warning: The private key is shown only once. If you lose it, you cannot recover it — you must delete and recreate the key pair.
Example 3: Clean Up Resources
import boto3
from botocore.exceptions import ClientError
ec2 = boto3.client('ec2')
def delete_security_group(sg_id):
try:
ec2.delete_security_group(GroupId=sg_id)
print(f'Deleted security group {sg_id}')
except ClientError as e:
print(f'Error deleting SG: {e}')
def delete_key_pair(key_name):
try:
ec2.delete_key_pair(KeyName=key_name)
print(f'Deleted key pair {key_name}')
except ClientError as e:
print(f'Error deleting key pair: {e}')
# Replace with actual IDs/names
# delete_security_group('sg-0123456789abcdef0')
# delete_key_pair('my-dev-key')
Expected output (when run):
Deleted security group sg-0123456789abcdef0
Deleted key pair my-dev-key
Compare Options / When to Choose What
When managing security groups, you have several options for source rules. The choice depends on your use case. Here's a comparison table:
| Source type | Example | Use case | Pros | Cons |
|---|---|---|---|---|
| Single IP | 203.0.113.10/32 |
Developer SSH access | Most secure; precise | Need to update when IP changes |
| IP range (CIDR) | 203.0.113.0/24 |
Office network | Covers multiple users | Larger attack surface |
| Anywhere | 0.0.0.0/0 |
Public web server (port 80/443) | Simple; allows all | Dangerous for SSH/admin ports |
| Security group reference | sg-abcdef01234567890 |
Allow traffic from another SG (e.g., load balancer) | Dynamic; follows attached resources | More complex to set up |
For key pairs, you have two main approaches when you need to SSH into an instance:
- Create a new key pair with
create_key_pair— the standard approach, but you must manage the private key file securely. - Import your own public key with
import_key_pair— if you already have an SSH key, you can import just the public part, keeping your private key safe.
Troubleshooting & Edge Cases
Even with the right steps, things can go wrong. Here are common issues and how to fix them:
InvalidGroup.Duplicateerror: You tried to create a security group with a name that already exists. Solution: either use a different name or fetch the existing group's ID.InvalidPermission.Duplicateerror: You tried to add an ingress rule that already exists. Theauthorize_security_group_ingresscall is idempotent? Actually, it's not — it throws an error. Solution: check existing rules before adding, or usedescribe_security_groupsto verify.- Can't SSH, even with the key pair: Your security group doesn't allow port 22 from your IP, or the key pair is wrong. Debug by checking the security group rules and trying
ssh -i yourkey.pem ec2-user@instance_ip. - Lost private key: AWS does not store the private key. You must delete the key pair and create a new one to regain access.
- Security group deletion fails: If the security group is attached to an instance, you'll get a
DependencyViolationerror. Detach it first by modifying the instance's security groups. - Port 22 open to the world: A common misconfiguration. Use
describe_security_groupsto audit your rules and look for0.0.0.0/0on SSH or RDP ports.
What You Learned & What's Next
You've now learned the core skills for managing EC2 security groups and key pairs with Python. You can create security groups, authorize the right inbound rules, generate and secure key pairs, and clean up when done. These abilities are the foundation of secure EC2 operations — without them, your instances are either isolated or exposed.
In our next lesson, we'll build on this foundation to launch EC2 instances with custom configurations — combining security groups and key pairs into a single automated deployment. You'll learn how to attach your newly created SG and key pair to an instance, and finally, how to SSH in and take control of your cloud server. Stay tuned!
Before you move on, make sure you can:
- Create a security group with inbound SSH rules using boto3.
- Generate a key pair and save the
.pemfile securely. - List and delete security groups and key pairs programmatically.
Practice recap
Now it's your turn: write a script that (1) creates a security group with both SSH (from your IP) and HTTP (from anywhere) rules, and (2) creates a key pair and saves the private key to a file. Then, in a separate script, list all your security groups and key pairs, and note which ones you can safely delete. Run the cleanup to remove the ones you created in this exercise.
Common mistakes
- Using
0.0.0.0/0for SSH access — you're exposing your instance to brute force attacks. Always restrict to your own IP or a trusted CIDR. - Forgetting to save the private key after
create_key_pair— AWS only gives you the private key once, so if you don't save it, you're locked out. - Not checking for
InvalidGroup.Duplicatebefore creating a security group — your script will crash on re-runs. - Deleting a security group that is still attached to an instance — you'll get a
DependencyViolationerror. - Leaving old security groups and key pairs behind — they clutter your account and increase the attack surface.
Variations
- Use
ec2.import_key_pair()to import an existing public SSH key instead of creating a new key pair. - Use
ec2.revoke_security_group_ingress()to remove rules dynamically, e.g., when rotating IPs or closing ports. - Use EC2 security group rules with security group references to allow traffic from load balancers or other instance groups.
Real-world use cases
- Automating the creation of per-environment security groups (dev, staging, prod) in a CI/CD pipeline using boto3 scripts.
- A daily audit script that lists all security groups and flags any with port 22 open to
0.0.0.0/0and sends the results to a Slack channel. - Rotating key pairs for a fleet of EC2 instances: generate a new key, distribute it via a secure channel, and delete the old one.
Key takeaways
- Security groups are stateful firewalls; you must explicitly allow inbound traffic.
- Key pairs are private keys — save the
.pemfile immediately and protect it. - Use boto3 methods like
create_security_group,authorize_security_group_ingress, andcreate_key_pairto automate access management. - Always audit your security groups to avoid leaving dangerous ports open, especially SSH.
- Clean up unused security groups and key pairs to avoid clutter and reduce risk.
- Troubleshooting involves checking for duplicate rules, dependencies, and private key loss.
Keep learning
Related tutorials, quizzes, and articles for this topic.
Discussion
Questions, corrections, and tips help everyone reading this page.
0 comments
Add a comment
No comments yet — start the thread.