Automating AWS EC2 Instance Management with Python

Managing AWS EC2 instances can often require repetitive tasks such as listing available instances, starting stopped ones, and securely logging into them. Automating these tasks not only saves time but also reduces the possibility of human error. In this blog post, we'll explore how to use Python and Boto3, AWS's SDK for Python, to create a simple yet powerful script that lists your EC2 instances, powers on any that are off, and then securely logs into them.

Getting Started

Before diving into the code, ensure you have Python and Boto3 installed in your environment. Boto3 allows you to directly interact with AWS services in Python. If you haven't installed Boto3 yet, you can do so by running pip install boto3.

The Script Explained

Our script performs several key functions to manage EC2 instances efficiently. Let's break down each part of the script:

Initialization

First, we initialize a Boto3 EC2 resource and a client. These objects allow us to interact with the EC2 instances and perform actions like starting or stopping them.

import boto3

ec2 = boto3.resource('ec2')
ec2_client = boto3.client('ec2')

Listing Instances

The list_all_instances function lists all EC2 instances based on specific filters. It can filter instances by name and whether they are running or stopped. This makes it easy to identify which instances are available for connection.

Selecting an Instance

The select_instance function prompts the user to select an instance from the list of available ones. This interaction ensures that users have control over which instance they're managing.

Handling SSH Keys

To securely log into an instance, the correct SSH key is required. The find_key_for_instance function locates the SSH key associated with the instance, ensuring a secure connection.

Starting Stopped Instances

One of the script's key features is its ability to start instances that are found to be stopped. This is handled in the ssh_into_instance function, which checks the instance's state before attempting to log in. If the instance is stopped, the script starts it and waits until it's in the 'running' state.

Securely Logging Into Instances

Finally, once the instance is ready, the script logs into it using SSH. It constructs the SSH command with the correct parameters, including the path to the SSH key, and executes it.

Running the Script

To run the script, simply execute it in your terminal. You'll be prompted to decide whether to include stopped instances in the list and to optionally filter instances by name. Then, select the instance you wish to log into from the provided list. If the selected instance is stopped, the script will start it and then automatically log you in.

Conclusion

This script is a starting point for automating EC2 instance management. By leveraging Python and Boto3, you can customize and extend this script to fit your specific needs, such as automating backups, deploying applications, or managing instance lifecycles. Automating these tasks not only saves time but also ensures that your operations are more secure and efficient.

Remember, automation is key to effective cloud management. By incorporating scripts like this into your workflow, you can focus on more important tasks while letting your code handle the routine work.

import boto3
import subprocess
import os
import time

# Initialize a boto3 EC2 resource
ec2 = boto3.resource('ec2')
ec2_client = boto3.client('ec2')  # Added for starting instances

def list_all_instances(include_stopped=False, search_term=None):
    """List all EC2 instances, optionally excluding stopped instances and filtering by search term."""
    filters = [{'Name': 'tag:Name', 'Values': ['*'+search_term+'*']} if search_term else {'Name': 'instance-state-name', 'Values': ['running', 'stopped']}]

    if not include_stopped:
        filters.append({'Name': 'instance-state-name', 'Values': ['running']})

    instances = ec2.instances.filter(Filters=filters)
    return instances

def get_instance_name(instance):
    """Extract the name of the instance from its tags."""
    for tag in instance.tags or []:
        if tag['Key'] == 'Name':
            return tag['Value']
    return "No Name"

def select_instance(instances):
    """Allow the user to select an instance to log into."""
    print("Available instances:")
    if not instances:
        print("No matching instances found.")
        return None

    for i, instance in enumerate(instances, start=1):
        name = get_instance_name(instance)
        print(f"{i}) Name: {name}, Instance ID: {instance.id}, State: {instance.state['Name']}")

    selection = input("Enter the number of the instance you want to log into (or 'exit' to quit): ")
    if selection.lower() == 'exit':
        return None
    try:
        selection = int(selection) - 1
        return list(instances)[selection]
    except (ValueError, IndexError):
        print("Invalid selection.")
        return None

def find_key_for_instance(instance):
    """Find the SSH key for the instance based on its KeyName."""
    key_name = instance.key_name
    keys_directory = os.path.expanduser("~/.ssh")
    for key_file in os.listdir(keys_directory):
        if key_file.startswith(key_name) and key_file.endswith(".pem"):
            return os.path.join(keys_directory, key_file)
    return None

def ssh_into_instance(instance, remote_user="ec2-user"):
    """SSH into the selected instance, if any."""
    if instance is None:
        return

    # Check instance state and start if stopped
    if instance.state['Name'] == 'stopped':
        print(f"Instance {instance.id} is stopped. Starting instance...")
        ec2_client.start_instances(InstanceIds=[instance.id])
        print("Waiting for instance to be in 'running' state...")
        instance.wait_until_running()
        instance.reload()  # Refresh instance state and details

    ssh_key_path = find_key_for_instance(instance)
    if not ssh_key_path:
        print(f"No matching SSH key found for instance {instance.id} with KeyName {instance.key_name}")
        return

    print(f"Logging into {get_instance_name(instance)} ({instance.id})...")
    private_ip = instance.private_ip_address
    ssh_cmd = f'ssh -o StrictHostKeyChecking=no -i {ssh_key_path} {remote_user}@{private_ip}'
    subprocess.run(ssh_cmd, shell=True)

def main():
    """Main function to list instances and allow user selection for SSH login."""
    include_stopped = input("Include stopped instances? (yes/no): ").lower().startswith('y')
    search_term = input("Enter a search term to filter by instance name (leave empty for no filter): ").strip() or None
    instances = list(list_all_instances(include_stopped, search_term))

    selected_instance = select_instance(instances)
    ssh_into_instance(selected_instance)

if __name__ == "__main__":
    main()
🚀 **Support Our DevOps Blog with Your Amazon Shopping!** 🚀 Love shopping on Amazon? Now you can fuel your shopping spree *and* support our blog at no extra cost! Just use our link for your next purchase: **[Shop on Amazon & Support Us!] Browse Stuff on Amazon Every click helps us keep sharing the DevOps love. Happy shopping!

Leave a Comment