みずほリサーチ&テクノロジーズが AWS CDK で実装したマルチアカウント管理の仕組み

~ 第 2 回 AWS Step Functions を活用した AWS アカウント発行処理の自動化 ~

2024-06-04
ビジネス × クラウド

Author : 松尾 優成 (みずほリサーチ&テクノロジーズ株式会社, 調査役/マネージャー)

builders.flash 読者の皆さん、こんにちは!みずほリサーチ&テクノロジーズ株式会社の松尾です。
当社では、AWS Cloud Deployment Kit (AWS CDK) と AWS Service Catalog を活用して AWS の社内向けプラットフォームを構築しています。前回の記事 では「AWS Service Catalog を使ったセルフサービス型機能の提供」について、紹介しました。

本記事では、当社が実装したセルフサービス型のアカウント発行方法について、検討の経緯を追いながらご紹介します。本稿では前回の記事を前提としていますので、未読の方はそちらも是非ご覧ください。


プラットフォーム構成の概要

本プラットフォームでは、AWS Control Tower ランディングゾーンを採用しています。AWS Control Tower ランディングゾーンでは AWS IAM Identity Center も包含されるため、AWS IAM Identity Center でユーザー・グループを一元的に管理できます。

しかし、当社では AWS IAM Identity Center でユーザー・グループの直接的な管理をしていません。AWS 以外も含めた各種サービスへのアクセスでシングルサインオンを実現するため、Microsoft Entra ID を活用してユーザー・グループを一元管理しています。本プラットフォームでは AWS IAM Identity Center を Microsoft Entra ID と SAML 連携させることで、各 AWS アカウントへのシングルサインオンを実現しています。

SAML 連携構成

従って、AWS アカウント発行時には AWS に関する作業だけではなく、他チームが管理している Microsoft Entra ID 関連の作業も必要です。


AWS アカウント発行処理の課題

当社では、AWS Control Tower の Account Factory でアカウント発行後、共通で必要となるセキュリティやアカウント管理のリソースをデプロイしています。元々、複数のアカウントを切り替えながら、マネジメントコンソールや AWS CloudShell の操作を手動実行していました。

AWS Service Catalog 導入以前におけるアカウント発行処理の流れ

  1. 利用者のアカウント発行申請を受付
  2. 他チームに Microsoft Entra ID 関連作業を依頼
  3. AWS Control Tower 管理アカウントにアクセスし、Account Factory から新規アカウント発行
  4. AWS Organizations で、新規アカウントに利用者情報のタグを付与
  5. 新規 AWS アカウントにアクセスし、管理者ロールに付与する IP 制限用ポリシー・AWS CDK デプロイ用ロールを作成
  6. AWS Control Tower 管理アカウントにアクセスし、AWS IAM Identity Center でアクセス権限関連の作業を実施
  7. 資産管理・デプロイ用アカウントにアクセスし、AWS CodeCommit で新規アカウントの接続先を更新後、共通資産をデプロイ
  8. 利用者へアカウント発行完了連絡をメール送信

AWS アカウント発行に伴う初期設定の内、上記 3 以降の作業だけで 3~4 時間を要していました。これは、利用者へ迅速に AWS アカウントを提供するというプラットフォームの目的に反しており、操作ミスのリスクも高まっていました。そこで、AWS アカウント発行処理の自動化を模索しました。


調査・検討

プラクティスの調査

AWS Black Belt Online Seminar AWS Control Tower 機能紹介編 では、アカウント発行のカスタマイズ方法として以下が挙げられています。

# 名称 概要 IaC
1 Account Factory Customization (AFC) Account Factory に連動して、追加の AWS Service Catalog 製品をデプロイ AWS CloudFormation, Terraform
2 AWS CloudFormation StackSets 組織または OU にアカウントが追加されたタイミングで、StackSets が自動デプロイ AWS CloudFormation
3 Customizations for AWS Control Tower (CfCT) AWS Control Tower のライフサイクルや AWS CodeCommit の更新に連動して、追加リソースをデプロイ AWS CloudFormation
4 Account Factory for Terraform (AFT) Account Factory を拡張し、Terraform で追加リソースをデプロイ Terraform

