Python × AWS CLIで「AWSパラメーターシート自動生成ツール」を作ってみた

こんにちは、SCSKの齋藤です。
本記事では、AWSのパラメータシート作成を自動化するツールのバックエンド実装(Python)について解説します。

1.はじめに

本アプリの全体像や要件定義については、すでに以下の記事で紹介されています。

「アプリ概要編」でも触れられている通り、AWSの設計書やパラメータシートを手作業でExcelにまとめる作業は、非常に時間がかかりミスも起きやすい工程です。
そこで私たちは、AWS CLIの実行結果をExcelに自動変換するツールを開発しました。

本記事では、このツールの心臓部となるPythonによるバックエンド実装にフォーカスして、どうやって「リソースごとのバラバラなJSON構造」を「統一されたExcelフォーマット」に落とし込んだのか、その技術的な裏側をご紹介します。

 

2.作ったもの(バックエンド)

AWSパラメータシート自動生成エンジン

AWS CLIでリソース情報を取得し、Excelファイルに加工・出力するPythonアプリケーションです。

【処理フロー】
AWS CLI実行 → JSON取得 → 構造解析・整形 → Excel出力

Webフロントエンドからのリクエスト(リソースID等)を受け取り、以下の処理を自動で行います。

  • リソース別処理: mapping.yamlの設定に基づき、リソースごとに最適なコマンドを発行
  • データ整形: ネストされたJSONデータをExcelで見やすい形式に平坦化・展開
  • Excel生成: 表紙・改訂履歴・各リソースごとのシートを出力

※なお、Web画面(フロントエンド)の実装については、次回の記事で詳しく解説されます。

 

3.システム構成

開発期間

だいたい 1ヶ月半 くらいかけて作りました。

最初の2週間は基本的な機能を作って、残りの期間でリファクタリングや機能追加をしていった感じです。本業の合間に少しずつ進めていたので、実際の作業時間としては30~40時間くらいだと思います。

開発環境

  • マシン: Ubuntu (WSL2)
  • エディタ: VS Code + AI
  • 言語: Python 3.12
  • パッケージ管理: uv

VS CodeのAIコード補完には本当に助けられました。「こういう処理を書きたい」と思ったときに、コメントを書くだけでコードを提案してくれるので、Python初心者の私でもなんとか書き進めることができました。

全体の流れ

main.py(エントリーポイント)

Step 1: AWS CLI実行 → Step 2: JSON収集 → Step 3: Excel生成

ディレクトリ構成

generate_parameter_sheet/
├── config/
│   ├── mapping.yaml      # ← ここで取得するリソースを定義
│   └── request.json      # ← 取得対象のリソースID
├── src/
│   ├── main.py           # エントリーポイント
│   ├── aws_executor.py   # AWS CLI実行
│   ├── data_processor.py # データ変換
│   ├── excel_generator.py# Excel生成
│   └── excel_formatter.py# スタイル適用
├── input/                # AWS実行結果(JSON)
└── output/               # 出力Excel

4.使用技術

項目 技術
言語 Python 3.12
パッケージ管理 uv
Excel操作 openpyxl
設定ファイル YAML
AWS操作 AWS CLI

なぜこの構成にしたか

  • Python: 学習コストが低く、ライブラリが豊富。ネットで調べると情報がたくさん出てくる
  • uv: 依存関係管理が簡単(uv sync一発)。pipより速くて気持ちいい
  • openpyxl: PythonでExcelを扱う定番ライブラリ。ドキュメントも充実している
  • YAML: 設定ファイルとして読みやすい。JSONより人間に優しい

正直、最初は「とりあえず動けばいいや」くらいの気持ちで技術選定しました。でも結果的に、この構成は初心者にも扱いやすくて正解だったと思います。

 

5.対応しているAWSリソース

現在、以下のリソースなどに対応しています:

EC2 / VPC / ELBv2 / RDS / Route53 / S3 / ACM / CloudWatch など

各リソースでどのようなAWS CLIコマンドを使用しているか、シートを分割するかまとめるか、複数コマンドを結合するかなどの詳細な要件については、以下の記事をご覧ください:

要件定義編:各AWSリソースの取得ルールとシート分割設計

出力例

consolidated_aws_resources.xlsx
├── 表紙
├── 改訂履歴
├── vpcs                      # VPC情報
├── web-server-01             # EC2(Tags.Nameから)
├── batch-server              # EC2(Tags.Nameから)
├── prod-alb                  # ELB(ロードバランサー名から)
├── example.com               # Route53(ドメイン名から)
├── prod-database             # RDS(DBインスタンス名から)
└── ...

 

6.工夫した点

汎用化への挑戦

このツールを作る上で一番大変だったのが、リソースごとに要件がバラバラだったことです。

【リソースごとの違い】

  • EC2: 複数インスタンス → 各インスタンスごとにシート分割
  • VPC: 複数VPC → 1つのシートにまとめて出力
  • ELB: ALB + ターゲットグループ + 属性 → 結合して1シート
  • RDS: DB + サブネット + パラメータ → 結合して1シート
  • Route53: ホストゾーン + レコード → 結合して1シート

