はじめに
「このセキュリティグループ、ほんまに消してええんか...?」
ってことありますよね。
というわけで、そのセキュリティグループが紐づいているサービスをチェックするスクリプトを作りました。
かつて自分で作って使っていたものを生成AIに改善していただきまして、人様に晒しても大丈夫そうなのでこちらに残しておこうと思います。
スクリプト
コードはこちら ↓
クリックして展開
#!/bin/bash
# セキュリティ設定
set -euo pipefail # エラー時即座に停止、未定義変数使用禁止、パイプラインエラー検知
# 第一引数からセキュリティグループIDを取得
SECURITY_GROUP_ID=$1
# 第二引数が指定されていない場合はデフォルトプロファイルを使用
export AWS_PROFILE=${AWS_PROFILE:-default}
export AWS_REGION=${AWS_REGION:-ap-northeast-1}
# ANSIエスケープコードで色を定義
readonly BLUE="\033[0;34m"
readonly YELLOW="\033[1;33m"
readonly GREEN="\033[0;32m"
readonly GRAY="\033[0;90m"
readonly RED="\033[0;31m"
readonly NC="\033[0m" # テキスト色をリセットするためのコード
# エラーメッセージ表示関数
error_exit() {
echo -e "${RED}Error: $1${NC}" >&2
exit 1
}
# 引数チェック
if [[ -z "${SECURITY_GROUP_ID:-}" ]]; then
echo "Usage: $0 <security-group-id>" >&2
exit 1
fi
# セキュリティグループIDの形式検証(インジェクション対策)
if [[ ! "$SECURITY_GROUP_ID" =~ ^sg-[0-9a-f]{8,17}$ ]]; then
error_exit "Invalid Security Group ID format: $SECURITY_GROUP_ID (expected format: sg-xxxxxxxxx)"
fi
# AWS CLIの存在確認
if ! command -v aws &> /dev/null; then
error_exit "AWS CLI is not installed. Please install it first."
fi
# AWS認証チェック(実行前に確認)
if ! aws sts get-caller-identity &> /dev/null; then
error_exit "AWS authentication failed. Please check your credentials and profile."
fi
# セキュリティグループの存在確認
echo -e "${GRAY}Validating security group...${NC}" >&2
if ! aws ec2 describe-security-groups --group-ids "$SECURITY_GROUP_ID" &> /dev/null; then
error_exit "Security group not found: $SECURITY_GROUP_ID"
fi
# 現在のAWSプロファイルとアカウント情報を表示
AWS_ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text 2>/dev/null || echo "unknown")
SG_INFO=$(aws ec2 describe-security-groups --group-ids "$SECURITY_GROUP_ID" --query "SecurityGroups[0].[GroupName,VpcId]" --output text 2>/dev/null)
SG_NAME=$(echo "$SG_INFO" | awk '{print $1}')
VPC_ID=$(echo "$SG_INFO" | awk '{print $2}')
echo ""
echo "=========================================="
echo -e "${GREEN}AWS Account:${NC} $AWS_ACCOUNT_ID"
echo -e "${GREEN}AWS Profile:${NC} $AWS_PROFILE"
echo -e "${GREEN}AWS Region:${NC} $AWS_REGION"
echo -e "${GREEN}Security Group:${NC} $SG_NAME ($SECURITY_GROUP_ID)"
echo -e "${GREEN}VPC:${NC} $VPC_ID"
echo "=========================================="
echo ""
# === 高速チェック(単一APIコール) ===
# Network Interfaces (ENI)の一覧をコンソールに出力
echo -e "${BLUE}Network Interfaces (ENI):${NC}"
result=$(aws ec2 describe-network-interfaces --filters "Name=group-id,Values=$SECURITY_GROUP_ID" --query "NetworkInterfaces[*].[NetworkInterfaceId,Description,Status,Attachment.InstanceId]" --output text 2>/dev/null | awk '{if ($4 && $4 != "None") printf "%s - %s (%s) [Instance: %s]\n", $1, $2, $3, $4; else printf "%s - %s (%s)\n", $1, $2, $3}')
if [[ -z "$result" ]]; then
echo -e "${YELLOW} (No resources found)${NC}"
else
echo "$result"
fi
echo ""
# EC2インスタンスの一覧をコンソールに出力
echo -e "${BLUE}EC2 Instances:${NC}"
result=$(aws ec2 describe-instances --filters "Name=instance.group-id,Values=$SECURITY_GROUP_ID" --query "Reservations[*].Instances[*].[InstanceId,Tags[?Key=='Name'].Value|[0],State.Name]" --output text 2>/dev/null | awk '{if ($2) printf "%s (%s) - %s\n", $1, $2, $3; else printf "%s - %s\n", $1, $3}')
if [[ -z "$result" ]]; then
echo -e "${YELLOW} (No resources found)${NC}"
else
echo "$result"
fi
echo ""
# === ロードバランサー ===
# ALB/NLBの一覧をコンソールに出力
echo -e "${BLUE}Application/Network Load Balancers:${NC}"
result=$(aws elbv2 describe-load-balancers --query "LoadBalancers[?SecurityGroups != null && contains(SecurityGroups, '$SECURITY_GROUP_ID')].[LoadBalancerName,Type]" --output text 2>/dev/null | awk '{printf "%s (%s)\n", $1, $2}')
if [[ -z "$result" ]]; then
echo -e "${YELLOW} (No resources found)${NC}"
else
echo "$result"
fi
echo ""
# CLBの一覧をコンソールに出力
echo -e "${BLUE}Classic Load Balancers:${NC}"
result=$(aws elb describe-load-balancers --query "LoadBalancerDescriptions[?SecurityGroups.contains(@, '$SECURITY_GROUP_ID')].[LoadBalancerName]" --output text 2>/dev/null)
if [[ -z "$result" ]]; then
echo -e "${YELLOW} (No resources found)${NC}"
else
echo "$result"
fi
echo ""
# === データベース・キャッシュ ===
# RDSインスタンスの一覧をコンソールに出力
echo -e "${BLUE}RDS Instances:${NC}"
result=$(aws rds describe-db-instances --query "DBInstances[?VpcSecurityGroups[?VpcSecurityGroupId=='$SECURITY_GROUP_ID']].[DBInstanceIdentifier,Engine]" --output text 2>/dev/null | awk '{printf "%s (%s)\n", $1, $2}')
if [[ -z "$result" ]]; then
echo -e "${YELLOW} (No resources found)${NC}"
else
echo "$result"
fi
echo ""
# Elasticacheクラスターの一覧をコンソールに出力
echo -e "${BLUE}Elasticache Clusters:${NC}"
result=$(aws elasticache describe-cache-clusters --show-cache-node-info --query "CacheClusters[?SecurityGroups[?SecurityGroupId=='$SECURITY_GROUP_ID']].[CacheClusterId,Engine]" --output text 2>/dev/null | awk '{printf "%s (%s)\n", $1, $2}')
if [[ -z "$result" ]]; then
echo -e "${YELLOW} (No resources found)${NC}"
else
echo "$result"
fi
echo ""
# === サーバーレス ===
# AWS Lambda関数の一覧をコンソールに出力
echo -e "${BLUE}AWS Lambda Functions:${NC}"
result=$(aws lambda list-functions --query "Functions[?VpcConfig.SecurityGroupIds != null && contains(VpcConfig.SecurityGroupIds, '$SECURITY_GROUP_ID')].[FunctionName,Runtime]" --output text 2>/dev/null | awk '{printf "%s (%s)\n", $1, $2}')
if [[ -z "$result" ]]; then
echo -e "${YELLOW} (No resources found)${NC}"
else
echo "$result"
fi
echo ""
# === コンテナサービス ===
# ECS Servicesの一覧をコンソールに出力
echo -e "${BLUE}ECS Services:${NC}"
ecs_result=""
clusters=$(aws ecs list-clusters --query "clusterArns[*]" --output text 2>/dev/null)
if [[ -n "$clusters" ]]; then
# クラスター数を取得
cluster_array=($clusters)
total_clusters=${#cluster_array[@]}
current_cluster=0
echo -e "${GRAY} Checking $total_clusters cluster(s)...${NC}" >&2
for cluster_arn in $clusters; do
cluster_name=$(basename "$cluster_arn")
current_cluster=$((current_cluster + 1))
services=$(aws ecs list-services --cluster "$cluster_name" --query "serviceArns[*]" --output text 2>/dev/null)
if [[ -n "$services" ]]; then
# サービス数を取得
service_array=($services)
total_services=${#service_array[@]}
current_service=0
echo -e "${GRAY} [$current_cluster/$total_clusters] Cluster: $cluster_name ($total_services service(s))${NC}" >&2
for service_arn in $services; do
service_name=$(basename "$service_arn")
current_service=$((current_service + 1))
# 進捗をリアルタイム表示(上書き)
printf "\r${GRAY} Checking service $current_service/$total_services: $service_name...${NC}" >&2
sg_check=$(aws ecs describe-services \
--cluster "$cluster_name" \
--services "$service_name" \
--query "services[0].networkConfiguration.awsvpcConfiguration.securityGroups" \
--output text 2>/dev/null | grep -o "$SECURITY_GROUP_ID" || true)
if [[ -n "$sg_check" ]]; then
# マッチした場合は改行して結果を表示
printf "\r${GREEN} ✓ Found: $cluster_name / $service_name${NC}\n" >&2
ecs_result+="$cluster_name / $service_name"$'\n'
fi
done
# 最後の行をクリア
printf "\r%*s\r" 100 "" >&2
else
echo -e "${GRAY} [$current_cluster/$total_clusters] Cluster: $cluster_name (no services)${NC}" >&2
fi
done
fi
if [[ -z "$ecs_result" ]]; then
echo -e "${YELLOW} (No resources found)${NC}"
else
echo "$ecs_result"
fi
echo ""
# === 検索・分析サービス ===
# Amazon OpenSearch Serviceドメインの一覧をコンソールに出力
echo -e "${BLUE}Amazon OpenSearch Service Domains:${NC}"
opensearch_result=$(aws es list-domain-names --query "DomainNames[].DomainName" --output text 2>/dev/null | tr '\t' '\n' | \
while read domainName; do
if [[ -n "$domainName" ]]; then
sg_ids=$(aws es describe-elasticsearch-domain --domain-name "$domainName" --query "DomainStatus.VPCOptions.SecurityGroupIds" --output text 2>/dev/null)
if echo "$sg_ids" | grep -q "$SECURITY_GROUP_ID"; then
echo "$domainName"
fi
fi
done)
if [[ -z "$opensearch_result" ]]; then
echo -e "${YELLOW} (No resources found)${NC}"
else
echo "$opensearch_result"
fi
echo ""
echo "=========================================="
echo ""
echo -e "${GREEN}✓ Resource fetching complete${NC}"
実行結果
さいごに
セキュリティグループに紐づくサービスが分かれば、判断しやすくなります。
これでセキュリティグループの年末の大掃除も捗ることでしょう。
ただし、自己責任でお願いします。
