AWSの使われていないセキュリティグループを一覧取得する方法
AWS(Amazon Web Services)では、セキュリティグループ(SG)はネットワークトラフィックを制御するための仮想ファイアウォールとして機能します。セキュリティグループは、EC2インスタンスや他のAWSリソースに関連付けられ、許可されたトラフィックを定義します。しかし、長期間使用されていないセキュリティグループが放置されることが多く、無駄なリソースとして管理が必要です。このブログ記事では、AWS CLIを使用して使われていないセキュリティグループを一覧取得し、使用中のセキュリティグループの関連情報も確認する方法を解説します。
用語解説
- セキュリティグループ(SG): AWSリソースへのネットワークトラフィックを制御するための仮想ファイアウォール。インバウンド(受信)およびアウトバウンド(送信)のトラフィックルールを設定する。
- EC2インスタンス: AWSのElastic Compute Cloudサービスを利用した仮想サーバー。
- ENI(Elastic Network Interface): AWS VPC内でネットワークインターフェースを作成・管理するためのリソース。
- RDS(Relational Database Service): AWSのマネージドリレーショナルデータベースサービス。
- Redshift: AWSのデータウェアハウスサービス。
- ElastiCache: AWSのインメモリデータストアサービス。
- Lambda: サーバーレスコンピューティングを実現するAWSのサービス。
前提条件
この手順を実行するには、以下の前提条件が必要です。AWSのcloudshellを利用する場合は、すでにAWS CLI、認証情報が設定済みなので便利です。
- AWS CLI: AWS CLIがインストールされていること。
- AWS認証情報の設定: AWSアカウントにアクセスするための認証情報(アクセスキーとシークレットキー)が設定されていること。
使われていないセキュリティグループの判断基準
使われていないセキュリティグループを特定するための基準は以下の通りです。
- EC2インスタンスで未使用: セキュリティグループがEC2インスタンスに関連付けられていない場合、そのセキュリティグループは未使用とみなされます。
- ENIで未使用: セキュリティグループがENIに関連付けられていない場合も、未使用と見なすことができます。
- RDSインスタンスで未使用: Amazon RDSでもセキュリティグループが使用されるため、関連付けられていないセキュリティグループは未使用とみなされます。
- 他のAWSサービスでの使用: Amazon RedshiftやElastiCache、Lambdaなどでも使用されるため、これらのリソースに関連付けられていないセキュリティグループも未使用です。
使われていないセキュリティグループの一覧取得手順
セキュリティグループの一覧取得
全てのセキュリティグループを取得するために、以下のAWS CLIコマンドを使用します。
aws ec2 describe-security-groups --query 'SecurityGroups[*].{ID:GroupId,Name:GroupName}' --output table
このコマンドを実行することで、セキュリティグループのIDと名前の一覧がテーブル形式で表示されます。
使用中のセキュリティグループの確認
次に、使用中のセキュリティグループがどのリソースに関連付けられているかを確認します。以下のコマンドを実行します。
EC2インスタンスのセキュリティグループ取得
aws ec2 describe-instances --query 'Reservations[*].Instances[*].{ID:InstanceId,SecurityGroups:SecurityGroups[*].GroupId}' --output table
ENIのセキュリティグループ取得
aws ec2 describe-network-interfaces --query 'NetworkInterfaces[*].{ID:NetworkInterfaceId,Groups:Groups[*].GroupId}' --output table
RDSインスタンスのセキュリティグループ取得
aws rds describe-db-instances --query 'DBInstances[*].{ID:DBInstanceIdentifier,SecurityGroups:VpcSecurityGroups[*].VpcSecurityGroupId}' --output table
Redshiftクラスタのセキュリティグループ取得
aws redshift describe-clusters --query 'Clusters[*].{ID:ClusterIdentifier,SecurityGroups:VpcSecurityGroups[*].VpcSecurityGroupId}' --output table
ElastiCacheのセキュリティグループ取得
aws elasticache describe-cache-clusters --query 'CacheClusters[*].{ID:CacheClusterId,SecurityGroups:VpcSecurityGroupIds}' --output table
Lambdaのセキュリティグループ取得
aws lambda list-functions --query 'Functions[*].{Name:FunctionName,SecurityGroups:VpcConfig.SecurityGroupIds}' --output table
これらのコマンドを実行することで、各リソースに関連付けられているセキュリティグループの情報を得ることができます。
未使用のセキュリティグループの抽出
次に、全てのセキュリティグループと比較して未使用のセキュリティグループを特定します。以下のシェルスクリプトを使用して、未使用のセキュリティグループを一覧表示します。ただ、Auto Scalingなど一時的に使ってないセキュリティグループを削除しないようにご注意ください。
#!/bin/bash
# 引数からリージョンを取得、指定がなければデフォルトでap-northeast-1を使用
REGION="${1:-ap-northeast-1}"
# ログファイルの設定
LOG_FILE="security_groups_report.log"
echo "セキュリティグループレポート (リージョン: $REGION)" > "$LOG_FILE"
echo "=====================" >> "$LOG_FILE"
# セキュリティグループの一覧を取得
if ! SECURITY_GROUPS=$(aws ec2 describe-security-groups --region "$REGION" --query "SecurityGroups[*].[GroupId, GroupName]" --output text); then
echo "エラー: セキュリティグループの取得に失敗しました。" | tee -a "$LOG_FILE"
exit 1
fi
# 使用中のセキュリティグループを保存するための配列
declare -A USED_SECURITY_GROUPS
# 全セキュリティグループのカウント
TOTAL_SECURITY_GROUPS=$(echo "$SECURITY_GROUPS" | wc -l)
# 各セキュリティグループに関連付けられているリソースを取得
while read -r GROUP_ID GROUP_NAME; do
echo "----------------------------------------" | tee -a "$LOG_FILE"
echo "セキュリティグループ ID: $GROUP_ID, 名前: $GROUP_NAME" | tee -a "$LOG_FILE"
# EC2インスタンスのリストを取得
if EC2_INSTANCES=$(aws ec2 describe-instances --region "$REGION" --filters "Name=instance.group-id,Values=$GROUP_ID" --query "Reservations[*].Instances[*].[InstanceId, Tags[?Key=='Name'].Value | [0]]" --output text); then
if [ -n "$EC2_INSTANCES" ]; then
echo " このセキュリティグループを使用しているEC2インスタンス:" | tee -a "$LOG_FILE"
echo "$EC2_INSTANCES" | while read -r INSTANCE_ID INSTANCE_NAME; do
echo " インスタンス ID: $INSTANCE_ID, 名前: $INSTANCE_NAME" | tee -a "$LOG_FILE"
done
USED_SECURITY_GROUPS["$GROUP_ID"]=1 # 使用中のSGを記録
else
echo " このセキュリティグループを使用しているEC2インスタンスはありません。" | tee -a "$LOG_FILE"
fi
else
echo " エラー: EC2インスタンスの取得に失敗しました。" | tee -a "$LOG_FILE"
fi
# ELBのリストを取得
if LOAD_BALANCERS=$(aws elbv2 describe-load-balancers --region "$REGION" --query "LoadBalancers[?contains(SecurityGroups, \`$GROUP_ID\`)].[LoadBalancerName]" --output text); then
if [ -n "$LOAD_BALANCERS" ]; then
echo " このセキュリティグループを使用しているELB:" | tee -a "$LOG_FILE"
echo "$LOAD_BALANCERS" | while read -r LOAD_BALANCER_NAME; do
echo " ELB 名: $LOAD_BALANCER_NAME" | tee -a "$LOG_FILE"
done
USED_SECURITY_GROUPS["$GROUP_ID"]=1 # 使用中のSGを記録
else
echo " このセキュリティグループを使用しているELBはありません。" | tee -a "$LOG_FILE"
fi
else
echo " エラー: ELBの取得に失敗しました。" | tee -a "$LOG_FILE"
fi
# RDSのリストを取得
if RDS_INSTANCES=$(aws rds describe-db-instances --region "$REGION" --query "DBInstances[?contains(VpcSecurityGroups[*].VpcSecurityGroupId, \`$GROUP_ID\`)].[DBInstanceIdentifier, DBInstanceClass]" --output text); then
if [ -n "$RDS_INSTANCES" ]; then
echo " このセキュリティグループを使用しているRDSインスタンス:" | tee -a "$LOG_FILE"
echo "$RDS_INSTANCES" | while read -r DB_INSTANCE_IDENTIFIER DB_INSTANCE_CLASS; do
echo " RDS インスタンス ID: $DB_INSTANCE_IDENTIFIER, クラス: $DB_INSTANCE_CLASS" | tee -a "$LOG_FILE"
done
USED_SECURITY_GROUPS["$GROUP_ID"]=1 # 使用中のSGを記録
else
echo " このセキュリティグループを使用しているRDSインスタンスはありません。" | tee -a "$LOG_FILE"
fi
else
echo " エラー: RDSインスタンスの取得に失敗しました。" | tee -a "$LOG_FILE"
fi
echo "" | tee -a "$LOG_FILE"
done <<< "$SECURITY_GROUPS"
# 全セキュリティグループのリストを表示
echo "----------------------------------------" | tee -a "$LOG_FILE"
echo "全セキュリティグループの一覧:" | tee -a "$LOG_FILE"
while read -r GROUP_ID GROUP_NAME; do
echo " セキュリティグループ ID: $GROUP_ID, 名前: $GROUP_NAME" | tee -a "$LOG_FILE"
done <<< "$SECURITY_GROUPS"
# 未使用のセキュリティグループを表示
echo "----------------------------------------" | tee -a "$LOG_FILE"
echo "未使用のセキュリティグループ:" | tee -a "$LOG_FILE"
UNUSED_SECURITY_GROUPS_COUNT=0
while read -r GROUP_ID GROUP_NAME; do
if [[ -z ${USED_SECURITY_GROUPS["$GROUP_ID"]} ]]; then
echo " セキュリティグループ ID: $GROUP_ID, 名前: $GROUP_NAME" | tee -a "$LOG_FILE"
((UNUSED_SECURITY_GROUPS_COUNT++))
fi
done <<< "$SECURITY_GROUPS"
# 結果の出力
echo "----------------------------------------" | tee -a "$LOG_FILE"
echo "全セキュリティグループの件数: $TOTAL_SECURITY_GROUPS" | tee -a "$LOG_FILE"
echo "未使用のセキュリティグループの件数: $UNUSED_SECURITY_GROUPS_COUNT" | tee -a "$LOG_FILE"
echo "レポートは $LOG_FILE に保存されました。"
このスクリプトを実行すると、使用中のセキュリティグループとその関連リソース、さらに未使用のセキュリティグループが表示されます。必要に応じて、スクリプトをカスタマイズして特定のリソースをチェックすることも可能です。
自動化の例:スクリプトでの処理
上記のシェルスクリプトを定期的に実行することで、使われていないセキュリティグループをリストアップできます。これを定期的に実行することで、環境をクリーンに保つことが可能です。
Lambdaを使った自動化
AWS LambdaとCloudWatchを使用すれば、このスクリプトをAWS環境で定期的に実行させることもできます。CloudWatchイベントをトリガーとしてLambda関数を実行し、未使用のセキュリティグループを定期的に確認することができます。
未使用のセキュリティグループの削除コマンド
未使用のセキュリティグループを削除するには、次のコマンドを使用します。<security-group-id>
を削除したいセキュリティグループのIDに置き換えてください。削除前に本当に不要かどうか、ご確認ください。
aws ec2 delete-security-group --group-id <security-group-id>
一括削除のスクリプト
未使用のセキュリティグループが多数ある場合は、以下のスクリプトを使用して一括削除することができます。
#!/bin/bash # エラーメッセージを出力する関数 error_exit() { echo "$1" exit 1 } # 全てのセキュリティグループのIDを取得 all_sgs=$(aws ec2 describe-security-groups --query 'SecurityGroups[*].GroupId' --output text) || error_exit "セキュリティグループの取得中にエラーが発生しました。" # 使用中のセキュリティグループのIDを取得 used_sgs=$(aws ec2 describe-instances --query 'Reservations[*].Instances[*].SecurityGroups[*].GroupId' --output text) || error_exit "使用中のセキュリティグループの取得中にエラーが発生しました。" # その他のリソースからも使用中のセキュリティグループを追加取得 used_sgs+=" $(aws ec2 describe-network-interfaces --query 'NetworkInterfaces[*].Groups[*].GroupId' --output text) || error_exit "ネットワークインターフェースの取得中にエラーが発生しました。" used_sgs+=" $(aws rds describe-db-instances --query 'DBInstances[*].VpcSecurityGroups[*].VpcSecurityGroupId' --output text) || error_exit "RDSインスタンスの取得中にエラーが発生しました。" used_sgs+=" $(aws redshift describe-clusters --query 'Clusters[*].VpcSecurityGroups[*].VpcSecurityGroupId' --output text) || error_exit "Redshiftクラスタの取得中にエラーが発生しました。" used_sgs+=" $(aws elasticache describe-cache-clusters --query 'CacheClusters[*].[CacheClusterId,VpcSecurityGroupIds]' --output text) || error_exit "ElastiCacheの取得中にエラーが発生しました。" used_sgs+=" $(aws lambda list-functions --query "Functions[?VpcConfig!=null].[FunctionName, VpcConfig.SecurityGroupIds]" --output text) || error_exit "Lambda関数の取得中にエラーが発生しました。" # 使用中のセキュリティグループのリストをユニークなものに整形 used_sgs=$(echo "$used_sgs" | tr ' ' '\n' | sort -u) # 未使用のセキュリティグループを抽出 unused_sgs=$(comm -23 <(echo "$all_sgs" | tr ' ' '\n' | sort) <(echo "$used_sgs")) # 結果を表示 echo "使用中のセキュリティグループとその関連リソース:" echo "-----------------------------------------------------" if [ -z "$used_sgs" ]; then echo "使用中のセキュリティグループはありません。" else for sg in $used_sgs; do echo "セキュリティグループ ID: $sg" echo "関連リソース:" # EC2インスタンスを表示 echo "EC2インスタンス:" ec2_instances=$(aws ec2 describe-instances --filters "Name=instance.group-id,Values=$sg" --query 'Reservations[*].Instances[*].InstanceId' --output text) || error_exit "EC2インスタンスの取得中にエラーが発生しました。" if [ -n "$ec2_instances" ]; then echo "$ec2_instances" else echo " なし" fi # ネットワークインターフェースを表示 echo "ネットワークインターフェース (ENI):" eni_ids=$(aws ec2 describe-network-interfaces --filters "Name=group-id,Values=$sg" --query 'NetworkInterfaces[*].NetworkInterfaceId' --output text) || error_exit "ENIの取得中にエラーが発生しました。" if [ -n "$eni_ids" ]; then echo "$eni_ids" else echo " なし" fi # RDSインスタンスを表示 echo "RDSインスタンス:" rds_instances=$(aws rds describe-db-instances --query 'DBInstances[?VpcSecurityGroups[?VpcSecurityGroupId==`'$sg'`]].{ID:DBInstanceIdentifier, SecurityGroups:VpcSecurityGroups[*].VpcSecurityGroupId}' --output table) || error_exit "RDSインスタンスの取得中にエラーが発生しました。" if [ -n "$rds_instances" ]; then echo "$rds_instances" else echo " なし" fi # Redshiftインスタンスを表示 echo "Redshiftクラスタ:" redshift_clusters=$(aws redshift describe-clusters --query 'Clusters[*].[ClusterIdentifier,VpcSecurityGroups[*].VpcSecurityGroupId]' --output text | grep "$sg") || error_exit "Redshiftクラスタの取得中にエラーが発生しました。" if [ -n "$redshift_clusters" ]; then echo "$redshift_clusters" else echo " なし" fi # ElastiCacheインスタンスを表示 echo "ElastiCacheインスタンス:" elasticache_clusters=$(aws elasticache describe-cache-clusters --query 'CacheClusters[*].[CacheClusterId,VpcSecurityGroupIds]' --output text) || error_exit "ElastiCacheの取得中にエラーが発生しました。" if [ -n "$elasticache_clusters" ]; then echo "$elasticache_clusters" else echo " なし" fi # Lambda関数を表示 echo "Lambda関数:" lambda_functions=$(aws lambda list-functions --query "Functions[?VpcConfig!=null].[FunctionName, VpcConfig.SecurityGroupIds]" --output text) || error_exit "Lambda関数の取得中にエラーが発生しました。" if [ -n "$lambda_functions" ]; then echo "$lambda_functions" else echo " なし" fi echo "-----------------------------------------------------" done fi # 未使用のセキュリティグループの表示 echo "" if [ -z "$unused_sgs" ]; then echo "未使用のセキュリティグループはありません。" else echo "未使用のセキュリティグループ:" echo "$unused_sgs" fi
まとめ
AWS環境を最適化し、無駄なリソースを削減するためには、使われていないセキュリティグループを定期的に確認し、削除することが重要です。AWS CLIを使用して、使用中のセキュリティグループがどのリソースに関連付けられているかを確認し、未使用のセキュリティグループを特定する方法を紹介しました。このプロセスを自動化することで、管理作業を効率化し、より安全な環境を維持できます。
AWS Lambdaで使われていないセキュリティグループ(SG)を自動削除する方法:完全ガイド
AWSCLIでの個別の削除方法を解説しました。lambda(条件自動実行)でセキュリティグループ(SG)を自動削除する方法は別記事のこちらで紹介いたします。