Terraform を利用していないため、最初に AFT (#4) の選択肢を除外しました。2023 年 7 月の検討時点では、AWS CDK で StackSets のハイレベルコンストラクトが正式リリースされていなかったことから、StackSets (#2) の選択肢を見送りました (2024 年 5 月 1 日時点では GitHub - cdklabs/cdk-stacksetsで StackSets のハイレベルコンストラクトが v0.0.150 として公開されています)。CfCT (#3) についても、裏側で StackSets が使われていたことから、同様に見送りました。

当社ではセルフサービス型のサービス提供を実現するため、AWS Service Catalog を導入しています。AWS Service Catalog との相性を考えると、AFC (#1) が最も現実的であったため、調査・検証を進めました。調査・検証の中で議論となったのは、以下の点です。

Account Factory Customizations (AFC) に関する考慮事項

AFC の考慮事項として、ホームリージョンに登録された 1 つの AWS Service Catalog 製品のみデプロイできます。マルチリージョンにデプロイする場合、Stack Sets でホームリージョンの製品が展開されるため、単一のテンプレートで記述する必要があります。

一方、当社のプラットフォームではマルチリージョン利用を前提としており、リージョン毎に異なる設定のリソースを定義していました。例えば、以下などのケースです。

  • ホームリージョンに通知を集約(Amazon EventBridge ルールや Amazon SNS トピックなど)
  • 大阪リージョンのみ対応していないリソースについて、除外設定
  • etc...

これらのリソースは AWS CDK のレイヤーで分岐処理をしていたため、StackSets における分岐処理の考え方と合わないという問題がありました。

AWS CDK における分岐処理の考え方

以下記事の図でも示されている通り、AWS CDK ではプログラミング言語の if ステートメントなどを使用して、AWS CDK レイヤー内で条件分岐を表現します。

環境面やリージョン毎にテンプレートが分かれるため、AWS CDK で生成されたテンプレートは条件分岐がなく、静的になります。※トークン による最終的な合成処理はあるものの、ここでは考慮外とします。

上記は実行環境ごとに分岐させていますが、リージョンで分岐させる場合も同様の考え方です。例えば、us-east-1リージョンのみに定義したいリソースがある場合、以下のように記述します[1]。(aws-cdk のバージョンは2.137.0です。)

import * as cdk from 'aws-cdk-lib';
import { Rule } from 'aws-cdk-lib/aws-events';
import { Construct } from 'constructs';

export class MyStack extends cdk.Stack {
  constructor(scope: Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);
    
    // 各リージョンに共通のリソース
    new Rule(this, "GuardDutyFindingsDetectionRule", {
      eventPattern: {
        source: ["aws.guardduty"],
        detailType: ["GuardDuty Finding"],
      },
    });
    
    if (cdk.Stack.of(this).region === "us-east-1") {
      // ここに"us-east-1"リージョン固有の処理を定義
      new Rule(this, "AccessKeyCreationDetectionRule", {
        eventPattern: {
          source: ["aws.iam"],
          detail: {
            eventSource: ["iam.amazonaws.com"],
            eventName: ["CreateAccessKey"],
          },
        },
      });
    }
  }
}

静的なテンプレートの特性により、AWS CloudFormation スタックのデプロイ時に条件分岐は原則発生しません。そのため、条件分岐の確認には AWS CDK のアサーションテストが効果的です。以下は、リージョン毎の分岐処理を確認するアサーションテスト例です。

import { Template, Match } from "aws-cdk-lib/assertions";
import * as cdk from "aws-cdk-lib";
import { MyStack } from "../lib/myStack";

describe("Fooのテスト(us-east-1)", () => {
  let template: Template;

  beforeEach(() => {
    // WHEN
    const stack = new MyStack(new cdk.App(), "MyStack", {
      env: { account: "012345678901", region: "us-east-1" },
    });
    // THEN
    template = Template.fromStack(stack);
  });

  test("スナップショットテスト", () => {
    expect(template).toMatchSnapshot();
  });

  // 分岐処理の確認
  test("AccessKey発行検知用Eventsルールが存在する", () => {
    template.hasResourceProperties("AWS::Events::Rule", {
      EventPattern: {
        source: ["aws.iam"],
        detail: {
          eventSource: ["iam.amazonaws.com"],
          eventName: ["CreateAccessKey"],
        },
      },
    });
  });
});

describe("Fooのテスト(ap-northeast-3)", () => {
  let template: Template;

  beforeEach(() => {
    // WHEN
    const stack = new MyStack(new cdk.App(), "MyStack", {
      env: { account: "012345678901", region: "ap-northeast-3" },
    });
    // THEN
    template = Template.fromStack(stack);
  });

  test("スナップショットテスト", () => {
    expect(template).toMatchSnapshot();
  });

  // 分岐処理の確認
  test("AccessKey発行検知用Eventsルールが存在しない", () => {
    template.hasResourceProperties("AWS::Events::Rule", {
      EventPattern: Match.not({
        source: ["aws.iam"],
        detail: {
          eventSource: ["iam.amazonaws.com"],
          eventName: ["CreateAccessKey"],
        },
      }),
    });
  });
});

当社では、このような考え方に沿って分岐処理を実装していました。

StackSets による分岐処理の考え方

一方、StackSets で分岐処理させるためには、AWS CloudFormation テンプレート内に条件関数を組み込む必要があります。先ほどの例を StackSets で実現するには、以下のようなテンプレートになります。(条件分岐の部分のみ抜粋)

条件関数を組み込んだ AWS CloudFromation テンプレート例

AWSTemplateFormatVersion: "2010-09-09"
Conditions:
  IsUSEast1: !Equals [ !Ref "AWS::Region", "us-east-1" ]
Resources:
  NotifyAccessKeyCreation:
    Type: 'AWS::Events::Rule'
    Condition: IsUSEast1
    Properties:
      EventPattern:
        source:
          - 'aws.iam'
        detail:
          eventSource:
            - 'iam.amazonaws.com'
          eventName:
            - 'CreateAccessKey'

上記を AWS CDK で実装する場合、以下のようなコードになります。

条件関数を組み込んだ AWS CDK プロダクトコード

import * as cdk from 'aws-cdk-lib';
import { Rule, CfnRule } from 'aws-cdk-lib/aws-events';
import { Construct } from 'constructs';

class MyStack extends cdk.Stack {
  constructor(scope: Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    // 各リージョンに共通のリソース
    //(省略)

    // 条件を定義
    const isUSEast1 = new cdk.CfnCondition(this, 'IsUSEast1', {
      expression: cdk.Fn.conditionEquals(cdk.Aws.REGION, 'us-east-1')
    });
    
    const rule = new Rule(this, 'NotifyAccessKeyCreation', {
      eventPattern: {
        source: ['aws.iam'],
        detail: {
          eventSource: ['iam.amazonaws.com'],
          eventName: ['CreateAccessKey']
        }
      }
    });

    // リソースに条件を追加
    (rule.node.defaultChild as CfnRule).cfnOptions.condition = isUSEast1;
  }
}

上記の通り、AWS CDK のプロダクトコードからプログラミング言語の if ステートメントがなくなるため、AWS CloudFormation で分岐処理が実行されます。つまり、AWS CDK のレイヤーではデプロイするリソースが定まりません。また、AWS CDK の特徴であった静的なテンプレートという特性が薄まるため、前述のようなアサーションテストで分岐処理の期待値を確認することはできません。

AWS CDK に AWS CloudFormation の条件関数を組み込む?

これまでの内容をまとめると、AFC でマルチリージョンデプロイを実現する場合、StackSets 対応として AWS CDK レイヤーの分岐処理を条件関数に書き換える必要があります。しかし、実際の資産は前述例のようなシンプルな形ではなく、多数のリソースと依存関係を含んでいるため、複雑です。分岐処理の期待値を確りと確認していた既存のコードを捨ててまで、条件関数を組み込むのかは非常に悩ましいです。

ここで、AWS CDK のベストプラクティスに振り返ってみます。以下は、AWS CDK のベストプラクティスにおける一節です。

デプロイ時ではなく、合成時に決定する
AWS CloudFormationでは (Conditionsや{ Fn::If }、Parametersによって) デプロイ時に動的に挙動を決めることができます。AWS CDKでもこれらの仕組みはある程度扱えるようになっていますが、実際にはこれらを使わないことをお勧めします。どのConstructを生成するかなど、すべての決定は合成 (cdk synth) する時に AWS CDKアプリケーションで行うようにしましょう。Fn.ifの代わりにプログラミング言語のifを使用したり、CfnParametersの代わりに関数のパラメータを使用したりすることができます。その理由は、AWS CloudFormationでは、値の種類や値に対して実行できる操作がかなり限定されているからです。
出典: AWS CDKでクラウドアプリケーションを開発するためのベストプラクティス

上記の通り、AWS CDK において、Conditions や Fn::If などは非推奨とされています。条件分岐のテスト観点からも、ベストプラクティスに沿って AWS CDK レイヤーで分岐処理を実装した方がよいと判断しました。

そこで、StackSets を使わずに AFC へ対応させる案がないか、検討しました。例えば、ホームリージョンの AWS Service Catalog 製品にカスタムリソースを組み込み、他リージョンの製品を起動するといった内容です。しかし、実運用においては AWS Service Catalog 製品の継続的な更新も考慮しなければなりません。AFC でデプロイされた AWS Service Catalog 製品を更新する場合、プラットフォーム提供側での対応が必須です。(フルサービス型)

サービスの提供方式

# 名称 概要
1 フルサービス型 プラットフォームチームが機能を提供・管理
2 セルフサービス型 プラットフォーム利用者がカタログから機能を選択して利用

前回の記事で触れた通り、当社では製品の更新タイミングをプラットフォーム利用者へ委ねるため、セルフサービス型の AWS Service Catalog を採用しました。AWS Service Catalog の導入により、プラットフォーム利用者ごとのリリース調整がなくなるため、プラットフォーム利用者・提供者双方の開発サイクルが高速化します。それにも関わらず、AFC を利用するとフルサービス型の提供方式に戻ってしまうため、また同じ問題に直面してしまいます。統制観点であれば AFC は良い選択肢ですが、プラットフォームの方針に沿わないため、断念しました。

セルフサービス型プラットフォームの実装

調査の結果、AWS Control Tower ユーザーガイドに掲載されている以下チュートリアルに辿り着きました。

チュートリアル: Service Catalog API による AWS Control Tower のアカウントプロビジョニングの自動化 - AWS Control Tower (amazon.com)

チュートリアルの動画 では、Account Factory の実態となる図の Service Catalog 製品に対して、AWS CLI から ProvisionProduct API を実行する一連のフローが紹介されています。

画像をクリックすると拡大します

本手法を踏襲すれば、自作の AWS Service Catalog 製品もデプロイできます。また、ProvisionProduct API でデプロイされた自作の AWS Service Catalog 製品については、プラットフォーム利用者が任意のタイミングで更新できます (セルフサー ビス型)。

従って、Account Factory の AWS Service Catalog 製品を含めた一連のアカウント発行処理について、AWS API で自動化することに決定しました。チュートリアルを参考に具体的な実装を考慮した結果、AWS Step Functions を採用しました。AWS Step Functions の採用理由は以下の通りです。

  • 複雑な処理の定義や実行状況をビジュアルワークフローで視覚化することによる認知負荷の軽減
  • AWS Step Functions の AWS SDK 統合 により、ステートマシンから直接 API を実行できるため、Lambda 関数の数を抑制可能[2]
  • ステートマシンで簡易的なバリデーションを実装可能
  • ローコードツール AWS Step Functions ワークフロースタジオ による検証のしやすさ

ワークフローの詳細イメージが固まるまでは、ローコードツールのワークフロースタジオを活用して、検証を進めました。最終的には AWS CDK で AWS Step Functions のワークフローを定義しました。


アカウント発行処理の自動化概要

以下では、AWS Step Functions を活用した AWS アカウント発行処理の自動化について、具体的なフローを紹介します。

構成図

構成図は以下の通りです。

実運用では、以下 1 ~ 2 を完了後にプラットフォーム運用者がステートマシンを実行するだけで、3 ~ 8 に該当する処理が自動化されます。

(再掲)

AWS Service Catalog 導入以前におけるアカウント発行処理の流れ

  1. 利用者のアカウント発行申請を受付
  2. 他チームに Microsoft Entra ID 関連作業を依頼
  3. AWS Control Tower 管理アカウントにアクセスし、Account Factory から新規アカウント発行
  4. AWS Organizations で、新規アカウントに利用者情報のタグを付与
  5. 新規 AWS アカウントにアクセスし、管理者ロールに付与する IP 制限用ポリシー・AWS CDK デプロイ用ロールを作成
  6. AWS Control Tower 管理アカウントにアクセスし、AWS IAM Identity Center でアクセス権限関連の作業を実施
  7. 資産管理・デプロイ用アカウントにアクセスし、AWS CodeCommit で新規アカウントの接続先を更新後、共通資産をデプロイ
  8. 利用者へアカウント発行完了連絡をメール送信

元々、手作業で複数のアカウントを切り替えながら作業していましたが、AWS Step Functions の導入により AWS Control Tower 管理アカウントからの実行のみで完結するようになりました。

ワークフロー

ワークフロー図は以下の通りです。本図は、AWS Step Functions ワークフロースタジオで出力しています。
(記事掲載用に一部脚色していますが、実際のワークフローと殆ど変わりません。)

上記ワークフローの処理は大きく 5 つに区分されます。以下では、バリデーションや並列処理などの各処理の工夫点を、1 つずつ紹介します。

(1) InputValidation

最初にステートマシンへの入力項目に漏れや誤りがないか、簡易的なバリデーションを実行します。アカウント発行処理は気軽にやり直しできないため、本フェーズにおけるパラメータの事前チェックは重要です。

ご参考として、ステートマシンの実行時に渡すパラメータ例を紹介します。

# 項目名 設定値 許可パターン
1 SSOUserEmail アカウント発行実施者の SSOUserEmail "*@*"
2 SSOUserFirstName アカウント発行実施者の名前 入力値の存在チェックのみ
3 SSOUserLastName アカウント発行実施者の名字 入力値の存在チェックのみ
4 ManagedOrganizationalUnit 新規アカウントを配置するOU 名(OU_ID) "Workload(ou-a-1)" or "Test(ou-a-2) or ...
5 SystemName 新規アカウントのシステム名 入力値の存在チェックのみ
6 Env 新規アカウントの環境区分 "prod" or "dev" or ...
7 RootEmailAddress 新規アカウントにおけるルートユーザーのメールアドレス "*@*"
8 BillingEmailAddress 新規アカウントの請求先メールアドレス "*@*"
9 ContactEmailAddress 新規アカウントの連絡用メールアドレス "*@*"

(2) GetGroupId

前提条件となる Microsoft Entra ID のグループ作成・同期が完了しているか、AWS IAM Identity Center のグループに対して確認します。AWS IAM Identity Center に新規アカウント用のグループ ID がなければ、後続のアサインメント作成を実行できないため、アカウント発行を実行せずにステートマシンが停止します。

(3) CreateAccount

Account Factory の AWS Service Catalog 製品を起動し、新規 AWS アカウントを発行します。製品の起動には時間がかかるため、完了するまで待機します。

(4) Parallelism

以下 3 つの処理を並列実行します。

(4-1) RegisterAccountTags

AWS Organizations で管理されている AWS アカウントのメタデータに、プラットフォーム利用者の情報をタグとして登録します。登録する内容は、ステートマシン起動時にパラメータとして渡した環境区分や連絡用メールアドレスなどです。

(4-2) ProvisionCommonProducts

共通で必要となるセキュリティやアカウント管理のリソースを配布するため、自作の AWS Service Catalog 製品をデプロイします。まず、製品の最新バージョンを取得します。その後、取得したバージョンを指定して製品を起動します。製品の起動には時間がかかるため、完了するまで待機します。

直列で複数のリージョンにデプロイすると時間がかかるため、Map を使用して、並列実行しています。

(4-3) CreateAssignmentWithAllowedIp

AWS Service Catalog 製品で IP 制限用 IAM ポリシーを作成後、AWS IAM Identity Center でアサインメントを設定します。製品の最新バージョンを取得・デプロイする流れは、(4-2)と同じです。アサインメントの設定により、プラットフォーム利用者向けの許可セット(IAM ロール)が新規アカウントに作成されます。この許可セットでは、プラットフォーム利用者のアクセス場所を制限するために、IP 制限のカスタマー管理ポリシーを付与しています[3]

なお、本フェーズでデプロイする AWS Service Catalog 製品では、セルフサービスで許可したい CIDR を入力できます。アカウント発行時点では社内の CIDR に絞っていますが、特権をもつプラットフォーム利用者が自由なタイミングで、製品の設定から CIDR を変更できます。

(5) SendEmailAccountCreationComplete

アカウント発行処理が正常に完了した旨を、プラットフォーム利用者と運用者に通知します。Lambda 関数でメッセージを整形し、Amazon Simple Email Service (SES) 経由で E メールを送信します。

アカウント発行処理自動化の効果

元々、複数人体制の手作業で約 3 ~ 4 時間かかっていた処理が、AWS Service Catalog および AWS Step Fucntions の導入により、20 分ほどで完了するようになりました。ワークフローで自動チェックや並列処理を取り入れた結果、多数の AWS アカウント発行依頼に対しても速やかに対応できるようになりました。

アカウント発行時点で、社内ルールに沿ったセキュリティ機能やアカウント管理の設定が有効化されているため、プラットフォーム利用者はビジネス価値の創造へ迅速に着手できます。これは、正にプラットフォームが思い描いていたプロダクトビジョンにマッチします。

アカウント発行の更なる自動化を実現するためには AWS 側だけでなく、Microsoft Entra ID 側の対応も必要です。従って、現在は全体の自動化に向けた取組も検討しています。


おわりに

本稿では、AWS Control Tower 環境のアカウント発行処理において、自作の AWS Service Catalog 製品を配布する手法について紹介しました。アカウント発行処理で AWS Step Functions を採用したことにより、セルフサービス型の機能提供を維持しつつ、迅速な AWS アカウントの提供を実現できたことについて、ご理解いただけたのではないかと思います。

「みずほリサーチ&テクノロジーズが AWS CDK で実装したマルチアカウント管理の仕組み」の連載について、ご紹介は以上となります。本連載の実現にあたり、アマゾン ウェブ サービス ジャパン合同会社の能仁信亮様、builders.flash 編集部の皆様には、多大なるご支援と協力を賜りました。また、アマゾン ウェブ サービス ジャパン合同会社の高野賢司様には、AWS Dev Day 2023 Tokyo の Ask the Speaker で多数の技術的アドバイスをいただきました。心より感謝申し上げます。読者の皆様にはご愛読を賜り、誠にありがとうございました。

本稿がどなたかのお役に立てれば幸いです。

参考資料


  1. あくまでサンプルであり、実環境で機能するリソース定義ではない点にご注意ください。 ↩︎

  2. ステートマシンでは、クロスリージョンで AWS API を実行できない点にご注意ください。 ↩︎

  3. 本制限の副作用として、AWS CloudShell などアクセス元 IP が AWS サービスになる操作は実行できません。従って、AWS CloudShell などを利用する際は、権限を絞った IAM ロールで作業してもらうようにプラットフォーム利用者へ促しています。 ↩︎


builders.flash メールメンバーへ登録することで
AWS のベストプラクティスを毎月無料でお試しいただけます

筆者プロフィール

松尾 優成
みずほリサーチ&テクノロジーズ株式会社
先端技術研究部 兼 プロジェクト推進部 調査役/マネージャー

入社後、国際系金融システムの開発・保守業務を担当。その後、CCoE 活動を経て、様々な社内 AWS 案件を推進。
現在は AWS の社内共通プラットフォーム整備に従事。その他、〈みずほ〉グループ横断コミュニティ「コクリエ」を運営。

AWS を無料でお試しいただけます

AWS 無料利用枠の詳細はこちら ≫
5 ステップでアカウント作成できます
無料サインアップ ≫
ご不明な点がおありですか?
日本担当チームへ相談する