AWS Cloudshell一键更换EC2实例Elastic IP(含运营商IP)脚本

合并脚本,自动获取实例列表:

#!/bin/zsh
# ==========================================
# AWS EIP 自动更换工具 (Zsh Native 修复版)
# 修复了 IP 地址带反斜杠的问题
# ==========================================

setopt shwordsplit
setopt no_nomatch
set -euo pipefail

USED_IP_FILE="ip.txt"
touch "$USED_IP_FILE"

REGION="${AWS_REGION:-$(aws configure get region 2>/dev/null || true)}"
if [[ -z "$REGION" ]]; then
  echo "❌ 未检测到 Region。"
  echo "请先运行: export AWS_REGION=us-east-1"
  exit 1
fi

start_keep_alive() {
  (
    while true; do
      sleep 300 
      echo -e "\n\033[90m[防超时守护] $(date +%H:%M:%S) 终端保持活跃中...\033[0m" >&2
      echo -n "按回车键开始换 IP (输入 q 返回)... " >&2
    done
  ) &
  KEEP_ALIVE_PID=$!
  trap "kill $KEEP_ALIVE_PID 2>/dev/null || true" EXIT
}

start_keep_alive

region_from_az() { echo "$1" | cut -d- -f1-3; }
is_wavelength_az() { [[ "$1" == *wlz* ]]; }
normalize_nbg() { [[ "$1" == *a ]] && echo "${1%a}" || echo "$1"; }

# ==========================================
# 普通实例逻辑
# ==========================================
simple_swap_interactive() {
  local INSTANCE_ID="$1"
  local REGION="$2"

  echo
  echo "======================================"
  echo "普通实例 IP 刷新模式"
  echo "Instance: $INSTANCE_ID"
  echo "Region:   $REGION"
  echo "操作:按回车刷 IP,输入 q 返回实例列表"
  echo "======================================"
  echo

  while true; do
    echo -n "按回车键开始换 IP (输入 q 返回)... "
    if ! read -r input; then break; fi
    
    if [[ "$input" == "q" ]]; then
        echo "正在返回实例列表..."
        return 0
    fi

    while true; do
      local CURRENT_ALLOC_ID
      CURRENT_ALLOC_ID=$(aws ec2 describe-addresses --filters "Name=instance-id,Values=$INSTANCE_ID" --query "Addresses[0].AllocationId" --output text --region "$REGION")

      if [[ "$CURRENT_ALLOC_ID" != "None" ]]; then
        local ASSOC_ID
        ASSOC_ID=$(aws ec2 describe-addresses --filters "Name=instance-id,Values=$INSTANCE_ID" --query "Addresses[0].AssociationId" --output text --region "$REGION")
        echo "清理当前 EIP..."
        aws ec2 disassociate-address --association-id "$ASSOC_ID" --region "$REGION"
        aws ec2 release-address --allocation-id "$CURRENT_ALLOC_ID" --region "$REGION"
      fi

      echo "执行 IP 刷新操作..."
      local NEW_ALLOC_ID
      NEW_ALLOC_ID=$(aws ec2 allocate-address --domain vpc --query "AllocationId" --output text --region "$REGION")
      aws ec2 associate-address --instance-id "$INSTANCE_ID" --allocation-id "$NEW_ALLOC_ID" --region "$REGION" >/dev/null

      local ASSOC_NEW
      ASSOC_NEW=$(aws ec2 describe-addresses --allocation-ids "$NEW_ALLOC_ID" --query "Addresses[0].AssociationId" --output text --region "$REGION")
      aws ec2 disassociate-address --association-id "$ASSOC_NEW" --region "$REGION"
      aws ec2 release-address --allocation-id "$NEW_ALLOC_ID" --region "$REGION"

      local NEW_PUBLIC_IP="None"
      while [[ "$NEW_PUBLIC_IP" == "None" ]]; do
          sleep 1
          NEW_PUBLIC_IP=$(aws ec2 describe-instances --instance-ids "$INSTANCE_ID" --query "Reservations[0].Instances[0].PublicIpAddress" --output text --region "$REGION")
      done

      echo "获取到 IP: $NEW_PUBLIC_IP"
      if grep -Fq "$NEW_PUBLIC_IP" "$USED_IP_FILE"; then
        echo ">> IP $NEW_PUBLIC_IP 已使用过,自动重试..."
        echo
        continue 
      fi

      local TIMESTAMP
      TIMESTAMP=$(date "+%Y-%m-%dT%H:%M:%S%z")
      echo "$TIMESTAMP $NEW_PUBLIC_IP" >> "$USED_IP_FILE"
      echo "✅ 已记录:$TIMESTAMP $NEW_PUBLIC_IP"
      echo
      break 
    done
  done
}

