利用Python一键创建AWS EC2实例

运行环境:

  1. 一个AWS可用账号 免费玩AWS-CSDN博客

  2. 本地已安装配置AWS CLI AWS创建API Access Key-CSDN博客

  3. 本地已安装Python

AWS EC2实例自动化管理脚本功能清单

一、核心管理功能

1. EC2实例管理

  • 自动检测 - 检查是否存在运行中的指定标签实例

  • 自动创建 - 无运行实例时自动创建新实例

  • 状态监控 - 实时监控实例创建状态

  • 标签管理 - 自动添加实例名称标签

2. SSH密钥对管理

  • 自动检查 - 验证AWS平台密钥对是否存在

  • 自动创建 - 平台缺失时自动创建新密钥对

  • 本地存储 - 自动保存PEM文件至本地keys目录

  • 权限设置 - 自动配置PEM文件访问权限

  • 故障恢复 - 本地PEM丢失时自动重建并更新密钥名

  • 格式验证 - 自动验证PEM文件格式完整性

3. 安全组管理

  • 自动检查 - 查询指定名称安全组

  • 自动创建 - 不存在时创建新安全组

  • 规则配置 - 自动添加SSH(22端口)全球访问规则

二、系统特性

1. 跨平台支持

  • Windows系统 - 支持管理员权限检测、icacls权限设置

  • Linux系统 - 支持chmod权限设置、euid检测

  • 终端适配 - 兼容不同操作系统的命令重定向

2. 用户交互

  • 彩色输出 - 7种颜色区分不同信息类型

  • 进度显示 - 实时显示操作状态

  • 异常提示 - 清晰的错误信息和堆栈跟踪

  • 操作指引 - 自动生成SSH连接命令

  • 按键暂停 - 执行完毕等待用户确认

3. 错误处理

  • 异常捕获 - 全面的try-catch异常处理

  • 状态验证 - AWS命令执行状态码检查

  • 故障恢复 - 密钥损坏自动重建

  • 资源清理 - 异常状态及时反馈

三、配置参数

可配置项

参数 说明 默认值
AMI_ID Amazon机器镜像ID ami-098d7f3f866fca1fd
REGION AWS区域 ap-southeast-1
INSTANCE_TYPE 实例规格 t4g.micro
DEFAULT_KEY_NAME 默认密钥名称 ec2-keypair
NAME_TAG 实例名称标签 my-ec2
SECURITY_GROUP_NAME 安全组名称 ec2-security-group
SSH_USER SSH登录用户 admin

四、执行流程

  1. 初始化 → 2. 密钥对检查/创建 → 3. 安全组检查/创建

  2. 实例状态检测 → 5. (如有)获取运行实例 / (如无)创建新实例

  3. 等待运行完成 → 7. 获取公网IP → 8. 显示SSH连接信息

五、适用场景

  • 🔹 开发环境快速部署 - 快速创建临时测试实例

  • 🔹 自动化运维 - 集成到CI/CD流程

  • 🔹 教学演示 - 演示AWS EC2实例创建流程

  • 🔹 灾备恢复 - 快速重建丢失密钥的实例连接

  • 🔹 跨平台工具 - 在不同操作系统统一管理EC2

python 复制代码
#!/usr/bin/env python3

import os
import sys
import time
import json
import stat
import subprocess
from pathlib import Path

# ==============================
# CONFIG
# ==============================
AMI_ID = "ami-098d7f3f866fca1fd"
REGION = "ap-southeast-1"
INSTANCE_TYPE = "t4g.micro"
DEFAULT_KEY_NAME = "ec2-keypair"
NAME_TAG = "my-ec2"
SECURITY_GROUP_NAME = "ec2-security-group"
SECURITY_GROUP_DESC = "EC2 SSH Access Security Group"
SSH_USER = "admin"

# ==============================
# COLOR CONSTANTS
# ==============================
class Colors:
    CYAN = '\033[96m'
    GREEN = '\033[92m'
    YELLOW = '\033[93m'
    RED = '\033[91m'
    MAGENTA = '\033[95m'
    WHITE = '\033[97m'
    GRAY = '\033[90m'
    END = '\033[0m'

# ==============================
# UTILITY FUNCTIONS
# ==============================