同じ「AWSリソースをExcelに出力する」という処理なのに、

  • シート分割の単位が違う(リソースごとに分ける or まとめる)
  • 複数のAWS CLIコマンドを結合する必要がある(ELBやRDS)
  • JSON構造が全然違う(EC2はReservations[].Instances[]、RDSはDBInstances[]
  • シート名に使うフィールドが違う(EC2はTags.Name、RDSはDBInstanceIdentifier

これらをすべてコードで分岐させていたら、メンテナンス不可能になっていたと思います。

そこで、mapping.yamlという設定ファイルですべての違いを吸収できる設計にしました。

設定ファイルによる制御

mapping.yamlに設定を追加するだけで、新しいAWSリソースに対応できるようにしました。

mapping.yamlの主要オプション:

オプション 説明
data_structure JSONのどの階層からデータを抽出するか(例:Reservations[].Instances[]
sheet_name_field シート名として使用するフィールド(例:Tags.Name
merge_subfolder_files サブフォルダのJSONファイルを結合するかどうか
json_transform 文字列フィールドをJSONとしてパースするなどの変換処理
allow_batch 複数リソースをまとめて取得するかどうか
# 例:RDSの設定
rds_db_instances:
  description: "RDS DBインスタンス情報取得"
  allow_batch: false
  merge_subfolder_files: true
  sheet_name_field: "DBInstanceIdentifier"  # ← シート名に使うフィールド
  commands:
    - command: "aws rds describe-db-instances --db-instance-identifier {values}"
      is_main_data: true
    - command: "aws rds describe-db-subnet-groups ..."
      merge_into_main: "DBSubnetGroups"

コードを書かなくても、YAMLを編集するだけで拡張できるのがポイントです。

新しいリソースを追加するときも、既存の設定をコピーして少し変えるだけ。最初からこの設計にしていればよかったのですが、途中で気づいてリファクタリングしました。

シート名の自動決定

各リソースのシート名を、意味のある名前で自動設定するようにしました。

リソース シート名の決定方法
EC2 Tags.Name(例:web-server-01)
ELB LoadBalancerName(例:prod-alb)
RDS DBInstanceIdentifier(例:prod-database)
Route53 HostedZone.Name(例:example.com)

 

7.苦労した点

JSONの構造がリソースごとに違う

EC2はReservations[].Instances[]という構造、RDSはDBInstances[]という構造…と、AWSのレスポンス形式がバラバラで苦労しました。

解決策: mapping.yamldata_structureオプションを追加し、設定で対応できるようにしました。

ec2_instances:
  data_structure: "Reservations[].Instances[]"  # ← 構造を定義

このオプションを使って、JSONから必要なデータを抽出する処理を実装しました:

def extract_data_by_structure(json_data, structure):
    """data_structure設定に基づいてJSONからデータを抽出"""
    if not structure:
        return json_data
    
    # "Reservations[].Instances[]" のような形式をパース
    parts = structure.replace('[]', '').split('.')
    
    result = json_data
    for part in parts:
        if isinstance(result, list):
            # リストの場合は各要素から抽出
            result = [item.get(part, []) for item in result]
            # ネストしたリストをフラット化
            result = [item for sublist in result for item in 
                     (sublist if isinstance(sublist, list) else [sublist])]
        elif isinstance(result, dict):
            result = result.get(part, [])
    
    return result

これにより、どんな構造のJSONでも設定ファイルに書くだけで対応できるようになりました。

複数コマンドの結果を結合する処理

RDSの場合、DBインスタンス情報だけでなく、サブネットグループやパラメータグループの情報も一緒に取得して結合する必要がありました。

# mapping.yamlでの設定例
rds_db_instances:
  commands:
    - command: "aws rds describe-db-instances ..."
      is_main_data: true                    # メインのデータ
    - command: "aws rds describe-db-subnet-groups ..."
      merge_into_main: "DBSubnetGroup"      # メインに結合
    - command: "aws rds describe-db-parameter-groups ..."
      merge_into_main: "DBParameterGroups"  # メインに結合

結合処理のロジックは以下のようになっています:

def merge_subcommand_results(main_data, sub_data, merge_key):
    """サブコマンドの結果をメインデータに結合"""
    if isinstance(main_data, dict):
        main_data[merge_key] = sub_data
    elif isinstance(main_data, list):
        # リストの場合は各要素に結合
        for item in main_data:
            if isinstance(item, dict):
                item[merge_key] = sub_data
    return main_data

Excelシート名の31文字制限

Excelのシート名は31文字までという制限があり、長いリソース名が切れてしまう問題がありました。

解決策: シート名を自動で31文字以内に切り詰める処理を追加しました。また、[]などExcelで使えない文字も自動で置換するようにしています。

def sanitize_sheet_name(name, max_length=31):
    """Excelシート名として使用できる形式に変換"""
    # 使用できない文字を置換
    invalid_chars = ['\\', '/', '*', '?', ':', '[', ']']
    for char in invalid_chars:
        name = name.replace(char, '_')
    
    # 長さを制限
    if len(name) > max_length:
        name = name[:max_length-3] + '...'
    
    return name

シート名の動的決定

EC2はTags.Name、RDSはDBInstanceIdentifierというように、リソースごとにシート名として使いたいフィールドが異なります。これをmapping.yamlsheet_name_fieldで指定できるようにしました。

def get_sheet_name(data, config):
    """設定に基づいてシート名を決定"""
    sheet_name_field = config.get('sheet_name_field')
    fallback = config.get('sheet_name_fallback', 'unknown')
    
    if not sheet_name_field:
        return fallback
    
    # ネストしたフィールドにも対応(例:Tags.Name)
    value = data
    for key in sheet_name_field.split('.'):
        if isinstance(value, dict):
            value = value.get(key)
        elif isinstance(value, list):
            # Tags配列から特定のKeyを探す場合
            for item in value:
                if item.get('Key') == key:
                    value = item.get('Value')
                    break
        else:
            break
    
    return sanitize_sheet_name(str(value)) if value else fallback

8.学んだこと

プログラミングは「問題解決の手段」

最初は「Pythonを勉強しよう」と思っていましたが、実際に作ってみると「この問題を解決したい」→「そのためにはこう書けばいい」という流れで学習が進みました。

目的があると、学習効率が全然違いますね。「Python入門」みたいな本を読むより、実際に何か作りながら学ぶ方が頭に入ってくる気がします。

設計の重要性

最初は場当たり的にコードを書いていましたが、途中で「これじゃ拡張できない…」と気づき、設定ファイル(YAML)で制御する形にリファクタリングしました。

最初からちゃんと設計していれば…という反省があります。

実際、後半は「新しいAWSリソースを追加したい」となったときに、YAMLに数行追加するだけで対応できるようになりました。最初からこの設計にしていれば、開発期間はもっと短くなったと思います。

小さく始めて、少しずつ育てる

最初から完璧なものを作ろうとすると、たぶん挫折していました。

まずは「EC2の情報をExcelに出力する」だけの簡単なものを作って、動くことを確認してから、少しずつ機能を追加していきました。

  • Step 1: EC2だけ対応
  • Step 2: VPCを追加
  • Step 3: ELBを追加(複数コマンドの結合が必要)
  • Step 4: RDSを追加(動的サブクエリが必要)
  • Step 5: リファクタリング(設定駆動型に)

この「小さく作って、育てる」アプローチは、プログラミング初心者には特におすすめです。

AIの活用

正直、このツールはAIの助けがなければ完成しなかったと思います。

  • エラーメッセージの意味を教えてもらう
  • 「こういうことがしたい」と伝えると実装方法を提案してもらう
  • コードレビューをしてもらう
  • 「もっとシンプルに書けない?」と聞くとリファクタリング案を出してくれる

プログラミング経験が浅くてもAIを活用すれば、ここまで作れる時代になったんだなと実感しました。

特に、エラーが出たときに「このエラーは何?」と聞くと、原因と解決策を教えてくれるのが本当に助かりました。以前なら数時間かけてググっていたような問題が、数分で解決できるようになりました。

 

9.フロントエンドとの連携

このバックエンドツールは、Webフロントエンドと連携して動作します。

ユーザーがWeb画面で取得したいリソースを選択すると、フロントエンドからバックエンドにリソースIDなどのパラメータが渡され、このPythonツールが実行される仕組みになっています。

【Webフロント】【バックエンド(本記事)】【Excel出力】

  • リソース選択・実行ボタン
  • パラメータ受信・Python実行
  • Excel生成・ダウンロード

関連記事

 

10.今後やりたいこと

  • より多くのAWSリソースに対応(Lambda、DynamoDB、CloudFrontなど)
  • 設定値の差分比較機能(前回との変更点を可視化)
  • パラメータシートからAWSリソースを作成する逆方向の機能
  • Terraformとの連携
  • 日本語対応(パラメーター名など)
  • 視覚的にさらに見やすく(罫線やフォーマットの改善)

 

11.まとめ

プログラミング経験が浅い私でも、約1ヶ月半かけて実用的なツールを作ることができました。

数字で見る効果

項目 Before(手作業) After(自動化)
作業時間 2〜3時間 20〜30秒
コピペミス 発生する なし
設定変更時 手動更新 再実行するだけ

最後に

「面倒だな」と思ったことを自動化できると、本当に気持ちいいです。

そして、一度作ってしまえば何度でも使い回せるのがプログラミングの良いところ。最初は大変でしたが、今では「作ってよかった」と心から思います。

プログラミングに自信がない方でも、「解決したい課題」があれば、ぜひチャレンジしてみてください!AIを活用すれば、想像以上にいろいろなことができますよ。

 

参考


次回は、加藤さんによる「フロントエンド編」の記事です。Web画面の実装について詳しく紹介される予定ですので、ぜひお楽しみに!

タイトルとURLをコピーしました