CodePipelineにおけるステージ設計とインフラCI/CDの実践Tips

目次

はじめに

これまで、第一弾第二弾の記事にてAWS CodePipelineとAWS CodeConnectionsを利用してCI/CDパイプラインを構成し、インフラ変更リリースをGitOpsで実現しようという内容を解説しました。第三弾の本記事では、いよいよCI/CDパイプラインの中でどのような設定を行うかについて説明します。

CodePipelineでどのようなステージを組むか

CodePipelineにおけるステージとは、パイプラインを構成する処理の区切り(フェーズ)のことを指します。パイプライン全体の流れを段階ごとに分け、ソフトウェアのビルドからデプロイまでをアクションを整理する役割を持っています。

パイプラインは複数のステージから構成され、上から下へ順番に処理が進みます。各ステージは、1つ以上のアクション(処理単位)を含みます。例えば、ソースの取得、ビルド、テスト、デプロイなどがアクションにあたります。ステージ間に手動承認を挟んだり、並列実行を行ったりすることで承認依頼や処理の分岐が可能です。ステージごとに進捗や成功・失敗が可視化され、作業の状況を確認することができます。

以下にインフラCI/CDパイプラインでCloudFormationテンプレートをデプロイする際に有用なステージの一例を示します。

ステージ    説明
ソース GitLab セルフマネージドインスタンスをソースとして利用する
リント CodeBuildを使用して、cfn-lint, checkovなどの静的解析ツールで、構文チェックとセキュリティチェックを変更反映前に行う
変更前確認 CloudFormationではデプロイ前の最後の確認として変更セットを作成し、変更が加わるリソースを一覧する
承認 少なくとも本番環境へのデプロイ前には承認ステージを設けることで、意図しない変更が発生しないことを担保する
デプロイ CodeBuildを使用して、aws cloudformation execute-change-setコマンドでデプロイする
検証 CodeBuildを使用して、デプロイ後にスモークテスト(システム全体が致命的に壊れていないかの確認=単体テストの一部)を実行する。設定値確認やサービス状態の確認、ヘルスチェックなどを行う。(スタックが CREATE_COMPLETE で完了しているか、EC2インスタンスが running 状態か等)

上記のステージ設計を図示したものが以下になります。

インフラCI/CDにおけるCodePipelineのステージ設計

この構成ではCodePipelineから一時的に起動できる実行環境であるCodeBuildを利用して、AWS CLIを直接実行する形にしていますが、変更前確認やデプロイについては、CodePipelineが備えるCloudFormationのデプロイアクションを利用することでよりマネージドな形で実行することができます。

CloudFormationのデプロイアクションを利用する場合は、一つのアクションが一つのスタックに対応するため、複数のスタックをデプロイする場合はデプロイステージの中でデプロイアクションをスタック数分定義することになります。

AWS CloudFormation deploy action reference - AWS CodePipeline

また、第二弾の記事ではパイプラインをリージョン間冗長構成にして、冗長環境からメインサイトのリソースを更新できるようにしました。そうしたクロスリージョンのデプロイもCloudFormationのデプロイアクションはサポートしています。

Add a cross-Region action in CodePipeline - AWS CodePipeline

CFnテンプレートのリントに有用なツール

リント(lint)とは、コードや設定ファイルを実行せずに静的にチェックして、ミスや一貫性の乱れ、潜在的なバグやセキュリティリスクを検出することです。

CloudFormationテンプレートの静的解析では、以下のツールがポピュラーです。

これらのツールをソースステージの後のリントステージでCodeBuildによって実行し、チェック内容に問題があればビルドステージに進む前に停止します。

パイプラインの中に複数のソースを設定する

CodePipelineは一つのパイプラインの中に複数のソースを設定することができます。これは、管理元のリポジトリが分かれているがビルド時には同じパイプラインでまとめてビルドしたいときに特に有用です。CodeBuildで別々のアーティファクトとして別個にビルドしても良いし、CodeBuildの中で一つの作業ディレクトリにまとめることもできます。

インフラCI/CDの文脈では、この機能は、例えば環境面が複数に分かれていて冗長環境も存在し、パイプラインの本数がかなり多くなってしまうケースで利用できるかもしれません。同じような用途だが環境面が細かく分かれていて、パイプラインの数が環境面数分だけ増えてしまうという場面では、パイプラインは1本に集約して、パイプラインの中でソースからデプロイまでを並列に構成するということは考えられます。

AWS CodePipeline CodeBuild のサンプル - AWS CodeBuild

パイプラインの実行対象と実行内容をコミットごとに制御する

CI/CDパイプラインは、基本的にパイプラインで実行される処理内容と実行対象が固定されていて、処理されるコードのバージョンだけ上がっていくのが理想であると思います。しかし、これは例えば何かのアプリケーションの実行バイナリをビルドするなどの、ビルド対象もデプロイ対象も固定されている場合には実現できますが、インフラCI/CDのように、変更対象のサーバー群が多様で、処理内容も幅があり、処理内容と実行対象の組み合わせが無限に近く発散していく場合には、パイプラインの数が爆発的に増えてしまいます。

例えば、これまでの文脈ではCloudFormationテンプレートを実行することに限定して議論してきたため、実機とテンプレートが1:1で対応している前提があるので、パイプラインの中で複数のスタック(テンプレート名)を固定で定義しても問題にはなりにくいです。一方、例えばCodePipelineでAnsibleを実行しようと考えると、処理を実行したいサーバー群と処理内容の組み合わせが不定になるため、パイプラインを固定することは現実的ではありません。

こうした時、パイプラインの考え方からすると最善とは言えませんが、以下のような工夫をすると、ソースコードのコミットごとに実行対象と実行内容を制御できます。

  • リポジトリops/plan.ymlというファイルを作り、コミットするときは必ず含めるようにする

ops/plan.yml

runs:
  - inventory: "inventories/A/hosts.yml"
    limit: "group_A"                   # 省略可(配列でもOK: ["web01","web02"])
    actions:
      - playbook: "file_dist.yml"
        tags: ["files"]
        extra_vars: { content: "Hello" }

  - inventory: "inventories/B/hosts.yml"
    limit: ["web01","web02"]           # 文字列なら "web01,web02" でもOK
    actions:
      - playbook: "config_change.yml"
        check: true
  • CodeBuildが参照するbuildspec.ymlの中で、ops/plan.ymlを解釈して、その中で指定されたインベントリファイルとplaybookを実行するように、都度ansible-playbookコマンドが組み立てられるようにする。

buildspec.yml

version: 0.2
env:
  variables:
    PLAN_FILE: "ops/plan.yml"

phases:
  install:
    commands:
      - pip3 install --upgrade pip
      - pip3 install "ansible==9.*" pyyaml
  pre_build:
    commands:
      - if [ ! -f "$PLAN_FILE" ]; then echo "no plan → skip"; exit 0; fi
  build:
    commands:
      - |
        python3 - <<'PY'
        import os, sys, json, yaml, subprocess
        plan = yaml.safe_load(open(os.environ['PLAN_FILE'])) or {}
        runs = plan.get('runs') or []
        if not runs: sys.exit(0)

        for r in runs:
            inv = r.get('inventory')
            if not inv or not os.path.isfile(inv):
                raise SystemExit(f"inventory not found: {inv}")

            limit = r.get('limit')
            if isinstance(limit, list):
                limit = ",".join(limit)

            for a in r.get('actions', []):
                pb = a['playbook']
                cmd = ["ansible-playbook","-i",inv,f"playbooks/{pb}"]
                if limit: cmd += ["--limit",limit]

                tags = a.get('tags')
                if tags:
                    if isinstance(tags, list): tags = ",".join(tags)
                    cmd += ["--tags", tags]

                ev = a.get('extra_vars')
                if ev:
                    with open("ev.json","w") as f: json.dump(ev,f)
                    cmd += ["-e","@ev.json"]

                if a.get('check'): cmd += ["--check","--diff"]

                print("+"," ".join(cmd))
                subprocess.check_call(cmd)
        PY

このような仕組みを作れば、リポジトリ側のAnsibleのディレクトリ構成を維持して新しいPlaybookを増やしながら、コミット時にどのPlaybookをどのサーバーに対して実行するかをops/plan.ymlの中で制御することができるようになります。

まとめ

今回は、インフラCI/CDパイプラインを構成する際に、特に有用となるTipsをまとめました。インフラCI/CDはアプリケーションのソースコードをビルドするなど範囲が決まった運用が難しい側面があるため、特に工夫が必要かと思います。本記事が設計時のお役に立てば幸いです。

CodeConnections非対応リージョンにおける代替構成とCodePipelineの冗長構成

目次

はじめに

前回の記事で、インフラCICDパイプラインによるGitOpsの実践についてまとめ、インフラリソースの変更管理をCICDパイプラインで管理することのメリットを紹介しました。本記事では、第二弾としてAWSでCICDパイプラインを構成する際の、より実践的な課題や悩みをどのように解決できるかを説明します。

AWS CodeConnectionsの対応リージョン

AWS CodeConnections(旧称:CodeStar Connections)は、CICDパイプラインを構成することができるAWS CodePipelineなどのサービスを、Gitのソースリポジトリと接続するためのサービスです。

SaaSとして提供されるGitHubやGitLab, AWS CodeCommitなどのGitプロバイダに対応しており、GitLabの場合は、EC2などに導入するセルフマネージドインスタンスにも対応しています。

今回は、企業のITシステム構成としてよくあるセルフマネージドのGitLabインスタンスをソースとし、CodeConnectionsを利用してCodePipelineのCICDパイプラインを構成する例を考えます。

日本のAWS利用者であれば、多くの場合、AWS東京リージョンをメインサイトに選定して、大阪リージョンをDRサイトとして構成するか、その逆の構成とすることでしょう。2025年9月現在、残念ながらCodeConnectionsは大阪リージョンに対応しておらず、長い事動きもないため、大阪リージョンでGitプロバイダをソースとしてCodePipelineを構成することは出来ない状況となっています。

この機能は、アジアパシフィック (香港)、アジアパシフィック (ハイデラバード)、アジアパシフィック (ジャカルタ)、アジアパシフィック (メルボルン)、アジアパシフィック (大阪)、アフリカ (ケープタウン)、中東 (バーレーン)、中東 (アラブ首長国連邦)、欧州 (スペイン)、欧州 (チューリッヒ)、イスラエル (テルアビブ)、または AWS GovCloud (米国西部) の各リージョンでは使用できません。