def pause_script():
    """Pause and wait for key press"""
    print(f"\n{Colors.CYAN}========================================={Colors.END}")
    print(f"{Colors.CYAN}Press any key to exit...{Colors.END}")
    print(f"{Colors.CYAN}========================================={Colors.END}")
    try:
        if os.name == 'nt':
            import msvcrt
            msvcrt.getch()
        else:
            import termios, tty
            fd = sys.stdin.fileno()
            old = termios.tcgetattr(fd)
            try:
                tty.setraw(fd)
                sys.stdin.read(1)
            finally:
                termios.tcsetattr(fd, termios.TCSADRAIN, old)
    except:
        input()

def run_aws_command(command, return_output=True):
    """Run AWS CLI command and return output"""
    try:
        result = subprocess.run(
            command,
            shell=True,
            capture_output=True,
            text=True,
            check=False
        )
        
        if return_output:
            return result.stdout.strip(), result.stderr, result.returncode
        return result.returncode == 0
    except Exception as e:
        print(f"{Colors.RED}Error running AWS command: {e}{Colors.END}")
        return None if return_output else False

def is_admin():
    """Check if running as administrator on Windows"""
    if os.name == 'nt':
        try:
            import ctypes
            return ctypes.windll.shell32.IsUserAnAdmin() != 0
        except:
            return False
    return os.geteuid() == 0

# ==============================
# KEYPAIR FUNCTIONS
# ==============================

def ensure_keypair():
    """Check and create SSH keypair if needed"""
    global current_key_name
    
    print()
    print(f"{Colors.CYAN}========================================={Colors.END}")
    print(f"{Colors.CYAN}Checking SSH keypair: {current_key_name}{Colors.END}")
    print(f"{Colors.CYAN}========================================={Colors.END}")
    print()
    
    # Create keys directory
    script_dir = os.path.dirname(os.path.abspath(__file__))
    key_dir = os.path.join(script_dir, "keys")
    pem_path = os.path.join(key_dir, f"{current_key_name}.pem")
    
    # Create keys directory if it doesn't exist
    if not os.path.exists(key_dir):
        os.makedirs(key_dir)
        print(f"{Colors.GRAY}Created directory: {key_dir}{Colors.END}")
    
    # Check if keypair exists in AWS
    print(f"{Colors.YELLOW}Checking if keypair '{current_key_name}' exists in AWS...{Colors.END}")
    
    # Platform-specific null redirection
    null_device = '2>nul' if os.name == 'nt' else '2>/dev/null'
    cmd = f'aws ec2 describe-key-pairs --key-names {current_key_name} --region {REGION} --query "KeyPairs[0].KeyName" --output text {null_device}'
    
    output, stderr, rc = run_aws_command(cmd)
    
    if not output or output == "None" or output == "":
        print(f"{Colors.YELLOW}Keypair '{current_key_name}' not found in AWS. Creating new keypair...{Colors.END}")
        
        # Create new keypair
        cmd = f'aws ec2 create-key-pair --key-name {current_key_name} --region {REGION} --query "KeyMaterial" --output text'
        key_material, stderr, rc = run_aws_command(cmd)
        
        if rc == 0 and key_material:
            # Save PEM file
            with open(pem_path, 'w') as f:
                f.write(key_material + '\n')
            
            # Set permissions
            if os.name == 'nt':
                try:
                    subprocess.run(f'icacls {pem_path} /reset', shell=True, capture_output=True)
                    subprocess.run(f'icacls {pem_path} /inheritance:r', shell=True, capture_output=True)
                    subprocess.run(f'icacls {pem_path} /grant:r "{os.environ["USERNAME"]}:(R)"', shell=True, capture_output=True)
                    print(f"{Colors.GRAY}PEM file permissions set{Colors.END}")
                except:
                    print(f"{Colors.GRAY}Note: Could not set file permissions{Colors.END}")
            else:
                os.chmod(pem_path, stat.S_IRUSR | stat.S_IWUSR)
            
            print(f"{Colors.GREEN}Keypair created successfully!{Colors.END}")
            print(f"{Colors.GREEN}PEM file saved to: {pem_path}{Colors.END}")
        else:
            raise Exception(f"Failed to create keypair '{current_key_name}'")
    else:
        print(f"{Colors.GREEN}Keypair '{current_key_name}' exists in AWS{Colors.END}")
        
        # Check if PEM file exists locally
        if not os.path.exists(pem_path):
            print(f"{Colors.YELLOW}PEM file not found locally. Creating new keypair...{Colors.END}")
            print(f"{Colors.YELLOW}Note: AWS does not allow downloading existing private keys.{Colors.END}")
            print(f"{Colors.YELLOW}Creating a new keypair with timestamp suffix...{Colors.END}")
            
            timestamp = time.strftime("%Y%m%d%H%M%S")
            new_key_name = f"{current_key_name}-{timestamp}"
            new_pem_path = os.path.join(key_dir, f"{new_key_name}.pem")
            
            cmd = f'aws ec2 create-key-pair --key-name {new_key_name} --region {REGION} --query "KeyMaterial" --output text'
            key_material, stderr, rc = run_aws_command(cmd)
            
            if rc == 0 and key_material:
                current_key_name = new_key_name
                pem_path = new_pem_path
                
                with open(new_pem_path, 'w') as f:
                    f.write(key_material + '\n')
                
                if os.name == 'nt':
                    try:
                        subprocess.run(f'icacls {new_pem_path} /reset', shell=True, capture_output=True)
                        subprocess.run(f'icacls {new_pem_path} /inheritance:r', shell=True, capture_output=True)
                        subprocess.run(f'icacls {new_pem_path} /grant:r "{os.environ["USERNAME"]}:(R)"', shell=True, capture_output=True)
                    except:
                        pass
                else:
                    os.chmod(new_pem_path, stat.S_IRUSR | stat.S_IWUSR)
                
                print(f"{Colors.GREEN}New keypair created successfully!{Colors.END}")
                print(f"{Colors.GREEN}Key Name: {new_key_name}{Colors.END}")
                print(f"{Colors.GREEN}PEM file: {new_pem_path}{Colors.END}")
        else:
            print(f"{Colors.GREEN}Found local PEM file: {pem_path}{Colors.END}")
            
            # Verify PEM file format
            try:
                with open(pem_path, 'r') as f:
                    content = f.read()
                if "-----BEGIN RSA PRIVATE KEY-----" not in content and "-----BEGIN OPENSSH PRIVATE KEY-----" not in content:
                    print(f"{Colors.YELLOW}PEM file appears to be corrupted. Recreating...{Colors.END}")
                    os.remove(pem_path)
                    
                    cmd = f'aws ec2 create-key-pair --key-name {current_key_name} --region {REGION} --query "KeyMaterial" --output text'
                    key_material, stderr, rc = run_aws_command(cmd)
                    
                    with open(pem_path, 'w') as f:
                        f.write(key_material + '\n')
                    
                    print(f"{Colors.GREEN}PEM file recreated successfully!{Colors.END}")
            except:
                print(f"{Colors.GRAY}Could not verify PEM file format{Colors.END}")
    
    print()
    print(f"{Colors.GREEN}Keypair ready: {current_key_name}{Colors.END}")
    print()
    return pem_path

