こんにちは。23新卒の山本です。 現在OJT研修中です(※執筆当時)
突然ですが、セキュリティグループのインバウンドルールを40件追加して!と頼まれたらどんな作業をイメージしますか?
コンソール画面から1件ずつ登録するのは、非効率的ですよね。
ということで、今回は大量のセキュリティグループをCLIコマンドから一括登録する方法をご紹介します。
- 前提
- csvファイル作成
- csvファイルからJSONファイルを生成
- 生成したJSONファイルを使ってCLIで一括登録
- 別のセキュリティグループやプレフィックスリストをソースに指定したい場合
- csvの内容と登録したインバウンドルールがあっているか確認
- 最後に
前提
以下検証は Python 3.10.12、AWS CLI 2.13.15 を使用しています。
セキュリティグループは、インバウンドルールを空の状態でコンソール画面から作成済みです。
このあとセキュリティグループIDを使うので、控えておきましょう。
またセキュリティグループに追加できるインバウンドルールの デフォルト上限は60件 です。
61件以上登録したい際は、上限緩和申請を行うか、セキュリティグループを2つ以上に分けることを検討してください。
csvファイル作成
まずはインバウンドルール一覧を記載した csvファイルを作成します。
サンプルですが中身はこんな感じ。
※CiderIpは私のデフォルトVPCのPrivate IPの領域です
各項目はコンソール画面からの設定時の以下に該当します。
ポートが1つの場合はFromPortにもToPortにも同じ値を入れ、範囲がある場合はFromPort:3268, ToPort:3269 のように入れます。
また、ICMPなど 0-65535 すべてのポートの場合は FromPort, ToPort どちらにも -1 を指定します。
csvファイルからJSONファイルを生成
続いて先ほど作成した csvファイルからJSONファイルを生成するPythonファイルを作成し、以下のプログラムを記述します。
csv_file_path に csvファイルのパスを、json_file_path に JSONファイルのパスを指定してください。
import csv import json def convert_csv_to_json(csv_filepath, json_filepath): data = [] with open(csv_filepath, mode='r', encoding='utf-8') as csv_file: csv_reader = csv.DictReader(csv_file) for row in csv_reader: ip_permission = { "IpProtocol": row["IpProtocol"], "FromPort": int(row["FromPort"]), "ToPort": int(row["ToPort"]), "IpRanges": [{"CidrIp": row["CidrIp"], "Description": row["Description"]}] } data.append(ip_permission) with open(json_filepath, 'w', encoding='utf-8') as json_file: json.dump(data, json_file, ensure_ascii=False, indent=4) # csvファイルとJSONファイルのパスを指定 csv_file_path = 'xxxxxx.csv' json_file_path = 'xxxxxx.json' # 変換関数の実行 convert_csv_to_json(csv_file_path, json_file_path)
Pythonプログラムを記述したら、実行しJSONファイルが生成されたことを確認します。
python ファイル名
生成されたJSONファイル
[ { "IpProtocol": "icmp", "FromPort": -1, "ToPort": -1, "IpRanges": [ { "CidrIp": "172.31.0.0/16", "Description": "ICMP " } ] }, { "IpProtocol": "tcp", "FromPort": 42, "ToPort": 42, "IpRanges": [ { "CidrIp": "172.31.0.0/16", "Description": "WINS " } ] }, { "IpProtocol": "tcp", "FromPort": 53, "ToPort": 53, "IpRanges": [ { "CidrIp": "172.31.0.0/16", "Description": "DNS " } ] }, : 以下省略
生成したJSONファイルを使ってCLIで一括登録
それでは下準備が整ったのでCLIコマンドで一括登録してみましょう。
sg-xxxxxxxxxxx をセキュリティグループIDに修正し、jsonファイルパスを修正してください。
aws ec2 authorize-security-group-ingress --group-id sg-xxxxxxxxxxx --ip-permissions file://jsonファイルパス
インバウンドルールが追加されているのを確認できました。
別のセキュリティグループやプレフィックスリストをソースに指定したい場合
上記ではソースがCIDRのみの一括登録を紹介しました。
しかしソースはCIDRだけでなく別のセキュリティグループや、プレフィックスリストを指定することもできるので、それらが混在する場合のcsvファイルの中身やPythonプログラムを紹介します。
csvファイル
Type にソースの種類を指定し、Value にCIDRやセキュリティグループID、プレフィックスリストIDを指定します。
Pythonプログラム
上記で作成したcsvファイルのTypeを条件にして、JSONを生成しています。
import csv import json def convert_csv_to_json(csv_file_path, json_file_path): rules = [] with open(csv_file_path, mode='r') as file: csv_reader = csv.DictReader(file) for row in csv_reader: rule = {"IpProtocol": row["IpProtocol"], "FromPort": int(row["FromPort"]), "ToPort": int(row["ToPort"])} if row["Type"] == "CidrIp": rule["IpRanges"] = [{"CidrIp": row["Value"], "Description": row["Description"]}] elif row["Type"] == "PrefixListId": rule["PrefixListIds"] = [{"PrefixListId": row["Value"], "Description": row["Description"]}] elif row["Type"] == "GroupId": rule["UserIdGroupPairs"] = [{"GroupId": row["Value"], "Description": row["Description"]}] rules.append(rule) with open(json_file_path, 'w') as json_file: json.dump(rules, json_file, indent=4) # csvファイルとJSONファイルのパスを指定 csv_file_path = 'xxxxxx.csv' json_file_path = 'xxxxxx.json' # 変換関数の実行 convert_csv_to_json(csv_file_path, json_file_path)
実行方法は先ほど同様、「python ファイル名」でJSONファイルを生成してCLIコマンドを実行すれば、ソースが混在したインバウンドルールも一括登録できます。
csvの内容と登録したインバウンドルールがあっているか確認
まずCLIコマンドでAWS環境のセキュリティグループ情報を取得してJSONファイルに格納します。
sg-xxxxxxxxxxxxxxをセキュリティグループIDに修正して実行してください。
aws ec2 describe-security-groups --group-ids sg-xxxxxxxxxxxxxx > security-group-rules.json
続いて比較するためのPythonプログラムを記述します。
xxxxxx.json は csvファイルから生成したJSONファイルに修正してください。
import json # JSONファイルの読み込み def load_json_file(file_path): with open(file_path, "r") as file: return json.load(file) def compare_rules(csv_json_rules, aws_json_rules): for csv_rule in csv_json_rules: if not any( compare_rule_details(csv_rule, aws_rule) for aws_rule in aws_json_rules ): print(f"CSVファイルにあってAWSルールにないルール: {csv_rule}") for aws_rule in aws_json_rules: if not any( compare_rule_details(aws_rule, csv_rule) for csv_rule in csv_json_rules ): print(f"AWSルールにあってCSVファイルにないルール: {aws_rule}") def compare_rule_details(rule1, rule2): if ( rule1["IpProtocol"] != rule2["IpProtocol"] or rule1["FromPort"] != rule2["FromPort"] or rule1["ToPort"] != rule2["ToPort"] ): return False if "IpRanges" in rule1 and "IpRanges" in rule2: return rule1["IpRanges"] == rule2["IpRanges"] if "UserIdGroupPairs" in rule1 and "UserIdGroupPairs" in rule2: rule1_group_ids = {pair["GroupId"] for pair in rule1["UserIdGroupPairs"]} rule2_group_ids = {pair["GroupId"] for pair in rule2["UserIdGroupPairs"]} return rule1_group_ids == rule2_group_ids if "PrefixListIds" in rule1 and "PrefixListIds" in rule2: return rule1["PrefixListIds"] == rule2["PrefixListIds"] return False # csvから生成したJSONファイル csv_json_rules = load_json_file("xxxxxx.json") # AWSのセキュリティグループのインバウンドルールを取得して生成したJSONファイル aws_json_rules = load_json_file("security-group-rules.json")["SecurityGroups"][0]["IpPermissions"] compare_rules(csv_json_rules, aws_json_rules)
最後に上記プログラムを実行します。
一致していれば何もメッセージは出てきません。不一致がある場合はそのルールが返ってきます。
上記キャプチャはAWS環境からICMPルールの説明を書き換え、WINSルールを削除したうえで実行した結果です。
最後に
今回はCLIコマンドを使ってセキュリティグループのインバウンドルールを一括登録する方法をご紹介しました。
エクセルやcsvファイルでインバウンドルールを並行管理できるので、AWSのコンソール画面に接続するより簡単に確認することができます。
しかし今回の方法では、既に存在するインバウンドルールで実行するとエラーになってしまいます。
重複時はそのルールを避けて登録するプログラムや、csvの内容とAWS環境の内容が合致しているか確認するプログラムも一発で分かりやすく出力するなど、伸びしろありそうなので検証していこうと思います。
山本 瑞貴(執筆記事の一覧)
2023年新卒入社。現在OJT中です!