CodeStarSourceConnection (Bitbucket Cloud、GitHub、GitHub Enterprise Server、GitLab.com、および GitLab セルフマネージドアクションの場合) - AWS CodePipeline

CodeConnectionsが大阪で利用できないケースへの回避策

CodeConnectionsが大阪で利用できない場合、GitLabをソースとしたCodePipelineを構成する設計が大阪ではできないことになり、大阪リージョンをメインサイトとしてGitOpsを行いたい利用者は困りますし、東京リージョンをメインサイトにして、大阪リージョンをDRサイトとして冗長構成しようとする場合も不便があります。

そうした際に取り得る選択肢を4つ検討したため、それぞれの回避策を説明します。

比較項目 CodePipelineを諦める GitLab RunnerでS3にソースコードをZipでアップロード GitLabからCodeCommitへプッシュミラー CodePipelineだけ別リージョンで構成
パイプラインの実装方法 GitLab CI/CDかJenkinsなどで手組する コミットされる度にGitLab RunnerでソースをZip圧縮してS3へアップロード GitLabからCodeCommitへプッシュミラーして、ソースプロバイダーをCodeCommitに変更 CodePipelineだけ別リージョンで構成

一つ目のCodePipelineを諦めてしまう手法では、マネージドサービス活用による効率化や工数削減を諦めて、GitLab上でパイプラインを構成したり、別途Jenkinsなどのビルドサーバーを構成します。潔く諦めるパターンです。

二つ目のGitLab RunnerでS3にソースコードをZipでアップロードする手法は、CodeConnectionsがリリースされる以前にはよく採用されていた構成であり、コミット時のリポジトリのソース一式をS3に保管し、CodePipelineのソースをS3バケットに設定する方法です。Gitの世界から離れてしまうことになるので、コミットIDやブランチ、タグ情報、コミッター情報がCodePipelineから参照できなくなり、監査性が下がることが懸念されます。また、コミットされる度にGitLab Runnerが動いてソースのzip圧縮とアップロードが行われるのは都度時間がかかって効率が悪いとも感じます。

三つ目のGitLabからCodeCommitへプッシュミラーして、CodeCommitをソースとしてCodePipelineを構成する手法も、CodeConnectionsがGitLab対応するまではよく採用されていましたが、CodeConnectionsが2025年9月現在、新規利用を停止し、メンテナンスフェーズに入っていることを考えると積極的に取りうる選択肢とは言えません。

四つ目のCodePipelineだけ別リージョンで構成する手法は、消去法的な選択肢になりますが、大阪リージョンでCodeConnectionsが利用できないながらも、Gitの世界に完結してマネージドサービスの恩恵を受けるという観点では有望な選択肢になります。

CodeConnectionsをVPCで利用する

VPC内のGitLabをソースにして、CodePipelineでパイプラインを構成する場合、境界型セキュリティ的な観点では、その間のCodeConnections接続はVPC内に閉じたいケースが多いことでしょう。もちろん、近年のゼロトラストを前提としたSaaS製品のように、GitLabをインターネットに公開して、すべてインターネット越しに接続するということも可能です。CodeConnectionsをVPCで接続する際は、CodeConnectionsが自身のリージョン内のVPCにインターフェースVPCエンドポイントを構成します。このVPCエンドポイントがデプロイされているVPCがGitLabのデプロイされているVPCとNW上でルータブルであればリージョンを跨いでいても問題ありません。

VPC経由でのCodeConnectionsの接続

https://pages.awscloud.com/rs/112-TZM-766/images/AWS-Black-Belt_2024_AWS-CodeConections_1217_v1.pdf

つまり、大阪のGitLabをソースとして、東京のCodePipelineが東京のCodeConnections経由で接続し、パイプラインを構成することもできるのです。その前提は、大阪のGitLabがデプロイされているVPCが東京のCodeConnectionsのVPCエンドポイントがデプロイされているVPCVPCピアリングやTransit Gatewayなどで疎通可能となっていることです。 しかし、もし東京をメインサイトにしていて大阪をDRサイトにする場合、DRの構成に東京のCodePipelineを使用しては、東京障害時にDR用のパイプラインも止まってしまうため意味がありません。その場合は、海外リージョンを利用することが考えられます。

GitLabをソースとした東京・大阪でのCodePipelineの冗長構成

次の構成図は、東京をメインサイトとして開発環境にGitLabのインスタンスを立てて、GitOpsの考え方に基づいて開発環境のGitLabを信頼できる唯一の情報源(Single Source of Truth)としたうえで、災対向けのDR環境を大阪にも作成する設計を示しています。

GitLabをソースとした東京・大阪でのCodePipelineの冗長構成

この構成では、DRサイトは大阪リージョンとソウルリージョンを組み合わせてGitLabをソースとしたCodePipelineを構成しています。大阪とソウル間はNW的にVPC間が疎通可能である必要はありますが、東京リージョンのGitLabやパイプラインに障害が発生しても、大阪側で全環境のリソースの変更をパイプラインで継続することができます。GitLabインスタンスは二つ立てることとし、メインサイトからDRサイトへはプッシュミラーを行う構成にすれば、ソースコードも同一の内容を維持できます。

まとめ

本記事では、セルフマネージドインスタンスのGitLabをソースにして、CodeConnectionsが対応していないAWSリージョンでもCICDパイプラインを構成する方法をまとめました。また、東京・大阪間でCICDパイプラインをリージョン間冗長構成にする設計についても紹介しました。複数の構成案を取りうる時の設計判断にご活用いただければ幸いです。

インフラ管理へのGitOpsとCI/CDパイプラインのススメ

目次

はじめに

皆さんは大規模なITシステム環境で、サーバーが何十台もあり、環境も複数用意されているような状況でITインフラをデプロイして管理する際、どのような手法を用いるでしょうか。詳しい方ならInfrastructure as Code (IaC)によるサーバーの定義と構成をすぐに想像されるでしょう。

しかし、構築の際に単にAWSのCloudFormationやTerraformでコードを記述してデプロイするだけで、その後の運用の中での変更管理やコードでデプロイしたリソースをどのように維持していくか、という観点までは考えが至らないこともあるかと思います。

本記事では、そうしたIaCの構成観点で検討が必要な事項について考えを整理し、IaCでのインフラ運用の望ましい形を模索してみます。

インフラ構成管理上のよくある課題

サーバー間・環境間の設定差異

インフラ構築や運用における変更管理を行う上で良く遭遇するのが、構築後にサーバーに対する変更の必要が発生することです。テストの結果の修正対応や、アプリケーションチームから構成変更を依頼されるなど、変更依頼が断続的に発生すると、その変更を管理して全てのサーバーや環境で同じ設定を維持するのは難しくなります。その結果、あるサーバーには変更が反映されているのに、他のサーバーには反映されていないという事態が容易に起こり得ます。特に大規模環境では顕著です。

変更履歴が不明確

本番リリース後はしっかりリリース判定などを行って変更管理を手動で行うことが多いでしょうが、初期構築後から本番リリースまでの間に何度も設定変更を行う場合、そうした変更を誰がどのタイミングで行ったか分からなくなることがままあります。特に、構築スケジュールに余裕がない時は、皆慌てており、丁寧な変更管理が行き届かないこともあるでしょう。本番リリース後の運用の中でも、しっかり変更管理ができていなければ、気が付いたらサーバー間で設定値がずれていたということは起こり得ます。

リリース手順書に従った人手の手動リリース

伝統的な運用を行っている環境では、運用者が手動でサーバーにログインして変更を反映したり、多少システム化されている場合でも、リリースシェルスクリプトのようなものを作成して、それを実行するということが多いかと思います。人が介する変更作業はミスの元であり、リリースするたびにリリース手順書を作成したり、事前にリリース判定会議でそれを読み合せたりしていると、時間も労力も工数もかかります。かつてはそれで良かったとしても、IT人材の人手不足が叫ばれ、変化のスピードが速くなっている現在では、昔ながらの人手をかけた運用では、ビジネスの競争力を維持することは難しくなっていきます。

GitとCI/CDパイプライン

そうした課題に対して、インフラ構築と運用にもGitによるソース管理とCI/CDパイプラインを導入するというのが本記事の趣旨になります。

直感的にはそうした運用はアプリケーションコード開発に適しており、GitリポジトリへのコードのコミットやCI/CDによる自動デプロイは対象がコンテナやアプリケーションバイナリに限定された場合に機能すると感じられますが、考慮事項はありつつもインフラ管理へもこれらの仕組みを適用することは可能です。

GitOpsとは

世の中で多く解説されているため、本記事では多くは説明しませんが、GitOps(ギットオプス)とは、アプリケーションやインフラのデプロイ・管理を、Gitリポジトリを唯一の信頼できる情報源として自動化する運用モデルを指します。DevOpsの一種であり、「Git」と「Operations」を組み合わせた言葉です。

GitOpsを導入することで、インフラ運用でも以下のようなメリットを享受することができます。

  • 監査性:変更履歴がすべてGitに残る。Gitのコミットログと合わせて、誰が、いつ、どのコードを、どの環境にデプロイしたかが追跡可能。
  • 再現性:Gitの内容を元に、いつでも同じ状態を再構築できる。
  • 自動化:人手による手作業やCLI操作を減らし、ミスを防ぐ。
  • 安全性:パイプラインの中で承認ステージを設定することで、本番環境への変更にシステム的な承認要求を準備できる。
  • 処理の構成しやすさ:CIであるリントやテストをステージとして組み込める。デプロイ後の検証、通知、監視連携などのプロセスをパイプラインとしてまとめることができる。
  • ロールバックが簡単:Gitでバージョン管理されているので、以前の状態に戻すのが簡単。

ここで、GitOpsを含むインフラ構成管理の成熟度別実装を確認しましょう。

成熟度 構成 実装具体例 説明
最高 実践的GitOps ArgoCD/FluxでKubernetesやCrossplaneリソースを自動適用(Pull型) 理想形。自己修復・宣言的・Pull型。狭義・本来のGitOps
IaC + CI/CD CloudFormationやTerraformをCI/CDで自動実行(Push型) Gitを唯一の信頼できる情報源として活用しており、十分にGitOps的。広義のGitOps
IaC + 手動デプロイ Gitにコードはあるが、デプロイは手動実行 バージョン管理のメリットはあるが、自動化・監視の面ではGitOpsとは言いがたい。準GitOps・GitDrivenOpsなど
手作業 GUIや手作業管理中心 GitOpsではない