def show_ssh_instructions(public_ip, pem_path):
    """Display SSH connection instructions"""
    print()
    print(f"{Colors.CYAN}========================================={Colors.END}")
    print(f"{Colors.CYAN}SSH Connection Instructions{Colors.END}")
    print(f"{Colors.CYAN}========================================={Colors.END}")
    print()
    
    print(f"{Colors.WHITE}Your PEM file is located at:{Colors.END}")
    print(f"  {Colors.YELLOW}{pem_path}{Colors.END}")
    print()
    
    print(f"{Colors.WHITE}To connect via SSH:{Colors.END}")
    print(f"  {Colors.GREEN}ssh -i \"{pem_path}\" {SSH_USER}@{public_ip}{Colors.END}")
    print()
    
    print(f"{Colors.YELLOW}If you still get 'invalid format' error, try:{Colors.END}")
    print(f"{Colors.GRAY}  1. Delete the PEM file and run this script again{Colors.END}")
    print(f"{Colors.GRAY}  2. Or manually convert the key format:{Colors.END}")
    print(f"{Colors.GRAY}     ssh-keygen -p -f \"{pem_path}\" -m pem{Colors.END}")
    print()

# ==============================
# AWS HELPERS
# ==============================

def get_instance_state(instance_id):
    """Get EC2 instance state"""
    cmd = f'aws ec2 describe-instances --instance-ids {instance_id} --region {REGION} --query "Reservations[].Instances[].State.Name" --output text'
    output, _, _ = run_aws_command(cmd)
    return output