# ==========================================
# Wavelength 逻辑 (修正版)
# ==========================================
wavelength_swap_interactive() {
  local INSTANCE_ID="$1"
  local NETWORK_BORDER_GROUP_RAW="$2"
  local NETWORK_BORDER_GROUP REGION
  NETWORK_BORDER_GROUP="$(normalize_nbg "$NETWORK_BORDER_GROUP_RAW")"
  REGION="$(region_from_az "$NETWORK_BORDER_GROUP")"

  echo
  echo "======================================"
  echo "Wavelength 实例循环换运营商 IP 模式"
  echo "Instance: $INSTANCE_ID"
  echo "Region:   $REGION"
  echo "操作:按回车刷 IP,输入 q 返回实例列表"
  echo "======================================"
  echo

  while true; do
    echo -n "按回车键开始换运营商 IP (输入 q 返回)... "
    if ! read -r input; then break; fi
    
    if [[ "$input" == "q" ]]; then
        echo "正在返回实例列表..."
        return 0
    fi

    while true; do
      local CURRENT_ALLOC_ID
      CURRENT_ALLOC_ID=$(aws ec2 describe-addresses --filters "Name=instance-id,Values=$INSTANCE_ID" --query "Addresses[0].AllocationId" --output text --region "$REGION")

      if [[ "$CURRENT_ALLOC_ID" != "None" ]]; then
        local ASSOC_ID
        ASSOC_ID=$(aws ec2 describe-addresses --filters "Name=instance-id,Values=$INSTANCE_ID" --query "Addresses[0].AssociationId" --output text --region "$REGION")
        echo "释放旧 IP..."
        aws ec2 disassociate-address --association-id "$ASSOC_ID" --region "$REGION"
        aws ec2 release-address --allocation-id "$CURRENT_ALLOC_ID" --network-border-group "$NETWORK_BORDER_GROUP" --region "$REGION"
      fi

      echo "分配新 IP..."
      local NEW_ALLOC_ID
      NEW_ALLOC_ID=$(aws ec2 allocate-address --domain vpc --network-border-group "$NETWORK_BORDER_GROUP" --query "AllocationId" --output text --region "$REGION")
      aws ec2 associate-address --instance-id "$INSTANCE_ID" --allocation-id "$NEW_ALLOC_ID" --region "$REGION" >/dev/null

      local ASSOC_NEW
      ASSOC_NEW=$(aws ec2 describe-addresses --allocation-ids "$NEW_ALLOC_ID" --query "Addresses[0].AssociationId" --output text --region "$REGION")
      echo "解绑并释放临时 IP..."
      aws ec2 disassociate-address --association-id "$ASSOC_NEW" --region "$REGION"
      aws ec2 release-address --allocation-id "$NEW_ALLOC_ID" --network-border-group "$NETWORK_BORDER_GROUP" --region "$REGION"

      local PUBLIC_DNS INSTANCE_IP
      PUBLIC_DNS=$(aws ec2 describe-instances --instance-ids "$INSTANCE_ID" --query "Reservations[0].Instances[0].PublicDnsName" --output text --region "$REGION")

      if [[ -n "$PUBLIC_DNS" && "$PUBLIC_DNS" != "None" ]]; then
        local DNS_HOST IP_PART
        DNS_HOST="${PUBLIC_DNS%%.*}"
        IP_PART="${DNS_HOST#ec2-}"
        # 修复:移除转义符,只替换为纯点号
        INSTANCE_IP="${IP_PART//-/.}"
      else
        INSTANCE_IP=$(aws ec2 describe-instances --instance-ids "$INSTANCE_ID" --query "Reservations[0].Instances[0].PublicIpAddress" --output text --region "$REGION")
      fi

      echo "当前运营商 IP → $INSTANCE_IP"

      if grep -Fq "$INSTANCE_IP" "$USED_IP_FILE"; then
        echo ">> IP $INSTANCE_IP 已用过,重试…"
        echo
        continue
      fi

      local TIMESTAMP
      TIMESTAMP=$(date "+%Y-%m-%dT%H:%M:%S%z")
      echo "$TIMESTAMP $INSTANCE_IP" >> "$USED_IP_FILE"
      echo "✅ 已记录:$TIMESTAMP $INSTANCE_IP"
      echo
      break
    done
  done
}