Kubernetesのような、プラットフォームとしてGitOpsを念頭に置いた環境では、デプロイ対象側が自動で変更をGitまでPullしてきて、自己修復を行う本来のGitOpsを実現することが可能ですが、仮想マシンを管理するというレイヤーでは、IaC + CI/CDをGitへのコミットをトリガーに実行するPush型での構成が実現可能な範囲で最適と言えます。

本記事では、手作業でサーバーの管理を行っている環境へむけて、このIaC + CI/CDで構成される広義のGitOpsの導入を薦めたいと考えています。なお、この広義のGitOpsのことをCIOpsとも呼びますが、本記事ではGitOpsと呼ぶこととします。

Git + IaC + CI/CDのGitOpsをAWSで実現する

ここからは、実際にAWS環境において、インフラのGitOpsをどのように実現するかを説明します。

パイプラインの数

CI/CDパイプラインを構成する時、基本的には用途に応じてパイプラインを作成します。つまり、標準的には用途ごとにパイプラインは一つになるのです。しかし、単一パイプラインではなく、複数パイプラインを構成したい場合もあります。それはインフラCD/CDを実現する際に検討することになる、環境ごとにパイプラインを独立させるかという議論から起こります。

アプリケーション開発においては、単一のパイプラインで複数のステージを定義し、開発でのCIのステージが完了したら開発でデプロイされて、変更が検証されたら、同じパイプラインの中でステージング環境へのデプロイのステージが実行されて、最終的に本番環境へデプロイされる、という流れが起こります。つまり開発でビルドされたアーティファクトは同じものが本番までデプロイされるのです。インフラにおいては、これは同じ変更が全ての環境に適用されるということになります。

しかし、本来的には全ての環境で同じ変更が反映されて、環境間差異は外部パラメーターとして吸収されるというのが理想ですが、インフラCI/CDでは変更範囲が多様です。特定のコンテナやアプリケーションバイナリだけを置き換えれば良いわけでなく、サーバー自体を置き換えることになるため、すべての設定をすべての環境で外部パラメーター化して、一貫させるということがどうしてもできないところが出てきます。そういう時、環境ごとに複数パイプラインを構成するという考え方が出てきます。

以下に、単一パイプライン+環境ごとに複数ステージを用意するケースと、環境ごとに複数パイプラインを構成するケースを比較します。

比較項目 単一パイプライン+環境ごとに複数ステージ 環境ごとに複数パイプライン
環境間の独立性・安全性 一つのパイプラインで一度の変更で開発から本番まで同じ設定が反映されるため、本番と開発が一貫される。 パイプラインが環境ごとに独立しているため、仮に開発環境で何か問題が発生した場合でも本番環境には影響しない。
メンテナンス性・運用負荷 パイプラインの数を限定できるためメンテナンス性は高い。上手く運用が適合すれば運用負荷も低い。 環境ごとにパイプラインが出来上がるため、パイプラインのメンテンナンスは複数分必要。その分運用負荷は発生する。
ロールバック・検証のしやすさ 同じパイプラインの中で環境ごとに複数のステージが並ぶ場合、開発は適用成功しても本番が成功しない場合がある。その際に、冪等性を担保できないと、単に同じパイプラインの再実行はできないことになり、パイプライン管理にリスクがある。 環境ごとにパイプラインが用意されていれば、どこかの環境で問題が起こった場合にも個別復旧が可能。他の環境には影響しない。
構成の一貫性 明確に開発と本番の構成を揃えることになり、システム全体の構成は一貫する。環境間差異は外部パラメーター化するのが理想だが、一貫させるべきでない設定値があり、かつ外部パラメーター化できない場合が発生することが予想されるため、ノックアウトになるかも。 環境間で明示的に個別に設定変更を行うため、仮に環境間で一貫させるべきでない設定値があり、かつ外部パラメーター化できない場合には、環境ごとに反映する値を変更して制御できる。
変更作業のミス防止 開発環境でデプロイ成功した変更と同じ変更が本番でもデプロイされるので、開発と本番で異なる変更をデプロイしてしまうというミスが起きない。 基本的には同じ変更を別のブランチにマージするだけなので、致命的なミスは起きないはずだが、同じ変更でも環境間で異なるデプロイメントを行うことになるので、開発でテスト済みでも本番適用にあたりミスが生まれる可能性あり。(リリース判定会などでそうしたミスをカバーする必要あり)
伝統的運用との適合性 開発環境で行った変更とセットで本番環境に同じ変更が、(必要に応じて)承認された後に実行される。環境間差異がなくなり確実に変更が管理される。 伝統的運用では、開発環境で変更結果を確認してから、別途時間と体制を計画してリリースすることが通例。開発と本番間の運用レベルが明確に異なる。環境ごとにパイプラインを分けると伝統的運用と同じ運用が可能。

総論としては、大規模環境でこれまで手動に近い形で本番変更作業を行ってきた環境においては、環境ごとに複数パイプラインを定義して、Gitに環境ごとのブランチを作成し、開発パイプラインの実行完了後に、コミットを本番用ブランチにマージし、本番パイプラインを実行するパターンが無難かもしれません。

構成図

以下に「単一パイプライン+複数ステージ」と「環境ごとに複数パイプライン」を構成するアーキテクチャーを掲載します。この構成では、GitサービスとしてEC2でセルフマネージドなGitLabインスタンスを採用(※)し、それをソースとしてAWS CodePipelineでCI/CDパイプラインを構成して、CloudFormationでAWSリソースをデプロイするものとしています。

※1 本当はAWS CodeCommitをリポジトリサービスに選定したいところですが、AWS CodeCommitは2024年7月に新規顧客への提供が終了しています。

AWS CodeCommit リポジトリを他の Git プロバイダーに移行する方法 | Amazon Web Services ブログ

※2 なお、AWS CodePipelineはGitソースとしてセルフマネージドなGitLabインスタンスを2023年末からサポートしています。

CodePipeline が GitLab セルフマネージドをサポート

マルチアカウント構成を基本に、環境ごとにアカウントを分離して、 単一パイプライン構成ではCI/CDパイプライン専用のアカウントを用意しています。(開発環境アカウントに同居も可能ですが、開発から本番への権限を付与することになるため、権限分離が不完全になります)

単一パイプライン構成の場合、一つのパイプラインの中で複数のステージが実行され、同じソースのテンプレートから同じ変更が一貫して全ての環境に反映されます。

単一パイプライン+複数ステージ

一方、複数パイプライン構成では、Gitのブランチごとにパイプラインが構成されており、パイプラインの数が増えてメンテナンス性は下がりますが、ブランチにコミットするテンプレートの内容に応じて、環境間で異なる設定を行う余地ができるため、環境間の独立性が高まります。

環境ごとに複数パイプライン

AWSでCI/CDパイプラインを組む上で留意すること

AWS上でEC2やRDSのリソースを作成する時、AWS Backupを利用することは多いかと思います。しかし、AWS BackupとCI/CDパイプラインは相性が悪い所があり、AWS BackupからEC2やRDSインスタンスを復元する際は、元のインスタンスとは別のインスタンスが起動するため、復元されたインスタンスをコード管理/パイプライン管理の対象にする必要があります。

幸いなことに、AWS CloudFormationにはIaCジェネレーターと呼ばれる機能があり、この機能を利用することでコード管理されていないリソースもコード管理に取り込むことが容易になっています。

AWS CloudFormation IaC ジェネレーター で AWS リソースの棚卸を試してみた - builders.flash☆ - 変化を求めるデベロッパーを応援するウェブマガジン | AWS

Terraformの場合も、-generate-config-outオプションを利用することで、自分で実機と同じコードを書かなくても、自動で実機からコードを生成して取り込むことができます。

Import - Generating Configuration | Terraform | HashiCorp Developer

一方、残念なことに、2025/04現在、AWS CDKには、cdk importでもコード生成機能がないため、実機と同等のコードを運用者が記述する必要があり、コード管理されていないリソースを取り込むための手間がかかります。

AWS CDKCLI リファレンス - AWS Cloud Development Kit (AWS CDK) v2

CDKとCloudFormationの比較

CDKとCloudFormationの話が出たため、最後に伝統的なインフラ運用を行ってきた環境に対してどちらを選択すべきかという観点で両者を比較してみましょう。インフラ担当者主体で伝統的なインフラ運用を行ってきた環境でGitとCI/CDパプラインを用いてGitOpsを行う場合、AWS CloudFormationが優位かと思われます。

比較項目 AWS CDK AWS CloudFormation
記述の柔軟性 複数台のインスタンスを構成する場合などの記述が柔軟に可能 条件式などが限られており、複数台のインスタンスを作成する場合などに記述がやや冗長になる。
運用者のスキル適合性 プログラミング言語による記述であり、アプリケーション開発者であれば既存スキルセットの延長として低負荷で利用できるが、運用主体がインフラ担当者の場合、習熟難易度が高く適さない。 記述量は比較的多くなるが、その分抽象度が下がってコードのどの部分を変更すれば良いか明示的に指定できるため、変更管理が容易。運用主体がインフラ担当者の場合適している。
ツール管理 記述プログラミング言語のランタイム環境のバージョン管理やCDK自体(CDK CLI)のバージョン管理が必要。(パイプライン管理の場合は実行環境の管理不要) AWSマネージドサービスのため、バージョン管理不要。実行環境の管理不要。
手動作成リソースのインポート CDKは、cdk importでもコード生成機能がないため、実体と同等のコードを運用者が記述する必要あり(2025/04時点) CloudFormation IaCジェネレーターを利用すれば、低負荷で手動作成リソースをコード管理+パイプライン管理に取り込むことが可能。

まとめ

本記事では、大規模環境でインフラ担当者が伝統的なインフラの変更管理を行っているという状況に対して、IaCを用いることでGit + CI/CDパイプラインによるGitOpsを導入して、インフラ構成管理上の課題を解決するアプローチを解説しました。今回はAWSリソースに限定した構成を示しましたが、OS上の設定変更についての考え方なども別途示すことができればと思います。

RedshiftでNL2SQLを使って自然言語でデータ分析を行う

目次

はじめに

ここ数年、生成AIのビジネス利用の波が止まりません。私は2023年と2024年にアメリカのラスベガスでAWS re:Inventに現地参加していますが、2000以上あるセッションの大半で生成AIのトピックが取り上げられますし、基調講演でも2年続けて生成AIに関する発表が数多く行われました。

