こんにちは!イーゴリです。
既存の複数のセキュリティグループに複数のルールを追加/削除する場合、手作業で行うと時間がかかるのと、オペミスなどが発生する可能性があるため、CSVファイルに必要なルールを入れてスクリプトを実行すると便利だと思い、下記のスクリプトを作ってみました。
注意点:このスクリプトについては、一切の責任を負えませんので、ご自身で十分にご検証した上で、このスクリプトを使ってもよいかご判断ください。
前提条件
- AWS CLIがインストールされていること
- AWS CLIが設定されていること
- jq (任意)
CSVファイルの作成
「list.csv」のファイルを作成します。
下記の例の通り、「list.csv」に記載します。
1行目から3行目まで記入例を記載します。
4行目からスクリプトを読み込ませる情報を記載します。
4行目からは、余裕を持って「1」から「100」までの項番を作成します。項番の横に必要なSGルール内容を記載します。
ちなみに「Source」の欄にIPを記載する際、毎回「/32」のサブネットマスクを入れるのは手間がかかりますので、CSVに「/32」なしで入力し、スクリプト内に「/32」を入れました。もし別のサブネットマスクを入れる必要がある場合、CSV上で追記します(例:192.168.1.0/24)。
入力するフォーマットは上記の画像の例の通りなので、説明は不要だと思いますが、要点としてはB列に[add]を記載する場合は「ルールの追加」になり、[remove]を記載する場合は「ルールの削除」になります。
なお、この「list.csv」を手順書としても使いたいため、2-3行目を例のために残します。4行目からスクリプトが必要なパラメーターを読み始めます。
スクリプトの作成
existsg.shを作成します。作業内容(インバウンドルールの処理/アウトバウンドルールの処理)によって下記の内容をコピーします。
インバウンドルールの場合
#!/bin/bash # CSVファイルのパス CSV_FILE="$(pwd)/list.csv" # 操作間の遅延(秒単位) DELAY=3 # CSVファイルを4行目から読み込み tail -n +4 "$CSV_FILE" | while IFS=, read -r line_number operation sg_id source_type source protocol port_from port_to description; do # Descriptionから使えない特殊文字を削除 description=$(echo "$description" | tr -cd 'a-zA-Z0-9. _-:/()#,@[]+=&;{}!$*') # 操作が必要ない行をスキップ if [[ -z "$operation" ]]; then continue fi # 操作の定義 if [ "$operation" == "add" ]; then action="authorize-security-group-ingress" action_text="追加" elif [ "$operation" == "remove" ]; then action="revoke-security-group-ingress" action_text="削除" else echo "無効な操作です: $operation" continue fi # Source IPにマスクが含まれているか確認し、ない場合は/32を追加 if [[ "$source_type" == "ip" && "$source" != *"/"* ]]; then source="$source/32" fi # 実行中の操作に関するメッセージを表示 echo "対象SG: $sg_id, $action_text: $source (タイプ: $source_type), $port_from から $port_to までの範囲 (プロトコル: $protocol, 説明: $description) $action_text" # 操作の実行 if [ "$source_type" == "ip" ]; then aws ec2 "$action" --group-id "$sg_id" --ip-permissions IpProtocol="$protocol",FromPort="$port_from",ToPort="$port_to",IpRanges="[{CidrIp='$source',Description='$description'}]" elif [ "$source_type" == "sg" ]; then aws ec2 "$action" --group-id "$sg_id" --ip-permissions IpProtocol="$protocol",FromPort="$port_from",ToPort="$port_to",UserIdGroupPairs="[{GroupId='$source',Description='$description'}]" else echo "無効なソースタイプです: $source_type" continue fi # 操作の完了を確認し、3秒待つ if [ $? -eq 0 ]; then echo "対象SG: $sg_id -> $source を $action_text しました" else echo "対象SG: $sg_id -> $source を $action_text できませんでした" fi sleep $DELAY done
アウトバウンドルールの場合
インバウンドルールと全く同じ内容ですが、「ingress」というキーワードを「egress」に変更しました。
#!/bin/bash # CSVファイルのパス CSV_FILE="$(pwd)/list.csv" # 操作間の遅延(秒単位) DELAY=3 # CSVファイルを4行目から読み込み tail -n +4 "$CSV_FILE" | while IFS=, read -r line_number operation sg_id source_type source protocol port_from port_to description; do # Descriptionから使えない特殊文字を削除 description=$(echo "$description" | tr -cd 'a-zA-Z0-9. _-:/()#,@[]+=&;{}!$*') # 操作が必要ない行をスキップ if [[ -z "$operation" ]]; then continue fi # 操作の定義 if [ "$operation" == "add" ]; then action="authorize-security-group-egress" action_text="追加" elif [ "$operation" == "remove" ]; then action="revoke-security-group-egress" action_text="削除" else echo "無効な操作です: $operation" continue fi # Source IPにマスクが含まれているか確認し、ない場合は/32を追加 if [[ "$source_type" == "ip" && "$source" != *"/"* ]]; then source="$source/32" fi # 実行中の操作に関するメッセージを表示 echo "対象SG: $sg_id, $action_text: $source (タイプ: $source_type), $port_from から $port_to までの範囲 (プロトコル: $protocol, 説明: $description) $action_text" # 操作の実行 if [ "$source_type" == "ip" ]; then aws ec2 "$action" --group-id "$sg_id" --ip-permissions IpProtocol="$protocol",FromPort="$port_from",ToPort="$port_to",IpRanges="[{CidrIp='$source',Description='$description'}]" elif [ "$source_type" == "sg" ]; then aws ec2 "$action" --group-id "$sg_id" --ip-permissions IpProtocol="$protocol",FromPort="$port_from",ToPort="$port_to",UserIdGroupPairs="[{GroupId='$source',Description='$description'}]" else echo "無効なソースタイプです: $source_type" continue fi # 操作の完了を確認し、3秒待つ if [ $? -eq 0 ]; then echo "対象SG: $sg_id -> $source を $action_text しました" else echo "対象SG: $sg_id -> $source を $action_text できませんでした" fi sleep $DELAY done
作業手順
セキュリティグループの変更前の状態を取得
作業を行う前にセキュリティグループのDescribe情報を取得します。
BeforeのDescribe情報を取得する。 $ aws ec2 describe-security-groups --group-ids sg-XXX > before.json
- 「sg-XXX」の代わりに適切な値を入力します。
スクリプトの実行
$ cd <スクリプト及びCSVファイルのディレクトリ> $ sh existsg.sh
セキュリティグループの変更後の状態を取得
BeforeのDescribe情報を取得する。 $ aws ec2 describe-security-groups --group-ids sg-XXX > after.json
- 「sg-XXX」の代わりに適切な値を入力します。
差分確認
jqがインストールされていると、下記のコマンドで差分が分かりやすく表示されます。私はjqを使っていますが、楽な方法で確認してください。
jqがインストールされている場合:
$ diff <(jq . before.json) <(jq . after.json)
after.jsonに差分があったら、下記の例の通り表示されます。
上記のdiffの例の通り、before.json->after.jsonの順番で並べて比較しますと下記の表示になります。
- [>]はAfterにあるが、Beforeにないという意味です。→対象のルールを追加できた
- [<]はBeforeにあるが、Afterにないという意味です。→対象のルールを削除できた
差分結果の例(ルールの追加):
> { > "CidrIp": "192.168.1.1/32", > "Description": "from test1" > }, > { > "CidrIp": "192.168.1.2/32", > "Description": "from test2" > }, > { > "CidrIp": "192.168.1.3/32", > "Description": "from test3"
以上、御一読ありがとうございました。
本田 イーゴリ (記事一覧)
カスタマーサクセス部
・2024 Japan AWS Top Engineers (Security)
・AWS SAP, DOP, SCS, DBS, SAA, DVA, CLF
・Azure AZ-900
・EC-Council CCSE
趣味:日本国内旅行(47都道府県制覇)・ドライブ・音楽