特定のIPアドレスが設定されたセキュリティグループルールを指定したIPアドレスに変更するシェルスクリプトを作成してみた

2023.03.14

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

自宅の引っ越しに伴い、インターネット回線が変わるのでセキュリティグループに設定したパブリックIPアドレスを変更したいなと思いAWS CLIを使ったシェルスクリプトを作成しました。

作成したシェルスクリプト

作成したシェルスクリプトは以下になります。
シェルスクリプトは以下のGithubリポジトリにも保存しています。
リポジトリ

#!/bin/bash

#インバウンドルールに該当のIPアドレスが設定されてるセキュリティグループのIDを取得
sg_ids=`aws ec2 describe-security-groups --filters Name=ip-permission.cidr,Values=$1 --query SecurityGroups[].GroupId`
echo -e "対象のセキュリティグループ\n$sg_ids\n"

while :
do

#yesもしくはnoを入力する
  read -p "IPアドレスを変更しますか?(yes/no): " answer

#yesの場合変更を開始する
  if [ "yes" = $answer ] ; then

#セキュリティグループIDの数だけ処理を行うためfor文でループさせる
    for sg_id in $(echo $sg_ids | jq '.[]' | sed -E 's/[\"]//g')
    do

      echo -e "\n$sg_id を変更中"
#セキュリティグループに設定されているルールを取得
      sg_rules=`aws ec2 describe-security-group-rules --filters Name=group-id,Values=$sg_id --query SecurityGroupRules`

#セキュリティグループに設定されているルールの数だけ処理を行うためfor文でループさせる
      for sg_rule_id in $(echo $sg_rules | jq '.[].SecurityGroupRuleId')
      do

#セキュリティグループルールに設定されているIPアドレスを取得
        CidrIpv4=`aws ec2 describe-security-group-rules --filters Name=security-group-rule-id,Values=$sg_rule_id | jq '.SecurityGroupRules[].CidrIpv4' | sed -E 's/[\"]//g'`

#インバウンドルール、アウトバウンドルールか確認するために実行
        sg_rule_type=`aws ec2 describe-security-group-rules --filters Name=security-group-rule-id,Values=$sg_rule_id | jq '.SecurityGroupRules[].IsEgress'`

#該当のIPアドレスであること、インバウンドルールであることをチェックする
        if [ $1 = $CidrIpv4 ] && [ false = $sg_rule_type ] ; then

#セキュリティグループルールに設定するプロトコル、ポート番号を取得
          IpProtocol=`aws ec2 describe-security-group-rules --filters Name=security-group-rule-id,Values=$sg_rule_id | jq '.SecurityGroupRules[].IpProtocol' | sed -E 's/[\"]//g'`
          FromPort=`aws ec2 describe-security-group-rules --filters Name=security-group-rule-id,Values=$sg_rule_id | jq '.SecurityGroupRules[].FromPort'`
          ToPort=`aws ec2 describe-security-group-rules --filters Name=security-group-rule-id,Values=$sg_rule_id | jq '.SecurityGroupRules[].ToPort'`

#IPアドレスの変更を実行
          aws ec2 modify-security-group-rules --group-id $sg_id --security-group-rules SecurityGroupRuleId=$sg_rule_id,SecurityGroupRule={"IpProtocol=$IpProtocol,FromPort=$FromPort,ToPort=$ToPort,CidrIpv4=$2"}
        fi
      done
    done
    echo -e "\nIPアドレスの変更が完了しました"
    break
#noの場合変更しない
  elif [ "no" = $answer ] ; then
    echo -e "\nIPアドレスを変更しませんでした"
    break

#yesもしくはno以外の文字列が入力されたときの処理
  else 
    echo -e "\nyes もしくは no を入力してください\n"
  fi
done

シェルスクリプトの説明

4行目に記載されている以下のコマンドで特定のIPアドレスが設定されたセキュリティグループのID一覧を取得しています。

sg_ids=`aws ec2 describe-security-groups --filters Name=ip-permission.cidr,Values=$1 --query SecurityGroups[].GroupId`

ここで以下のような一覧を取得することができます。
※「xxxxx」となっている部分は仮で記載しています。

[
    "sg-xxxxx",
    "sg-xxxxx",
    "sg-xxxxx",
    "sg-xxxxx",
    "sg-xxxxx",
    "sg-xxxxx"
]

17行目の以下のfor文でセキュリティグループの数だけ処理を実行できるようにしてます。
その際にダブルクォーテーションを外すためにsedコマンドで消しています。

for sg_id in $(echo $sg_ids | jq '.[]' | sed -E 's/[\"]//g')

22行目の以下のコマンドでセキュリティグループに設定されているルール一覧を取得します。