私も生成AIは確実に世の中を変えていく技術だと考えており、公私ともに生成AIを利用したり、生成AIのシステム構築に携わったりしています。先日も、年末休暇中に今年分の確定申告のために帳票を作成している際、一年間の取引履歴を生成AIに取り込んで、集計作業や金額が想定と違う部分を生成AIとの対話で分析する等、便利に利用しています。

生成AIの便利な点は自然言語で指示を行えることですが、Excelスプレッドシートとにらめっこしたり、SQLクエリを書くことなく自然言語でデータ操作を行えることは本当に重宝します。自然言語で指示した内容をSQLに変換してデータ操作を行うことを一般的にNatural Language to SQL(NL2SQL)と呼びますが、本記事ではAmazon RedshiftでNL2SQLを利用して、データ分析を行う方法について記載します。

NL2SQLの価値

生成AIのビジネス利用に火がついた2022年末から2023年上半期頃には、生成AIとRAGによってエンタープライズサーチシステムを従来より劇的に改善するという文脈での活用方法が盛んに喧伝されました。この流れは今も続いていますし、クラウドストレージやSharePointなどに保管された企業内情報の検索性を高めるという一種のデジタルアダプションのようなユースケースがビジネス用途では多くの一般企業で普遍的であり、価値訴求しやすいからであると思います。

生成AIが学習時に取り込んでいない外部情報を、推論時に取り込むグラウンディング技術であるRAGが非常に有効であることは間違いないですが、エンタープライズサーチの文脈ではText, PDF, Word, HTMLなどの非構造化データや半構造化データの検索ばかりが取り上げられます。出張申請の申請方法を調べるなどがその例となりますが、エンタープライズサーチはこうした作業による認知負荷を下げる点では有益な一方、ビジネスの利益を増やすことには直結していません。生成AIをビジネスの利益に直結させるのであれば、扱うデータは日々の取引で蓄積される本番の業務データであるべきです。

ほとんどの場合、業務データはRDBMSやNoSQLのDBに格納されるでしょうが、この業務データを分析して示唆を得ることで、ビジネスの次の一手を打つことができれば大きな価値が生まれます。生成AIのRAGのアプローチが弱かったのはこの点ですが、NL2SQLを利用することでデータアナリストやビジネスユーザーが自然言語で簡易に業務データの分析ができるようになると、生成AIの価値が更に発揮されます。

⽣成 AI が変える、データアナリティクス(PDF)

上記のAWS Summit Japan 2024の資料にある通り、AWSをはじめとしたクラウドプロバイダーもこのようなユースケースを想定して、2023年末あたりからNL2SQLのマネージドサービスを展開してきました。前置きが長くなりましたが、本記事ではそうしたサービスの一つであるAmazon Q generative SQL in Amazon Redshift Query Editorを利用して、Redshiftの業務データを分析してみます。

Amazon Q generative SQLとは?

平たく言うと、AWSマネージメントコンソールのRedshift Query Editor内で、接続したDBに対する自然言語での指示をSQLクエリにしてくれる機能です。テーブルやカラムなどの情報を自動で認識してくれるのでDBに接続すればすぐに利用できます。オープンソース実装のLangChainのSQLDatabaseChainで行えることの一部をAWSがRedshiftと密に統合する形で実現したものです。

Amazon Q 生成 SQL の操作 - Amazon Redshift

AWS re:Invent 2023でプレビューが発表され、2024年中に正式リリースされて東京リージョンでも利用できるようになりました。

Amazon Q generative SQL in Amazon Redshift Query Editor Public Preview Demo(Youtube)

AWS re:Invent 2023 - [LAUNCH] Amazon Q generative SQL in Amazon Redshift Query Editor(Youtube)

Amazon Q generative SQLを利用してみる

Amazon Q generative SQLの有効化と価格

マネージメントコンソールの左下の歯車ボタンから「Q generative SQL settings」をクリックして有効化のチェックをいれます。

Amazon Q Developer 無料利用枠では、全ユーザー共通でAWSアカウントあたり月1000回まで無料で自然言語からSQLクエリを生成できます。Amazon Q Developer Proは1 ユーザーあたり19 USD/月で、契約したユーザーは月1000回までSQLクエリを生成できます。(ということは、1 AWSアカウント=1ユーザーの運用をしていればAmazon Q generative SQLについては無料枠が実質Proと同じということ?)

Amazon Q generative SQLの有効化

Amazon Q Developer free tier

ソフトウェア開発のための AI – Amazon Q Developer の料金 – AWS

下準備:Redshiftにデータを取り込む

本記事では、KaggleでCC0: Public Domainで公開されているCredit Card Transactions Fraud Detection Datasetを利用します。このデータセットCSVで、シミュレートされたアメリカのクレジットカード取引の記録が含まれています。本来は不正利用検知のための機械学習モデルの学習用のものです。

Credit Card Transactions Fraud Detection Dataset | Kaggle

※参考まで以下がデータセットに含まれるカラム

  • index - Unique Identifier for each row
  • trans_date_trans_time - Transaction DateTime
  • cc_num - Credit Card Number of Customer
  • merchant - Merchant Name
  • category - Category of Merchant
  • amt - Amount of Transaction
  • first - First Name of Credit Card Holder
  • last - Last Name of Credit Card Holder
  • gender - Gender of Credit Card Holder
  • street - Street Address of Credit Card Holder
  • city - City of Credit Card Holder
  • state - State of Credit Card Holder
  • zip - Zip of Credit Card Holder
  • lat - Latitude Location of Credit Card Holder
  • long - Longitude Location of Credit Card Holder
  • city_pop - Credit Card Holder's City Population
  • job - Job of Credit Card Holder
  • dob - Date of Birth of Credit Card Holder
  • trans_num - Transaction Number
  • unix_time - UNIX Time of transaction
  • merch_lat - Latitude Location of Merchant
  • merch_long - Longitude Location of Merchant
  • is_fraud - Fraud Flag

今回はこのデータセットをS3バケットに保管し、Redshift Serverlessにマネージメントコンソールからロードします。通常は事前に適切なスキーマを定義しロード先のテーブルを作成してから取り込みますが、ウィザードを利用してCSVからスキーマの自動検出を行って、テーブルを一緒に作成します。

※1 Redshift Serverlessはデフォルト設定で構成しています。
※2 zipを展開してfraudTrain.csvを利用しますが、そのままではヘッダーのidカラムが欠損しているので、S3アップロード前にメモ帳で編集し先頭をid列としています。

Amazon S3 からデータをロードする - Amazon Redshift

Automatic Schema Inference using Amazon Redshift Query Editor V2(Youtube)

S3からデータをロード

スキーマの自動検出

これで、devデータベースのpublicスキーマにfraudTrainテーブルを作成し、CSVのデータを取り込みました。

S3からRedshiftへデータロード完了

※同じことをSQLコマンドで実行するなら以下の通り

CREATE TABLE public.fraudtrain (
    id integer ENCODE az64,
    trans_date_trans_time timestamp without time zone ENCODE az64,
    cc_num bigint ENCODE az64,
    merchant character varying(256) ENCODE lzo,
    category character varying(256) ENCODE lzo,
    amt real ENCODE raw,
    first character varying(256) ENCODE lzo,
    last character varying(256) ENCODE lzo,
    gender character varying(256) ENCODE lzo,
    street character varying(256) ENCODE lzo,
    city character varying(256) ENCODE lzo,
    state character varying(256) ENCODE lzo,
    zip integer ENCODE az64,
    lat real ENCODE raw,
    long character varying(256) ENCODE lzo,
    city_pop integer ENCODE az64,
    job character varying(256) ENCODE lzo,
    dob date ENCODE az64,
    trans_num character varying(256) ENCODE lzo,
    unix_time integer ENCODE az64,
    merch_lat double precision ENCODE raw,
    merch_long character varying(256) ENCODE lzo,
    is_fraud integer ENCODE az64
) DISTSTYLE AUTO
SORTKEY
    (trans_date_trans_time);
COPY dev.public.fraudTrain
FROM 's3://redshift-data-ingestion-450394840884/fraudTrain.csv'
IAM_ROLE 'arn:aws:iam::450394840884:role/service-role/AmazonRedshift-CommandsAccessRole-20250104T231905'
FORMAT AS CSV
DELIMITER ','
QUOTE '"'
IGNOREHEADER 1
REGION AS 'ap-northeast-1'

自然言語でクエリする

クエリ対象のワークグループとデータベースを選択して接続し、ページ上部中央のAmazon Qアイコンをクリックすると、Amazon Q generative SQLのサイドバーが開きます。

Amazon Q generative SQLを利用する

1. 特定の時期に不正利用が集中しているか

まずは、「特定の時期に不正利用が集中しているか」を確認するため、次の内容を問いかけます。

不正利用の時系列分析のため、トランザクションの日時(Trans_date_trans_time)に対して、不正利用の数(Is_fraudが1の件数)を時系列で月別で集計し、特定の時期に不正利用が集中しているかを確認してください。

すると、以下のクエリが生成されました。サイドバーの「Add to notebook」をクリックすると、生成されたクエリをNotebookのセルに転記してくれます。セルを実行するとクエリの結果が表示されます。結果は、2019年12月に不正取引の回数が592回で最多でした。

SELECT
  DATE_TRUNC('month', trans_date_trans_time) AS month,
  COUNT(
    CASE
    WHEN is_fraud = 1 THEN 1 END
  ) AS fraud_count
FROM
  public.fraudtrain
GROUP BY
  DATE_TRUNC('month', trans_date_trans_time)
ORDER BY
  month;

特定の時期に不正利用が集中しているか

2. 不正取引が特定の取引種別に集中しているか

次に、不正取引が一番多かった2019年12月に「不正取引が特定の取引種別に集中しているか」を確認するため、次の内容を問いかけます。

2019年12月について、不正取引が特定の取引種別(category)に集中しているかを確認してください。

以下のクエリが生成されました。同じように「Add to notebook」をクリックし、Notebookのセルに転記します。セルを実行すると、結果はインターネットショッピング(shopping_net)が最多で、2019年12月に148回不正取引が発生していました。

SELECT
  category,
  COUNT(
    CASE
    WHEN is_fraud = 1 THEN 1 END
  ) AS fraud_count
FROM
  public.fraudtrain
WHERE
  DATE_TRUNC('month', trans_date_trans_time) = '2019-12-01'
GROUP BY
  category
