运行环境:
-
一个AWS可用账号 免费玩AWS-CSDN博客
-
本地已安装配置AWS CLI AWS创建API Access Key-CSDN博客
-
本地已安装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 |
四、执行流程
-
初始化 → 2. 密钥对检查/创建 → 3. 安全组检查/创建 →
-
实例状态检测 → 5. (如有)获取运行实例 / (如无)创建新实例 →
-
等待运行完成 → 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()