sg_rules=`aws ec2 describe-security-group-rules --filters Name=group-id,Values=$sg_id --query SecurityGroupRules`

実行されると以下のような一覧を取得することができます。

[
    {
        "SecurityGroupRuleId": "sgr-xxxxx",
        "GroupId": "sg-xxxxx",
        "GroupOwnerId": "xxxxx",
        "IsEgress": true,
        "IpProtocol": "-1",
        "FromPort": -1,
        "ToPort": -1,
        "CidrIpv4": "0.0.0.0/0",
        "Tags": []
    },
    {
        "SecurityGroupRuleId": "sgr-xxxxx",
        "GroupId": "sg-xxxxx",
        "GroupOwnerId": "xxxxx",
        "IsEgress": false,
        "IpProtocol": "tcp",
        "FromPort": 443,
        "ToPort": 443,
        "CidrIpv4": "△△.△△.△△.△△/32",
        "Tags": []
    }
]

25行目のfor文でセキュリティグループルールの数だけ処理を実行できるようにしてます。
その際にセキュリティグループルールIDを取得しています。

for sg_rule_id in $(echo $sg_rules | jq '.[].SecurityGroupRuleId')

29行目の以下のコマンドでifの条件に使うセキュリティグループルールに設定されたIPアドレスを取得しています。

CidrIpv4=`aws ec2 describe-security-group-rules --filters Name=security-group-rule-id,Values=$sg_rule_id | jq '.SecurityGroupRules[].CidrIpv4' | sed -E 's/[\"]//g'`

実行されるとdescribe-security-group-rulesの結果からCidrIpv4だけが取得できます。

32行目の以下のコマンドでセキュリティグループルールがインバウンドルールなのかアウトバウンドルールなのかを判断する値を取得します。
IsEgressの値がfalseの場合はインバウンドルールになります。

sg_rule_type=`aws ec2 describe-security-group-rules --filters Name=security-group-rule-id,Values=$sg_rule_id | jq '.SecurityGroupRules[].IsEgress'`

35行目の以下のif文で変更したいIPアドレスであること、インバウンドルールであることをチェックしています。

if [ $1 = $CidrIpv4 ] && [ false = $sg_rule_type ] ; then

38~40行目の以下のコマンドで元の状態のポート番号などを取得しています。

IpProtocol=`aws ec2 describe-security-group-rules --filters Name=security-group-rule-id,Values=$sg_rule_id | jq '.SecurityGroupRules[].IpProtocol' | sed -E 's/[\"]//g'`
FromPort=`aws ec2 describe-security-group-rules --filters Name=security-group-rule-id,Values=$sg_rule_id | jq '.SecurityGroupRules[].FromPort'`
ToPort=`aws ec2 describe-security-group-rules --filters Name=security-group-rule-id,Values=$sg_rule_id | jq '.SecurityGroupRules[].ToPort'`

43行目の以下のコマンドで実際にIPアドレスを変更しています。

aws ec2 modify-security-group-rules --group-id $sg_id --security-group-rules SecurityGroupRuleId=$sg_rule_id,SecurityGroupRule={"IpProtocol=$IpProtocol,FromPort=$FromPort,ToPort=$ToPort,CidrIpv4=$2"}

実行してみた

実行場所はCloudShellになります。
CloudShellの開き方はこちらの公式ドキュメントの方法で開けます。
開いたら以下のコマンドを実行します。

git clone https://github.com/Kobayashi-Riku0226/shellscript.git
cd shellscript
chmod 755 ip_address_change.sh
./ip_address_change.sh 変更前のIPアドレス/32 変更後のIPアドレス/32

実行すると以下の画像のように該当のIPアドレスが設定されたセキュリティグループの一覧表示と変更するかの確認が行われます。

問題が無ければ「yes」と入力してEnterを押すと変更が開始されます。
変更中は以下の画像のようにどのセキュリティグループが変更されているのか表示するようにしています。

変更が完了すると「IPアドレスの変更が完了しました」と表示されます。

改良が必要な可能性があるところ

私のAWSアカウントでは大量のセキュリティグループやセキュリティグループルールが無かったので、AWS CLIの結果にNextTokenが含まれることは無かったのですが環境によっては含まれてきます。
NextTokenが結果に含まれてくる場合はwhile文とif文を駆使してNextTokenが出てこなくなるまで実行する必要があります。

さいごに

for文でセキュリティグループルールを一つ一つチェックしているので実行完了まで時間がかかります。
プレフィックスリストに自宅のIPアドレスを登録しておけば今回のようなシェルスクリプトを作成しないでもよかったのにと途中で後悔していました。
マネージドプレフィックスリストを使用して CIDR ブロックをグループ化する