ORDER BY
  fraud_count DESC;

不正取引が特定の取引種別に集中しているか

3. 取引種別がインターネットショッピングで、取引金額が大きい不正取引が最も多い州はどこか

最後に、不正取引が一番多かった2019年12月において、「取引種別がshopping_netで、取引金額が1000ドル以上の額の大きい取引について、不正取引が最も多い州」を確認するため、次の内容を問いかけます。

2019年12月のデータを見た時、取引種別(category)がshopping_netで、取引金額(amt)が1000以上の取引について、不正取引が最も多い州(state)を教えてください。

以下のクエリが生成されました。同じように「Add to notebook」をクリックし、Notebookのセルに転記します。セルを実行すると、結果はペンシルベニア州が最多で、2019年12月にインターネットショッピングでの不正取引が10回発生していました。

SELECT
  state,
  COUNT(
    CASE
    WHEN is_fraud = 1 THEN 1 END
  ) AS fraud_count
FROM
  public.fraudtrain
WHERE
  DATE_TRUNC('month', trans_date_trans_time) = '2019-12-01'
  AND category = 'shopping_net'
  AND amt >= 1000
GROUP BY
  state
ORDER BY
  fraud_count DESC
LIMIT
  1;

取引種別がインターネットショッピングで、取引金額が大きい不正取引が最も多い州はどこか

4. より自然な言葉で指示する
より自然な言葉で指示する

先ほどまでは、Amazon Qがどれほど融通が利くか未知数のため、「取引種別(category)がshopping_netで、取引金額(amt)が1000以上」といった、カラム名をこちらが指定する形で問いかけましたが、もっと自然に分析したいので、指示の内容を以下に変更しました。

2019年12月について、取引種別がインターネットショッピングで、取引金額が1000ドル以上の取引について、不正取引が最も多い州を教えてください。

取引種別はcategory列であることは明示せず、shopping_netはインターネットショッピングと言い換え、取引金額が1000ドル以上と列を明示しないでデータとして含まれていない通貨単位がドルであることを含めました。

結果は、カラム名をこちらで補足せず、インターネットショッピングと言い換えても、Amazon Qが内容を理解して読み替えることで、補足した場合と同じクエリを生成してくれました。これだけ気が利くのは素晴らしいです。

より自然な言葉で指示する

まとめ

NL2SQLは生成AIによって業務データの分析を効率化することから、大きな価値を秘めています。Amazon Q generative SQLを試してみた結果から、日本語で極めて自然な自然言語による指示をSQLクエリとして生成する能力を有することが分かったため、Amazon Redshiftに格納された業務データの活用が一層進むことが期待されます。一方、Amazon Q generative SQLはあくまでAWSマネージメントコンソール上での実装であり、自社アプリのUI上でこうした自然言語による分析を行いたいニーズもあるでしょうし、単にSQLクエリを生成するだけでなく、データの内容に対する示唆を与えてほしいとも感じます。そうした機能は「Generative BI capabilities in Amazon QuickSight」の方で対応されているようにも見受けられますが、様々な形で実装される生成AIの機能を引き続き試してみたいと思います。

2024年のAWS Ambassador Global Summitに参加しました!

目次

AWS Ambassador Global Summitとは?

毎年9月はAWS Ambassador(※)にとって一年の中でre:Inventと同様に楽しみな月であり、心待ちにしている時期です。今年で6回目の開催になるとのことですが、毎年9月にアメリカのシアトルでAWS Ambassador Global SummitというAWSのプライベートイベントが開催されます。
イベントではAWS Ambassadorに選出された世界中のAWSパートナー企業の社員がシアトルのAmazon本社に招待されます。AWS Ambassador同士のコミュニケーションとコラボレーションを通してAWSパートナーの創造性を高めることで、AWSパートナーエコシステムを強化することを目的にAWSさんが主催しています。

イベントの中では、AWSのエグゼクティブやAWSサービスの開発リーダー、技術エバンジェリストなど複数のAWS社員の方が登壇して、AWSの会社としてのビジョン、今後の戦略、開発中のサービス機能、サービスのロードマップなどの技術的な紹介を行ってくれます。必ずしも技術的な話題だけでなく、イノベーションを起こす企業文化の説明や、AWSが自社のプロモーションのためにどのようにソーシャルメディアを活用しているか、効果的なプレゼンテーションを行う方法論、インクルージョンダイバーシティ・エクイティ(IDE)に対するAWSの取り組みなど、技術論以外のカルチャーセッションのような興味深い内容についても多くの時間が割かれます。

AWS社員の方からの一方向の説明だけでなく、AWSパートナーネットワークが提供するプログラムについてAWSパートナーが実際にどう感じているかをラウンドテーブルの中でフィードバックしたり、手を挙げた任意のAWS Ambassadorが自由なテーマで話すライトニングトークなど、双方向でのコミュニケーションとコラボレーションがAWSとパートナー企業間、AWSパートナー企業間で起こるようにセッションが構成されています。

また、上記のようなセッションが日中行われるのに対して、夕方以降は一年を通してAWS Ambassadorコミュニティの中で顕著な貢献を行ったメンバーや、AWS資格を全て取得したメンバーに対する表彰セッションが行われたり、AWSがホストになってシアトル市内の観光ツアーを行うなど、AWSパートナー同士のネットワーキングを行うための機会も提供されます。サミットの最終日の夜にはAmbassadorがスポーツバーに集まり、ボーリングやビリヤード、テーブルゲームをしながら親睦を深めるのが恒例となっています。

The Sphereのツアーにも参加できます

AWS Ambassador Partner Programとは?

AWS Ambassador Partner Programは、AWSパートナー企業の技術専門家を選出し、コミュニティを形成するためのAWSのグローバルプログラムです。

AWS Ambassador Partner Programでは、AWS 認定取得数、社内技術者育成への貢献、案件支援内容、ブログや書籍の執筆活動、セミナーでの講演等の外部活動を通じて、AWSの技術的専門知識を共有することで自身の技術スキル・クラウド知識を研鑽するプロフェッショナルを認定しています。

AWS Ambassadorに選出されるためには、以下の条件を満たす必要があります。

  • Advanced Tier以上のAWSパートナー企業所属(1社最大3名)
  • AWS技術に関する難易度の高い情報発信を定期的に実施
  • AWSパートナー企業のエグゼクティブ、AWS社員からの推薦
  • 日本においては、Japan AWS Top Engineersを受賞するレベルでAWSビジネスに対する貢献を行ったエンジニア・セールスなどのプロフェッショナル

AWS Ambassadorに選出されるベネフィットは次の通りです。

  • 限定プライベートウェビナーへの招待
  • グローバルなAWS Ambassadorsコミュニティへの参加
  • AWS re:Invent@ラスベガスへの参加パス
  • グローバルや地域でのイベント招待
  • AWS Ambassadorが集合するAmbassador Global Summit@シアトルへの招待
  • $500分のAWSクレジットの提供

AWS Ambassadorの表彰ディナーはAmazonオフィスの屋上で開かれます

2024年のAWS Ambassador Global Summitの内容

AWSさんとのNDAがあるため、セッションの内容について詳細に触れることはできませんが、2024年は3日間に渡って開催され、100名以上のAWS Ambassadorが世界中から参加しました。日本からも30名以上参加しましたが、AWSビジネスにおける日本の比重の高さが伺えるかと思います。

25個のプログラムが用意され、毎日朝から夜までみっちり実施される非常に密度の高い3日間になりました。

NDAに触れない範囲で私の受けた印象を記すと、やはり昨今の生成AIの流れに乗って「Amazon Q Developer」によっていかに開発者の開発体験が向上し、開発効率が高まるかといった点を強調するセッションが複数回行われていたことが印象的でした。具体的には、「Amazon Q Developer Agent for Code Transformation」によってプログラミング言語のバージョンを迅速に更新したり、ドキュメントが整備されていない既存のコードを読み取って説明を加えることで保守性を向上するなどです。「Amazon Q Developer」がAWSさんの最も推したいサービスであることが伝わってきました。
※開発者体験のセッションでは、タイミング的にCodeCommit等のサービスの新規利用受付停止の理由を問う質問が多く出ていました。

また、生成AIを取り巻くIDEの議論であったり、イノベーションを起こすための企業文化や個人の発想がどうあるべきかといった論点のセッションも多かったと思います。昨年のGlobal Summitでもトピックが用意されていましたが、「Women in Tech」というテーマで、長年アメリカのエンタメ業界に身を置いていた女性プロデューサーがAWSに移籍されるまでのキャリアを語るといった興味深い内容もありました。Amazon/AWSは大変な利益を生み出している会社であるので、人材の幅の広さや取り組みの多様さには驚かされます。

AWS Ambassador Global SummitはAWSパートナーのためのイベントであることから、AWS Marketplaceを利用してどのようにAWSパートナーが収益を上げることができるか、AWS顧客がそれぞれのニーズに最適なAWSパートナーを見つけるためのマッチングをどのように行えるかといった施策に対するビジネスサイドの取り組みの説明もありました。また、AWSパートナーのパートナーソリューションの開発と登録を促進するための説明も行われました。

一方、AWSパートナー側のトピックとしてAWS Ambassador20名以上がライトニングトークを行いましたが、世界中のAWS Ambassdorがそれぞれの関心に基づいて様々な内容を話されていました。自身の国におけるAWSエンジニアの育成とコミュニティ形成における取り組みについてであったり、Amazon Bedrockを用いて作成した生成AIアプリのアーキテクチャーの説明や、普段自身が携わっているAWS案件の内容についてなど、どれも興味深いもので、国が違ってもAWSに関わるエンジニアは同じような考えや課題を持っていることが分かり、ネットワーキングの価値が発揮されていると感じました。

Global Summitのアジェンダが公開されていれば良かったのですが、今年は一般公開されていないようなので、どのような顔ぶれの方がどういった内容を話されるのかイメージが浮かぶように昨年のアジェンダのリンクを以下に貼ります。
https://partnerdiscoverydays.splashthat.com/

初日のシアトルは晴天で、ケリーパークからマウントレーニアが美しくはっきりと望めました

まとめ

NDAがあるためプログラムの詳細に触れられず随筆のような内容になりましたが、AWS Ambassador Global Summitがどのような趣旨で開催され、AWSパートナー企業のAWS Ambassadorがどういった気付きを得られたかの雰囲気が伝わったかと思います。AWS Ambassador Partner Programに関心を持たれた方は、是非ご自身の所属されている企業でもプログラムに対する取り組みがないか確認してみてください。AWSパートナーエコシステムの裾野の広さと強いネットワークに感心することと思います。