def get_running_instance():
    """Get running instance with specified tag"""
    cmd = f'aws ec2 describe-instances --filters "Name=instance-state-name,Values=running" "Name=tag:Name,Values={NAME_TAG}" --region {REGION} --query "Reservations[].Instances[0].[InstanceId,PublicIpAddress]" --output text'
    output, _, _ = run_aws_command(cmd)
    if output:
        parts = output.split()
        if len(parts) >= 2:
            return parts[0], parts[1]
    return None, None

def tag_instance(instance_id):
    """Add Name tag to instance"""
    cmd = f'aws ec2 create-tags --resources {instance_id} --region {REGION} --tags Key=Name,Value={NAME_TAG}'
    run_aws_command(cmd, return_output=False)

# ==============================
# SECURITY GROUP FUNCTIONS
# ==============================

def get_security_group(group_name):
    """Get security group ID by name"""
    null_device = '2>nul' if os.name == 'nt' else '2>/dev/null'
    cmd = f'aws ec2 describe-security-groups --group-names {group_name} --region {REGION} --query "SecurityGroups[0].GroupId" --output text {null_device}'
    
    output, _, _ = run_aws_command(cmd)
    if output and output != "None" and output != "":
        return output
    return None

def create_security_group(group_name, description):
    """Create new security group"""
    print(f"{Colors.YELLOW}Creating security group: {group_name}{Colors.END}")
    
    cmd = f'aws ec2 create-security-group --group-name {group_name} --description "{description}" --region {REGION} --query "GroupId" --output text'
    group_id, _, _ = run_aws_command(cmd)
    
    print(f"{Colors.GREEN}Created security group: {group_id}{Colors.END}")
    return group_id

def add_ssh_rule(group_id):
    """Add SSH inbound rule to security group"""
    print(f"{Colors.YELLOW}Adding SSH TCP 22 inbound rule...{Colors.END}")
    
    cmd = f'aws ec2 authorize-security-group-ingress --group-id {group_id} --protocol tcp --port 22 --cidr 0.0.0.0/0 --region {REGION}'
    success = run_aws_command(cmd, return_output=False)
    
    if success:
        print(f"{Colors.GREEN}Added TCP 22 inbound rule from 0.0.0.0/0{Colors.END}")
    else:
        print(f"{Colors.GRAY}Rule may already exist or could not be added{Colors.END}")

def ensure_security_group():
    """Ensure security group exists and has SSH access"""
    print()
    print(f"{Colors.CYAN}========================================={Colors.END}")
    print(f"{Colors.CYAN}Checking EC2 security group...{Colors.END}")
    print(f"{Colors.CYAN}========================================={Colors.END}")
    print()
    
    group_id = get_security_group(SECURITY_GROUP_NAME)
    
    if group_id:
        print(f"{Colors.GREEN}Found existing security group: {group_id}{Colors.END}")
    else:
        group_id = create_security_group(SECURITY_GROUP_NAME, SECURITY_GROUP_DESC)
        add_ssh_rule(group_id)
    
    return group_id

# ==============================
# MAIN
# ==============================