# ==========================================
# 实例选择
# ==========================================
select_instance() {
  local REGION="$1"
  local STATE_FILTER="${2:-running}"

  local FILTER_ARGS=()
  if [[ "$STATE_FILTER" != "all" ]]; then
    FILTER_ARGS+=(--filters "Name=instance-state-name,Values=$STATE_FILTER")
  fi

  local TMP_LIST="/tmp/aws_ec2_list_$$"
  
  aws ec2 describe-instances \
    "${FILTER_ARGS[@]}" \
    --region "$REGION" \
    --query "Reservations[].Instances[].[InstanceId,State.Name,Placement.AvailabilityZone,PublicIpAddress,PrivateIpAddress,Tags[?Key=='Name'].Value|[0]]" \
    --output text | sed '/^$/d' > "$TMP_LIST"

  if [[ ! -s "$TMP_LIST" ]]; then
    echo "❌ 未找到实例(region=$REGION, state=$STATE_FILTER)" >&2
    rm -f "$TMP_LIST"
    exit 1
  fi

  local max_idx
  max_idx=$(wc -l < "$TMP_LIST" | tr -d ' ')

  {
    echo
    echo "======================================"
    echo "自动获取 EC2 实例列表 (Region: $REGION)"
    echo "======================================"
    printf "%-4s %-20s %-10s %-20s %s\n" "No." "InstanceId" "State" "AZ" "PublicIP"
    echo "----------------------------------------------------------------------"
  } >&2

  local idx=0
  local line IID STATE AZ PUB PRI NAME
  while read -r line; do
    idx=$((idx+1))
    read -r IID STATE AZ PUB PRI NAME <<< "$line"
    PUB="${PUB:-None}"
    printf "%-4s %-20s %-10s %-20s %s\n" "$idx" "$IID" "$STATE" "$AZ" "$PUB" >&2
  done < "$TMP_LIST"

  echo "----------------------------------------------------------------------" >&2
  echo >&2

  local choice
  while true; do
    echo -n "请输入要选择的实例编号(1-$max_idx): " >&2
    if ! read -r choice; then exit 1; fi
    
    if [[ "$choice" =~ ^[0-9]+$ ]] && (( choice >= 1 && choice <= max_idx )); then
      local picked
      picked=$(sed -n "${choice}p" "$TMP_LIST")
      read -r IID STATE AZ PUB PRI NAME <<< "$picked"
      printf "%s\t%s\n" "$IID" "$AZ"
      rm -f "$TMP_LIST"
      return 0
    fi
    echo "无效输入:$choice" >&2
  done
}

# ==========================================
# 主程序
# ==========================================

echo -n "实例状态过滤(running/all,默认 running): "
read -r STATE_FILTER
STATE_FILTER="${STATE_FILTER:-running}"

while true; do
  SEL="$(select_instance "$REGION" "$STATE_FILTER")"
  
  INSTANCE_ID="$(echo "$SEL" | cut -f1)"
  AZ="$(echo "$SEL" | cut -f2)"

  echo
  echo "✅ 已选择实例:$INSTANCE_ID"
  echo "   AZ:$AZ"
  echo

  if is_wavelength_az "$AZ"; then
    NBG="$(normalize_nbg "$AZ")"
    wavelength_swap_interactive "$INSTANCE_ID" "$NBG"
  else
    simple_swap_interactive "$INSTANCE_ID" "$REGION"
  fi

  clear
  echo "已返回实例列表,请重新选择..."
done

Next Post Previous Post
No Comment
Add Comment
comment url