発見的ガードレールの効率的な通知の構成方法

目次

はじめに

前回の記事で、AWS Control Towerを利用することでAWSマルチアカウント管理の利便性を高めることができることを紹介しました。本記事では、AWS Control Towerが既定でセットアップしてくれる機能の一つとして、AWS環境の設定値が発見的ガードレールに非準拠の場合に利用者に通知してくれる機能の使い方と課題、その課題をどのように回避できるかを説明します。

発見的ガードレールとは

発見的ガードレールとは、AWS Configルールを利用したAWS環境の設定値が望ましい状態になっているかを管理する仕組みを指します。「AWS 基礎セキュリティのベストプラクティス」等のAWS Security Hubのセキュリティ標準を有効化した際に、特定のコントロールに設定値が非準拠になってスコアが変動するのも同じ仕組みで実現しています。

AWS Security Hubの場合、運用者が月次などの頻度で能動的にマネージメントコンソールを確認することで最新のスコアを把握し、設定値の是正対応を行うといった運用を想定することができますが、場合によってはAmazon RDSインスタンスがパブリックアクセス可能の設定になっている場合など、あるべき形から逸れた設定値がAWS環境で設定された際に、即座に通知してほしいという要望もあるかもしれません。

そうした要望に応える形で、AWS Control TowerによってAWSマルチアカウント構成によるランディングゾーンを作成すると、発見的ガードレールの非準拠を任意の宛先に通知してくれるSNSトピックが作成されます。このSNSトピックにメールアドレスなどをサブスクライブすれば、設定の非準拠を検知した際に運用者まで任意の方法で通知が行われます。

AWS Control Towerによって作成されるSNSトピック

AWSマルチアカウント構成において発見的ガードレール非準拠の通知はAuditアカウントに集約されます。AuditアカウントにはAWS Control Towerによって用途に応じて3つのSNSトピックが作成されます。

SNSトピック名 説明
aws-controltower-AllConfigNotifications AWS Configの準拠・非準拠の状態変更時・リソースの変更時と、CloudTrailのログデリバリー時に通知が行われる
aws-controltower-SecurityNotifications Control Towerがサポートするリージョンに作成され、リージョン内のAWS Configの準拠・非準拠の状態変更時・リソースの変更時に通知される。このトピックへの通知は、aws-controltower-NotificationForwarderというLambda関数により、aws-controltower-AggregateSecurityNotificationsトピックにフォワードされる
aws-controltower-AggregateSecurityNotifications 各リージョンのaws-controltower-SecurityNotificationsに通知された内容が、aws-controltower-NotificationForwarderのLambda関数によってAWS Control Towerのホームリージョンのaws-controltower-AggregateSecurityNotificationsに集約して通知される

Compliance notifications by SNS in the audit account - AWS Control Tower

Guidance on subscribing to SNS Topics - AWS Control Tower

SNSトピックによる通知はリージョンを跨げないため、間に通知をフォワードするLambda関数を挟んで、一つのSNSトピックに通知を集約する構成となっています。

公式ドキュメントによると、AWS Configの準拠状況を通知する用途なら各リージョンの通知が集約されるaws-controltower-AggregateSecurityNotificationsトピックにサブスクライブすることが推奨されており、より詳細な情報も通知したければaws-controltower-AllConfigNotificationsにサブスクライブするよう記載されています。

しかし、いずれのトピックにサブスクライブした場合も、実際にはかなり大量の通知が飛んでくることになり、運用者がそれらの通知を適切に捌く事は現実的でないのが実情です。というのも、aws-controltower-AggregateSecurityNotificationsトピックにサブスクライブした場合、AWS Security Hubの実態はAWS Configルールであるため、AWS Security Hubのコントロールで非準拠になった設定が全て通知され、単にAWS Control Tower標準の発見的ガードレールの非準拠状況だけでなく、AWS Security Hubの状況まで全て通知されるからです。この状況を避けるために、冒頭ではAWS Security Hubのスコアは運用者が月次などの任意の頻度で能動的に確認する例を記載しました。

一方、aws-controltower-AllConfigNotificationsにサブスクライブすると、それ以上に大量の通知が飛んでくるため、もっと運用が回らなくなります。

具体的には、AWS Configに関する以下の全ての通知が行われます。

  • リソース設定の変更
  • リソース設定履歴のアカウントへの配信
  • 記録対象のリソースの設定スナップショットがアカウントで起動および配信
  • リソースの準拠状態
  • リソースに対してルールの評価が開始
  • AWS Config からアカウントへの通知の配信失敗

Notifications that AWS Config Sends to an Amazon SNS topic - AWS Config

大量の通知が飛んでくることは公式ドキュメントでも触れられており、AWS Control Towerが作成してくれるSNSトピックにサブスクライブして運用者への通知を行うというのは、そのままでは現実的には難しいです。

SNS topics in AWS Control Tower are extremely noisy, by design.

発見的ガードレールの通知運用をどのように行うか

AWS Configルールの通知構成

上記の図は各AWSアカウントで発生した発見的ガードレールを含むAWS Configルールの通知をAWS Control Towerがどのように構成するかを表したものです。

赤枠で囲った範囲がAWS Control Towerが作成する通知の仕組みであり、これまで説明した通知を実現する構成です。各アカウントのAWS Configルールに対してEventBridgeルールが設定され、リージョン内でSNSトピックに通知されます。SNSトピックにはLambda関数がサブスクライブされており、AuditアカウントのControl TowerホームリージョンのSNSトピックに通知が集約されます。

この仕組みにそのまま乗ると、先述の通り運用者に通知が大量に来ることになるため、対応策を考える必要があります。

AWSからは二つの方法が提示されており、一つは追加のLambda関数を作成してaws-controltower-AggregateSecurityNotificationsトピックにサブスクライブすることで、特定の通知内容をフィルターすることです。もう一つはAWS Control Towerが作成するのと同等の通知構成を自身で行い、EventBridgeルールで通知対象をフィルターする方法です。

Administrators who wish to filter out specific types of notifications from an SNS topic can create an AWS Lambda function and subscribe it to the SNS topic. Alternatively, you can set up an EventBridge rule to filter notifications, as described in this support article, How can I be notified when an AWS resource is non-compliant using AWS Config?

Compliance notifications by SNS in the audit account - AWS Control Tower

EventBridgeルールで通知対象をフィルターするなら、AWS Control Towerが作成するaws-controltower-ConfigComplianceChangeEventRuleを編集すれば早いということになりますが、残念ながらその操作はAWSからサポートされません。AWS Control Towerが作成したリソースを利用者が編集することはAWSのサポート外となり、もし編集した場合はControl Towerのバージョンアップや廃止の際に問題が発生する可能性があります。

本記事では、そうした理由からAWS Control Towerが作成するのと同等の構成を自身で作成して、aws-controltower-ConfigComplianceChangeEventRuleに相当するEventBridgeルールにて通知対象をフィルターする方法を紹介します。

AWS Control Towerの発見的ガードレールの準拠状況のみを通知する構成方法

先述の構成図の青枠で囲った範囲のリソースを個別に作成することで、特定の対象のみ通知する構成を行うことができます。本記事では、Control Towerが標準で管理するガードレールの非準拠のみを通知するようフィルターする構成とします。

作成するAWSリソースの設定値と説明を文章で全て網羅することはできないので、必要なリソースを一発でデプロイするCloudFormationテンプレートを記載します。

①Auditアカウントで通知用のSNSトピックを作成する

AWSTemplateFormatVersion: "2010-09-09"
Description: Create Notification SNS Topic for Non Compliant Guardrails

Parameters:
  EmailAddress:
    Description: Email Address for recieving Non Compliant Notification.
    Type: String

  OrgID:
    Description: Organization ID for recieving Non Compliant Notification. e.g. o-xxxxxxxxxx
    Type: String

Resources:
## SNS Topic
  NonCompliantNotificationSNSTopic:
    Type: AWS::SNS::Topic
    Properties:
      TopicName: NonCompliantNotificationTopic

## SNS Topic Policy
  SNSNotificationPolicy:
    Type: AWS::SNS::TopicPolicy
    Metadata:
      cfn_nag:
        rules_to_suppress:
          - id: F18
            reason: "Condition restricts permissions to current account."
    Properties:
      Topics:
        - !Ref NonCompliantNotificationSNSTopic
      PolicyDocument:
        Statement:
          - Sid: __default_statement_ID
            Effect: Allow
            Principal:
              AWS: "*"
            Action:
              - SNS:GetTopicAttributes
              - SNS:SetTopicAttributes
              - SNS:AddPermission
              - SNS:RemovePermission
              - SNS:DeleteTopic
              - SNS:Subscribe
              - SNS:ListSubscriptionsByTopic
              - SNS:Publish
              - SNS:Receive
            Resource: !Ref NonCompliantNotificationSNSTopic
            Condition:
              StringEquals:
                AWS:SourceOwner: !Sub ${AWS::AccountId}
          - Sid: AcceptNotificationsfromwithinOrganization
            Effect: Allow
            Principal:
              "AWS": "*"
            Action: sns:Publish
            Resource: !Ref NonCompliantNotificationSNSTopic
            Condition:
              StringEquals:
                aws:PrincipalOrgID: !Ref OrgID

## SNS Subscription
  NonCompliantEmailSubscription:
    Type: AWS::SNS::Subscription
    Properties:
      Protocol: email
      Endpoint: !Ref EmailAddress
      TopicArn: !Ref NonCompliantNotificationSNSTopic

②ガードレールの非準拠を監視したいアカウントで、非準拠通知に必要なIAMロールを作成する

AWSTemplateFormatVersion: "2010-09-09"
Description: Configure SNS Notification Forward IAM Roles
Parameters:
  SecurityTopicName:
    Type: String
    Default: NonCompliantNotificationTopic
    Description: Security Notification SNS Topic Name.
  AuditAccountId:
    Type: String
    MaxLength: 12
    MinLength: 12
    Description: AWS Account Id of the Audit account.
  RoleName:
    Type: String
    Default: SnsNotificationForwardRole
    Description: SNS Notification Forward IAM Roles.