def main():
    global current_key_name
    
    try:
        print(f"{Colors.MAGENTA}========================================={Colors.END}")
        print(f"{Colors.MAGENTA}AWS EC2 Instance Launcher{Colors.END}")
        print(f"{Colors.MAGENTA}========================================={Colors.END}")
        print()

        # Ensure keypair exists
        pem_file_path = ensure_keypair()

        # Ensure security group exists
        security_group_id = ensure_security_group()
        
        print(f"{Colors.CYAN}Using security group: {security_group_id}{Colors.END}")
        print(f"{Colors.CYAN}Using key pair: {current_key_name}{Colors.END}")
        print()

        instance_id, public_ip = get_running_instance()

        if instance_id and public_ip:
            print(f"{Colors.GREEN}Found running instance:{Colors.END}")
            print(f"  {Colors.WHITE}Instance ID: {instance_id}{Colors.END}")
            print(f"  {Colors.WHITE}Public IP: {public_ip}{Colors.END}")
            print(f"  {Colors.WHITE}Key Pair: {current_key_name}{Colors.END}")
            print()
            
            tag_instance(instance_id)
            show_ssh_instructions(public_ip, pem_file_path)
            
            print()
            print(f"{Colors.MAGENTA}========================================={Colors.END}")
            print(f"{Colors.GREEN}EC2 instance is running{Colors.END}")
            print(f"{Colors.GREEN}You can now SSH into the instance{Colors.END}")
            print(f"{Colors.MAGENTA}========================================={Colors.END}")

        else:
            print(f"{Colors.CYAN}Launching new EC2 instance...{Colors.END}")
            print(f"  {Colors.GRAY}AMI: {AMI_ID}{Colors.END}")
            print(f"  {Colors.GRAY}Type: {INSTANCE_TYPE}{Colors.END}")
            print(f"  {Colors.GRAY}Region: {REGION}{Colors.END}")
            print(f"  {Colors.GRAY}Security Group: {security_group_id}{Colors.END}")
            print(f"  {Colors.GRAY}Key Pair: {current_key_name}{Colors.END}")
            print(f"  {Colors.GRAY}PEM File: {pem_file_path}{Colors.END}")
            print()

            cmd = f'aws ec2 run-instances --image-id {AMI_ID} --instance-type {INSTANCE_TYPE} --key-name {current_key_name} --security-group-ids {security_group_id} --count 1 --region {REGION} --query "Instances[0].InstanceId" --output text'
            instance_id, _, rc = run_aws_command(cmd)

            if not instance_id or instance_id == "":
                raise Exception("Failed to create instance. No InstanceId returned.")

            print(f"{Colors.GREEN}Instance created: {instance_id}{Colors.END}")

            print(f"{Colors.YELLOW}Waiting for instance to enter running state...{Colors.END}")
            while True:
                time.sleep(2)
                state = get_instance_state(instance_id)
                print(f"  {Colors.GRAY}State: {state}{Colors.END}")
                
                if state == "terminated" or state == "shutting-down":
                    raise Exception(f"Instance entered {state} state. Check AWS console for details.")
                elif state == "running":
                    break

            print(f"{Colors.GREEN}Instance is running!{Colors.END}")
            tag_instance(instance_id)

            cmd = f'aws ec2 describe-instances --instance-ids {instance_id} --region {REGION} --query "Reservations[].Instances[].PublicIpAddress" --output text'
            public_ip, _, _ = run_aws_command(cmd)

            print(f"{Colors.GREEN}Public IP: {public_ip}{Colors.END}")
            show_ssh_instructions(public_ip, pem_file_path)
            
            print()
            print(f"{Colors.MAGENTA}========================================={Colors.END}")
            print(f"{Colors.GREEN}New EC2 instance created successfully!{Colors.END}")
            print(f"{Colors.GREEN}You can now SSH into the instance{Colors.END}")
            print(f"{Colors.MAGENTA}========================================={Colors.END}")

    except Exception as e:
        print()
        print(f"{Colors.RED}ERROR:{Colors.END}")
        print(f"{Colors.RED}{str(e)}{Colors.END}")
        print()
        print(f"{Colors.RED}Stack Trace:{Colors.END}")
        import traceback
        print(f"{Colors.GRAY}{traceback.format_exc()}{Colors.END}")

# Initialize global variable
current_key_name = DEFAULT_KEY_NAME

if __name__ == "__main__":
    main()
    pause_script()
相关推荐
EveryPossible2 小时前
工作流练习
服务器·python·缓存
一次旅行2 小时前
接口自动化测试模板
数据库·python·pytest
JiL 奥2 小时前
AWS之VPC、子网、路由、网关的关系和区别
云计算·aws
brucelee1862 小时前
AWS IoT Core + Lambda + DynamoDB + Redis 完整教程(Java JDK21)
redis·物联网·aws
Suryxin.2 小时前
从0开始复现nano-vllm「model_runner.py」上半篇之初始化分布式推理环境
人工智能·python·深度学习·机器学习·vllm
春日见2 小时前
commit与fetch
linux·人工智能·算法·机器学习·自动驾驶
奔跑的蜗牛FelixChioa2 小时前
python学习之快速掌握 pandas 数据可视化:「matplotlib+seaborn」极简实战方案
python·pandas·数据可视化
数新网络2 小时前
赋能某跨境智慧物流:基于 AWS Graviton 的全栈数据平台实现 25% 成本节省
java·云计算·aws
摇滚侠2 小时前
测试阿里云深度研究功能,评价一下 CSDN 博主连杰李的内容
阿里云·云计算