Resources:
  SnsNotificationForwardLambdaRole:
    Type: "AWS::IAM::Role"
    Properties:
      RoleName: !Sub ${RoleName}
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: Allow
            Principal:
              Service: "lambda.amazonaws.com"
            Action:
              - "sts:AssumeRole"
      Path: "/"
      ManagedPolicyArns:
        - "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
      Policies:
        - PolicyName: sns
          PolicyDocument:
            Statement:
              - Effect: Allow
                Action:
                  - "sns:publish"
                Resource: !Sub arn:aws:sns:*:${AuditAccountId}:${SecurityTopicName}

Outputs:
  SnsNotificationForwardRoleName:
    Description: SnsNotificationForwardRoleName
    Value: !Ref RoleName
    Export:
      Name: SnsNotificationForwardRoleName

③ガードレールの非準拠を監視したいアカウントで、非準拠通知に必要なリソースを作成する

AWSTemplateFormatVersion: "2010-09-09"
Description: Configure Eventbridge Rule, local SNS Topic, notifications forwarding Lambda and CloudWatch Logs events to forward messages from local SNS Topic to Security Topic
Parameters:
  RoleName:
    Type: String
    Default: SnsNotificationForwardRole
    Description: SNS Notification Forward IAM Roles.
  SecurityTopicName:
    Type: String
    Default: NonCompliantNotificationTopic
    Description: Security Notification SNS Topic Name.
  SecurityAccountId:
    Type: "String"
    MaxLength: 12
    MinLength: 12
    Description: AWS Account Id of the Audit account.
  SecurityAccountRegion:
    Type: "String"
    Default: ap-northeast-1
    Description: AWS Control Tower Home Region of the Audit account.
  LogsRetentionInDays:
    Description: "Specifies the number of days you want to retain notification forwarding log events in the Lambda log group."
    Type: Number
    Default: 14
    AllowedValues: [1, 3, 5, 7, 14, 30, 60, 90, 120, 150, 180, 365, 400, 545, 731, 1827, 3653]
  EnableConfigRuleComplianceChangeAlarm:
    Type: String
    Description: "Enable notifications for AWS Config rule compliance status changes"
    Default: true
    AllowedValues:
      - true
      - false

Conditions:
  EnableConfigRuleChangeNotification: !Equals
    - !Ref EnableConfigRuleComplianceChangeAlarm
    - "true"

Resources:
  ForwardSnsNotificationGroup:
    Type: "AWS::Logs::LogGroup"
    Properties:
      LogGroupName: /aws/lambda/LambdaNotificationForwarder
      RetentionInDays: !Ref LogsRetentionInDays

  ForwardSnsNotification:
    Type: "AWS::Lambda::Function"
    DependsOn: ForwardSnsNotificationGroup
    Properties:
      FunctionName: LambdaNotificationForwarder
      Description: SNS message forwarding function for aggregating account notifications.
      Code:
        ZipFile: !Sub |
          from __future__ import print_function
          import boto3
          import json
          import os
          def lambda_handler(event, context):
              #print("Received event: " + json.dumps(event, indent=2))
              region = os.environ.get('region')
              sns = boto3.client('sns', region_name=region)
              subject=event['Records'][0]['Sns']['Subject']
              if subject is None:
                  subject = 'None'
              message = event['Records'][0]['Sns']['Message']
              try:
                  msg = json.loads(message)
                  message = json.dumps(msg, indent=4)
                  if 'detail-type' in msg:
                    subject = msg['detail-type']
              except:
                  print('Not json')
              response = sns.publish(
                  TopicArn=os.environ.get('sns_arn'),
                  Subject='Config非準拠を検知しました',
                  Message=message
              )
              print(response)
              return response
      Handler: "index.lambda_handler"
      MemorySize: 128
      Role: !Sub arn:aws:iam::${AWS::AccountId}:role/${RoleName}
      Runtime: "python3.11"
      Timeout: 60
      Environment:
        Variables:
          # region: !Sub ${AWS::Region}
          # sns_arn: !Sub arn:aws:sns:${AWS::Region}:${SecurityAccountId}:${SecurityTopicName}
          region: !Sub ${SecurityAccountRegion}
          sns_arn: !Sub arn:aws:sns:${SecurityAccountRegion}:${SecurityAccountId}:${SecurityTopicName}

  LocalSecurityTopic:
    Type: AWS::SNS::Topic
    Properties:
      DisplayName: LocalSecurityNotificationTopic
      TopicName: LocalSecurityNotificationTopic

  SNSNotificationPolicy:
    Type: AWS::SNS::TopicPolicy
    Metadata:
      cfn_nag:
        rules_to_suppress:
          - id: F18
            reason: "Condition restricts permissions to current account."
    Properties:
      Topics:
        - !Ref LocalSecurityTopic
      PolicyDocument:
        Statement:
          - Sid: __default_statement_ID
            Effect: Allow
            Principal:
              AWS: "*"
            Action:
              - SNS:GetTopicAttributes
              - SNS:SetTopicAttributes
              - SNS:AddPermission
              - SNS:RemovePermission
              - SNS:DeleteTopic
              - SNS:Subscribe
              - SNS:ListSubscriptionsByTopic
              - SNS:Publish
              - SNS:Receive
            Resource: !Ref LocalSecurityTopic
            Condition:
              StringEquals:
                AWS:SourceOwner: !Sub ${AWS::AccountId}
          - Sid: TrustCWEToPublishEventsToMyTopic
            Effect: Allow
            Principal:
              Service: events.amazonaws.com
            Action: sns:Publish
            Resource: !Ref LocalSecurityTopic

  SNSNotificationSubscription:
    Type: "AWS::SNS::Subscription"
    Properties:
      Endpoint: !GetAtt ForwardSnsNotification.Arn
      Protocol: lambda
      TopicArn: !Ref LocalSecurityTopic

  SNSInvokeLambdaPermission:
    Type: AWS::Lambda::Permission
    Properties:
      Action: lambda:InvokeFunction
      Principal: sns.amazonaws.com
      SourceArn: !Ref LocalSecurityTopic
      FunctionName: !GetAtt ForwardSnsNotification.Arn

  # Enable notifications for AWS Config Rule compliance changes for NON_COMPLIANT
  CWEventRuleComplianceChangeEventForNONCOMPLIANT:
    Type: AWS::Events::Rule
    Condition: EnableConfigRuleChangeNotification
    Properties:
      Name: GRConfigNonCompliant
      Description: "Event Rule to send notification on NON_COMPLIANT Config Rule compliance changes."
      EventPattern:
        {
          "source": ["aws.config"],
          "detail-type": ["Config Rules Compliance Change"],
          "detail":
            {
              "messageType": ["ComplianceChangeNotification"],
              "configRuleName": [{ "prefix": "AWSControlTower_" }],
              "newEvaluationResult": { "complianceType": ["NON_COMPLIANT"] },
            },
        }
      State: ENABLED
      Targets:
        - Id: !Sub "Compliance-Change-Topic"
          Arn: !Ref LocalSecurityTopic
          InputTransformer:
            InputPathsMap:
              "Account": "$.account"
              "resourceId": "$.detail.resourceId"
              "Region": "$.region"
              "Time": "$.time"
              "complianceType": "$.detail.newEvaluationResult.complianceType"
              "configRuleName": "$.detail.configRuleName"
            InputTemplate: |
              "次のリソースで、Configルール(ガードレール)の非準拠を検知しました。"

                     "- Region : <Region> "
                     "- Account : <Account>"
                     "- Time : <Time>"
                     "- resourceId : <resourceId>"
                     "- configRuleName : <configRuleName>"
                     "- complianceType : <complianceType>"
Outputs:
  LocalSecurityTopic:
    Description: Local Security Notification SNS Topic ARN
    Value: !Ref LocalSecurityTopic
  LocalSecurityTopicName:
    Description: Local Security Notification SNS Topic Name
    Value: !GetAtt LocalSecurityTopic.TopicName

※1 通知先をControl Towerのホームリージョンに集約せず、リージョンごとにSNSトピックを作成して通知する場合、①のSNSトピックを通知対象リージョンごとに作成し、③のLambdaNotificationForwarderのLambda関数の環境変数regionとsns_arnをリージョンごとに設定ください。

※2 通知対象を変更する場合は、③のEventBridgeルールのEventPatternを変更ください。

まとめ

本記事では、AWS Control Towerによって設定される発見的ガードレール等の非準拠を通知する仕組みについて解説しました。そして、既定で作成される通知の対象が広範であるため、実運用での利用は厳しいことも説明しました。その課題に対してCloudFormationテンプレートを利用して個別の通知構成を行うことで、運用に耐えうる程度に通知対象を絞る方法について紹介しました。
AWS Control Towerが既定で作成する発見的ガードレールの通知の仕組みをよく理解できていなかった方や、既定の構成とは異なるワークアラウンドをお探しの方のお役に立てば幸いです。

AWS Control TowerのアカウントファクトリーをAWS CLIで利用する

目次

はじめに

AWSを効率的に利用する上で、AWSのマルチアカウントでの管理は欠かせない手法です。本ブログでも度々取り上げているように、Audit, Log Archive, Shared Service, Networkなど特定の機能単位でアカウントを分割して、管理アカウントと合わせてランディングゾーンと呼ばれるセキュリティ統制やスケーラビリティ、運用の効率性などの一貫したガバナンスを構成する方法は、AWSからも推奨される手法です。

What Is AWS Control Tower? - AWS Control Tower

AWSマルチアカウント管理を実践する上で、AWS Organizationsを利用することが前提となりますが、その利便性を更に高めてくれるのがAWS Control Towerです。AWS Control Towerはランディングゾーンの構成を支援するサービスであり、これを利用することで例えば各アカウントのセキュリティ状態を管理するAWS Security Hubのスコアリングに必要となるAWS Configの有効化をまとめて行ってくれたり、Cloudtrailの証跡をOrganizationsでまとめて取得する設定を行うなど、マルチアカウントで利用する上で便利な設定の多くを支援してくれます。

AWS Control Towerの詳細な機能解説は別記事に譲るとして、本記事では、AWS Control Towerの一機能であるアカウントファクトリーの利用について説明します。

AWS Control Towerのアカウントファクトリーとは

アカウントファクトリーを利用することで、マルチアカウント環境におけるAWSアカウントの新規作成が容易になります。AWSアカウントの通常の作成方法は、新規にAWSアカウントを取得する操作を行ってrootユーザーのメールアドレスや支払情報を登録します。既にAWS Organizationsが構成されているのであれば、AWS Organizationsの機能を利用して自身の組織のメンバーアカウントとして新規のAWSアカウントを作成することも可能です。AWS Control Towerでランディングゾーンを構成している環境では、AWS Control Towerの機能であるアカウントファクトリーを利用してAWSアカウントを作成します。

Account Factory

アカウントファクトリーで作成されるAWSアカウントと、スタンドアローンで作成されるAWSアカウントや、AWS Organizationsで作成されるAWSアカウントは何が異なるでしょうか。
アカウントファクトリーで作成されるAWSアカウントには、AWS Control Towerのベースライン設定が自動で反映され、AWS Control Towerの管理下に自動で登録されます。具体的には、下記ドキュメントに記載されているように、冒頭で述べたAWS Configの一括有効化の設定や、Cloudtrail証跡の組織レベルでの有効化、Control Towerを利用する上で必要なIAMロール、サービスロールの作成などがCloudFormation StackSetsでデプロイされます。

  • AWSControlTowerBP-BASELINE-CLOUDTRAIL
  • AWSControlTowerBP-BASELINE-CLOUDWATCH
  • AWSControlTowerBP-BASELINE-CONFIG
  • AWSControlTowerBP-BASELINE-ROLES
  • AWSControlTowerBP-BASELINE-SERVICE-ROLES
  • AWSControlTowerBP-BASELINE-SERVICE-LINKED-ROLES
  • AWSControlTowerBP-VPC-ACCOUNT-FACTORY-V1

Resource Considerations for Account Factory - AWS Control Tower

アカウントファクトリーの自動化手法

アカウントファクトリーによるAWSアカウントの新規作成は、AWSマネージメントコンソールから行うことができますが、ランディングゾーンによるマルチアカウント管理を行っていて、利用者用の新規AWSアカウントを繰り返し払い出す業務を行っている場合、毎回マネージメントコンソールで作成する手作業を省力化したいと思うようになります。
こうしたニーズによって登場したアカウントファクトリーの自動化手法が、Account Factory for Terraform (AFT)です。アカウントファクトリーによるAWSアカウントの新規作成をTerraformとGitOpsの手法で自動化することが可能です。

Provision accounts with AWS Control Tower Account Factory for Terraform (AFT) - AWS Control Tower

AFTが有用である一方、AFTは必要になるサービスが色々ありTerraformが前提となっていることも相まって、もっとシンプルな方法が欲しいと考えることもあるでしょう。

AWS Control Tower Account Factory for Terraform – account provisioning workflow

AFT Architecture - AWS Control Tower

AWSのブログではAFTとは異なる方法でアカウントファクトリーを自動化する方法が紹介されていますが、こちらも仕組化され過ぎていて、もっとシンプルに既存のアカウント申請のワークフローの中に処理を組み込む方法がないかと考えました。

How to automate the creation of multiple accounts in AWS Control Tower | AWS Cloud Operations & Migrations Blog

アカウントファクトリーをAWS CLIから利用する

アカウントファクトリーの実体は、AWS Service Catalogに登録されたポートフォリオと製品です。AWS Service CatalogはCloudFormationテンプレートなど管理者が構成済みのテンプレートを共有し、利用者が定義済みのリソースを簡単にデプロイすることが可能なサービスです。アカウントファクトリーはAWS Service Catalogのポートフォリオの一つとして登録されており、アカウントファクトリーでAWSアカウントを作成する際は、裏でAWS Service Catalogが実行されます。

AWS Control Tower Account Factory Portfolio

AWS Service CatalogにはAPIが用意されており、AWS CLIからも実行が可能となっているため、AWS Control Towerのアカウントファクトリーをシンプルな形で利用したい場合は、AWS CLIからAWS Service Catalogのコマンドを実行すれば良いことになります。AFTの利用についての情報は世の中で多くありますが、AWS Service CatalogのAWS CLIからの利用については情報が乏しいため、この記事に記載します。

aws servicecatalog provision-product \
--product-name "AWS Control Tower Account Factory" \
--provisioning-artifact-name "AWS Control Tower Account Factory" \
--provisioned-product-name "NewAWSAccount" \
--provisioning-parameters "file://AccountFactoryParameters.json"

AWS CLIのコマンドレファレンスは以下になります。

provision-product — AWS CLI 2.15.42 Command Reference

AWS CLIからAWS Control Towerのアカウントファクトリーで新しいAWSアカウントを作成する場合は、上記のようにaws servicecatalog provision-productのコマンドで実行することが可能です。オプションとして必須なのはproduct-name, provisioning-artifact-name, provisioned-product-name, provisioning-parametersになります。
product-name, provisioning-artifact-nameは"AWS Control Tower Account Factory"で固定で指定可能で、provisioned-product-nameには新しく作成するAWSアカウントの名前などを任意で指定することができます。provisioning-parametersファイルの内容はJSONで以下のように記述します。

[
  {
    "Key": "AccountEmail",
    "Value": "xxx@example.com"
  },
  {
    "Key": "AccountName",
    "Value": "New AWS Account"
  },
  {
    "Key": "ManagedOrganizationalUnit",
    "Value": "xxx (ou-xxx)"
  },
  {
    "Key": "SSOUserEmail",
    "Value": "AWSAdmin@example.com"
  },
  {
    "Key": "SSOUserFirstName",
    "Value": "xxx"
  },
  {
    "Key": "SSOUserLastName",
    "Value": "xxx"
  }
]
Key Value
AccountEmail 新しく作成するAWSアカウントのrootユーザー用の一意のメールアドレスを指定
AccountName 新しく作成するAWSアカウントの任意の名前を指定
ManagedOrganizationalUnit 新しく作成するAWSアカウントを組織のどのOUに所属させるかを名前とOU IDで指定
SSOUserEmail IAM Identity Centerで新しく作成するAWSアカウントへのログイン権限を持つSSOユーザーのメールアドレスを指定
SSOUserFirstName IAM Identity Centerで新しく作成するAWSアカウントへのログイン権限を持つSSOユーザーの名を指定
SSOUserLastName IAM Identity Centerで新しく作成するAWSアカウントへのログイン権限を持つSSOユーザーの姓を指定

アカウントファクトリーではAWS IAM Identity Centerの利用を半ば前提としており、パラメーターとして新しく作成するAWSアカウントの既定の管理者としてのSSOユーザーの指定を求められます。このユーザーに実際のメールアドレスを指定すると、IAM Identity Centerに新しいSSOユーザーが作成されるか、同メールアドレスの既存のSSOユーザーに新規AWSアカウントへのログイン権限が付与されます。アカウントファクトリーで新規作成されるAWSアカウントのrootユーザーは作成時点で誰もパスワードを知らない状態になるため、SSOUserEmailに実在しないメールアドレスを指定するなどして、新規AWSアカウントにログインできるユーザーがいない場合は、rootユーザーのパスワードリセットを行って、rootユーザーでアカウントへログインする必要が発生するかもしれません。AWS Organizationsの機能で新規AWSアカウントを作成すると、既定ではOrganizationAccountAccessRoleというスイッチ可能なロールがアカウントに作成されますが、アカウントファクトリーで作成した場合はこの名前のロールは作成されていませんでした。

Accessing member accounts in your organization - AWS Organizations

JSONのパラメーターファイルの中身については、以下のAWSドキュメントにも簡単に説明がありますが、説明がやや分かりにくいところもありました。

Walkthrough: Automate Account Provisioning in AWS Control Tower by Service Catalog APIs - AWS Control Tower

AWS Service Catalogの実行は非同期であるため、以下のようにコマンドの戻り値でStatusがCREATEDとなっていても実際のAWSアカウントの作成は完了していません。

{
    "RecordDetail": {
        "RecordId": "rec-xxxx",
        "ProvisionedProductName": "NewAWSAccount",
        "Status": "CREATED",
        "CreatedTime": "20xx-0x-xxT09:52:49.960000+00:00",
        "UpdatedTime": "20xx-0x-xxT09:52:49.969000+00:00",
        "ProvisionedProductType": "CONTROL_TOWER_ACCOUNT",
        "RecordType": "PROVISION_PRODUCT",
        "ProvisionedProductId": "pp-xxxx",
        "ProductId": "prod-xxx",
        "ProvisioningArtifactId": "pa-xxx",
        "PathId": "lpv3-xxx",
        "RecordErrors": [],
        "RecordTags": []
    }
}

アカウント作成が完了したかは、aws servicecatalog describe-provisioned-productを実行して、StatusがAVAILABLEになったことを確認しましょう。

aws servicecatalog describe-provisioned-product \
--name NewAWSAccount
{
    "ProvisionedProductDetail": {
        "Name": "NewAWSAccount",
        "Arn": "arn:aws:servicecatalog:ap-northeast-1:xxxxxxxxxxxx:stack/NewAWSAccount/pp-xxx",
        "Type": "CONTROL_TOWER_ACCOUNT",
        "Id": "pp-xxx",
        "Status": "AVAILABLE",
        "CreatedTime": "20xx-0x-xxT09:52:49.960000+00:00",
        "IdempotencyToken": "xxx-xxxx-xxxx-xxxx-xxx",
        "LastRecordId": "rec-xxx",
        "LastProvisioningRecordId": "rec-xxx",
        "LastSuccessfulProvisioningRecordId": "rec-xxx",
        "ProductId": "prod-xxx",
        "ProvisioningArtifactId": "pa-xxx"
    },
    "CloudWatchDashboards": []
}

なお、補足としてアカウントファクトリーで作成されたAWSアカウントはアカウントファクトリーで管理されているため、AWS OrganizationsのコンソールからOU移動などを行うと状態の不一致が発生し、OUへのAWSアカウントの再登録が必要になります。(30-40分程度要する)
アカウントファクトリーを使用して作成したAWSアカウントのOUを移動するなどパラメーターファイルで指定した内容を変更する際は、AWS Service Catalogのプロビジョニングされた製品の詳細から更新アクションで変更する必要があります。

まとめ

本記事では、AWS Control TowerのアカウントファクトリーでのAWSアカウントの作成を自動化する手法として、一番シンプルな方法であるAWS Service CatalogをAWS CLIから実行する手法を取り上げました。AWS CLIはシンプルで普遍的な実行方法であるため、既存のアカウント作成ワークフローにも取り込みやすく、AFTのような大きな仕組みをデプロイしなくても良いメリットがあります。
アカウントファクトリーの自動化手法はシンプルな方法が情報としてまとまった形で出てこないものになりますので、同様の要望をお持ちの方のお役に立てば幸いです。