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

~ 第 1 回 AWS Service Catalog を使ったセルフサービス型機能の提供 ~

2024-05-01
ビジネス x クラウド

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

builders.flash 読者の皆さん、こんにちは ! みずほリサーチ&テクノロジーズ株式会社の松尾です。
当社では、AWS Cloud Deployment Kit (AWS CDK) と AWS Service Catalog を活用して AWS の社内向けプラットフォームを構築しています。これから全 2 回に渡って、当社事例をご紹介します。

今回は当社のプラットフォームでなぜ AWS CDK と AWS Service Catalog を採用したのか、またどのように既存資産を移行したのか、ご紹介します。

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


当社プラットフォームの概要

当社には、AWS をベースとしたプラットフォームが 2 種類存在します。

No. 用途 概要
1 みずほ銀行向け​ みずほ銀行向けに、セキュリティ・ガバナンスを利かせた AWS 基盤
2 〈みずほ〉のお客様向け​ お客様の様々な要件へ速やかに対応できるように、発見的統制を重視した AWS 基盤

本記事で紹介するのは、No.2 のプラットフォームです。プラットフォームをプロダクトとして扱う Platform as a Product の考えを取り入れており、スクラムの手法で開発しています。本プラットフォームでは、プロダクトビジョンとして以下を掲げています。

みずほの新規事業創出およびシステム受託開発において、安心して AWS の有用性を発揮し、プロジェクトオーナーやシステム開発者に対して、ビジネス価値の創造に迅速に着手し、集中して開発できるプラットフォームを提供する。​

本プラットフォームの特徴として、開発に必要なアカウント管理やセキュリティ機能を備えた AWS アカウントを迅速に提供します。

全体構成

本プラットフォームでは、AWS Control Tower ランディングゾーンを採用しています。AWS Control Tower は、AWS のベストプラクティスに基づいたマルチアカウント環境を簡単に構築・管理できるサービスです。

本プラットフォームでは、AWS Control Tower がランディングゾーンとして作成するデフォルトアカウントに加えて、プラットフォーム開発者が使用するアカウントや多数のプラットフォーム利用者向けアカウントを作成しています。以下は組織単位(OU)の構成概要です。

OU全体構成

# OUの種類 OUに配置するアカウントの概要
1 Security ログ集約・監査 (AWS Control Tower のデフォルトアカウント)
2 Infrastructure プロダクトの開発環境・資産管理・デプロイ
3 Workload 本番・開発用途のアカウント発行
4 Sandbox 検証用途のアカウント発行
5 Exception 特殊要件でのアカウント発行[1]
6 Test プロダクトのデプロイテスト・SCP のテスト
7 Deleted 削除予定のアカウント

各アカウントは、AWS Control Tower が定義するガードレールと呼ばれるポリシーに従って運用されます。一方で、個別リソースの操作や許可リージョンなどを柔軟に統制するため、アカウント発行用の OU を 3 つに分けて、個別に SCP を適用しています。

開発体制

前述の通り、本プラットフォームの開発では、アジャイル開発の代表的な手法であるスクラムを採用しています。スクラムは、短い期間で反復的に開発とフィードバックを行うことで、お客様のニーズに応える高品質なソフトウェアを提供する方法です。

本プラットフォームの開発では、2 週間を 1 スプリントとしています。スプリントの最初には、プラットフォーム利用者などステークホルダーの要求事項を基に、優先順位をつけてバックログに登録します。スプリントの最後には、開発した成果物をステークホルダーにデモし、フィードバックを受け取ります。

実際の開発チーム (DEV) は、様々なメンバーの入れ替わりや増減がありましたが、現在は 4 名で落ち着いています。やむを得ず、全員が他業務と兼務しており、1 週間の半分 (2.5 日) を本プラットフォームの開発・運用にコミットしています。プロジェクト発足から 2 年半経過し、少しずつですが確実に前進しています。

構築方法

AWS Config や AWS CloudTrail などのランディングゾーンを形成する上で必要なサービスは、AWS Control Tower Account Factory でアカウント発行した際、自動的に有効化されます。

AWS Control Tower 管理外のリソースについては、AWS CDK を使用して構築しています。AWS CDK は、TypeScript や Python などの言語で AWS リソースを定義・デプロイできるツールです。本プラットフォームでは TypeScript を採用しており、専用の開発アカウントから、Web IDE の AWS Cloud9 を使って開発しています。実際に AWS CDK で開発しているリソースは以下などです。

  • プラットフォーム提供機能 (各アカウントへ配布)
    • 不正操作検知・障害情報の通知集約
    • 当社セキュリティチェックリストに基づいた追加設定 (追加の通知、違反リソースの自動修復など)
    • etc…
  • プラットフォーム利用者向けのドキュメント (静的サイト)
  • アカウント発行の申請や問合せ用のフォーム (Redmine)

サービス提供の課題

プラットフォームの機能を提供する方式は、大きく分けると一般的に以下の 2 種類です。

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

当初、上記のフルサービス型を採用しており、各アカウントに対してプラットフォームチームが機能を提供・管理していました。

フルサービス型のデプロイ例

しかし、このフルサービス型での提供方法には課題がありました。例えば、ちょっとした機能追加やランタイム更新などをしようとしても、本番用途で使用しているアカウントでは、デプロイ前に影響確認が必要となります。そのため、プラットフォーム利用者ごとにリリース調整が発生し、サービスデリバリのサイクルが円滑に回りませんでした。せっかく、スクラムで少しずつ機能拡張しているのに、速やかにリリースできなければスクラムの利点を活かせません。フルサービス型では、アカウント数の増加についていけないのは明白でした。

また、セキュリティ面でも懸念がありました。各アカウントへクロスアカウントアクセス可能な高権限のデプロイ用 IAM ロールを作成していましたが、AWS CDK を利用しないアカウントにとっては、セキュリティリスクの一つです。利用者にとって不要な高権限ロールの作成は、できる限り避けたいです。


解決方法

フルサービス型では前述の課題があったため、他の方法を検討しました。
そこで採用したのが、AWS Service Catalog です。AWS Service Catalog の概要は以下の通りです。

Q: AWS Service Catalog とは何ですか?
AWS Service Catalog では、IT 管理者が承認された製品のカタログを作成および管理し、そのカタログをエンドユーザーに配布できます。エンドユーザーは、その後、パーソナライズされたポータルから必要な製品にアクセスできるようになります。管理者は、各製品にアクセスできるユーザーを制御して、組織のビジネスポリシーへのコンプライアンスを実施できます。
出典: https://aws.amazon.com/jp/servicecatalog/faqs/

Service Catalog イメージ

AWS Service Catalog には、「ポートフォリオ」と「製品」というリソース区分があります。ポートフォリオは、製品の集合体であり、アクセス権限や起動方法などを制御します。一方、製品とは配布を目的とした AWS サービス群です。AWS CloudFormation テンプレートなどで定義し、各社のセキュリティ・コンプライアンスに沿った製品をセルフサービス型で組織内に展開できます。

AWS Service Catalog の採用理由

AWS Service Catalog をプラットフォーム開発に採用した理由は以下の 4 点です。

① セルフサービス型による開発サイクルの高速化

AWS Service Catalog を導入すると、利用者はマネジメントコンソールから数回のクリックで、利用者の必要とする機能をデプロイまたは更新できます。これは正にセルフサービス型の機能です。利用者が機能の更新タイミングを決定するため、プラットフォーム提供者の開発サイクルに影響を与えません。セルフサービス型はプラットフォームエンジニアリングにおいても重要であり、開発者の体験を向上させるために欠かせません。また、本方式では利用者側にデプロイ権限があればよいため、クロスアカウントアクセス可能な高権限の IAM ロールが不要になるのも嬉しい点です。

なお、AWS 規範ガイダンスでは AWS CDK と Service Catalog の利用サンプルが、DevOps の推奨パターンとして掲載されています。cdk-nag などセキュリティ・コンプライアンスチェックの静的解析ツールを導入すれば、セキュリティのシフトレフトを実践できるため、DevSecOps の実現にも近づきます。

AWS CDK を使用して AWS Service Catalog ポートフォリオと製品のデプロイを自動化する »

② AWS CDK によるプラットフォーム提供側の優れた開発者体験

プラットフォーム提供者は、AWS CDK のハイレベルコンストラクトを使用して、AWS Service Catalog 製品を構築できます。通常スタックとは異なる仕様の ProductStack に少々慣れが必要なものの、一般的な AWS CDK の使い方と殆ど変わらぬ感覚で開発作業を進めることが可能です。

2022 年にも導入を検討したのですが、当時は AWS Service Catalog 製品で Lambda 関数ハンドラコードなどの Assets を直接扱えませんでした。現在はアップデートにより、AWS Service Catalog でも AWS CDK の Assets を簡単に扱えるようになっており、製品を快適に開発できます。また、当社の既存資産には Assets が多数含まれていましたが、本アップデートにより Assets が含まれた資産を簡単に AWS Service Catalog へ移行できました。

③ AWS Organizations 連携による組織単位(OU)への簡単な共有

AWS Organizations との連携により、AWS Service Catalog 製品を組織単位(OU)へ共有できます。フルサービス型の機能提供を行っていたときは、アカウントごとに配布リソースのバージョン管理が必要でした。更新した機能を一括で OU に展開できるのは、AWS Service Catalog の大きな利点です。

また、委任設定を通じて、AWS Service Catalog の管理権限を他アカウントへ委譲することができます。当社では、マルチ管理アカウントのベストプラクティス に従い、AWS Service Catalog 管理用のアカウントを設けて AWS Control Tower 管理アカウントから権限を委任しています。

④ AWS Control Tower 管理リソース・BLEA での利用実績

AWS Control Tower によって管理されるリソースでは、AWS Service Catalog が使用されています。Account Factory から新規アカウントを発行する際、AWS Control Tower が管理する AWS Service Catalog 製品が裏側でデプロイされます。また、BLEA(baseline-environment-on-aws)でも、AWS Control Tower 環境における AWS Service Catalog のユースケースがコード付きで掲載されています。

これらの利用実績は、AWS Control Tower 環境における AWS Service Catalog の効果を示していると考えました。

構成概要

AWS Service Catalog 関連の構成図は以下の通りです。

構成概要

開発からサービス利用までの大まかな流れ

  1. プラットフォーム開発者が AWS CDK で AWS Service Catalog 製品を開発し、AWS CodeCommit 上にプルリクエストを作成
  2. プラットフォーム管理者が AWS CodeCommit のプルリクエストを承認・マージ
  3. AWS CodePipeline で承認された資産をビルド・テスト・デプロイ
  4. Organization 連携で AWS Service Catalog ポートフォリオをプラットフォーム利用者のメンバーアカウントへ共有[2]
  5. プラットフォーム利用者がコンソールから利用・更新したい製品を指定
  6. 指定された AWS Service Catalog 製品の AWS CloudFormation スタックをデプロイ

プラットフォーム利用者が製品の管理を行うのがポイントです。前述の通り、デプロイ・更新タイミングは利用者に委ねられるため、プラットフォーム提供者の開発サイクルに影響を与えません。プラットフォーム利用者にとっても、必要な時に欲しい機能をすぐに利用できるため、提供者・利用者双方にとって Win-Win の仕組みだと言えます。


AWS CDK 既存資産の AWS Service Catalog 移行方法

本章では、当社が AWS CDK の既存資産をどのように AWS Service Catalog 製品へ移行したか具体的に解説します。

移行方針

AWS Service Catalog 製品へ移行する前は、機能ごとにスタックを分けていました。一方、AWS Service Catalog への移行時には、既存のスタックを自作 Construct に変換して、製品用のスタックである ProductStack へ紐づけました。移行前後における Construct ツリーのイメージは以下の通りです。

移行前後の Construct ツリー

ProductStack とは、AWS Service Catalog 製品のリソースを定義する特殊なスタックです。利用者の AWS アカウントへデプロイされる単位であり、素直に移行するならば、機能ごとに ProductStack を定義すべきです。上図では、3 つの機能があるので、3 つの ProductStack を定義するのが自然です。

しかし、当社ではアカウント発行時にデプロイする機能を 1 つの ProductStack に纏めました。なぜなら、アカウント発行と連動する Account Factory Customization (AFC) において、デプロイできる ProductStack が 1 つのみだったためです。結局、AFC の採用は見送ったのですが、詳細は次回紹介します。

AWS CDK テストのススメ

AWS Service Catalog 製品へ移行する際、AWS CDK で以下 2 種類のテスト実施を推奨します。

# 名称 概要
1 スナップショットテスト AWS CDK で生成された AWS CloudFormation テンプレートの断面を保持し、変更前後の差分を確認
2 アサーションテスト AWS CDK で生成された AWS CloudFormation テンプレートで、各種リソースが想定通りの設定値か確認

スナップショットテスト を使用すると、自作 Construct への変換前後で簡単に差分を確認できます。また、既存のスタックでテンプレートの期待値チェックを行う アサーションテスト があると、変換後に同様の要件が満たされているか確認しやすく、更に安心して移行できます。

既存のスタックを自作 Construct へ変換すること自体はシンプルです。スタックの class で extends を cdk.Stack から Construct へ変換し、Props の extends cdk.StackProps を削除すれば、基本の変換は完了です。以下のようなイメージです。(aws-cdk のバージョンは 2.133.0 です)

(変換前)既存スタック

import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';
import { Topic }from 'aws-cdk-lib/aws-sns';
import { Rule } from 'aws-cdk-lib/aws-events'; 
import { SnsTopic } from "aws-cdk-lib/aws-events-targets";

export interface FooProps extends cdk.StackProps {
  bar: string;
  baz: string;
}

export class FooStack extends cdk.Stack {
  constructor(scope: Construct, id: string, props: FooProps) {
    super(scope, id, props);

    // The code that defines your stack goes here
    // example resource
    const topic = new Topic(this, 'QuxTopic');
    
    const eventRule = new Rule(this, 'QuxRule', {
      ruleName: "QuxRule",
      eventPattern: {
        source: ['foo.bar'],
      },
    });
    eventRule.addTarget(new SnsTopic(topic));
  }
}

(変換後)自作 Construct

import { Construct } from 'constructs';
import { Topic }from 'aws-cdk-lib/aws-sns';
import { Rule } from 'aws-cdk-lib/aws-events'; 
import { SnsTopic } from "aws-cdk-lib/aws-events-targets";

// extends cdk.StackProps を削除
export interface FooProps {
  bar: string;
  baz: string;
}

// extends を cdk.Stack から Construct に変更
export class Foo extends Construct {
  constructor(scope: Construct, id: string, props: FooProps) {
    // super() から props の引数を削除
    super(scope, id);

    // Define construct contents here
    // example resource
    const topic = new Topic(this, 'QuxTopic');
    
    const eventRule = new Rule(this, 'QuxRule', {
      ruleName: "QuxRule",
      eventPattern: {
        source: ['foo.bar'],
      },
    });
    eventRule.addTarget(new SnsTopic(topic));
  }
}

※ AWS Service Catalog 製品を定義する ProductStack では、上記のように変換した自作 Construct を呼び出します。本稿ではコード例を割愛しますが、詳細については BLEA をご覧ください。

次にテストコードを変換します。テストコードの変換も、基本的にはプロダクトコードと同じでシンプルです。変換した自作 Construct をテスト用の空スタックに紐づければ、テストを実行できます。

(変換前)スタックのテストコード

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

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

  beforeEach(() => {
    // WHEN
    const stack = new FooStack(new cdk.App(), "FooStack", {
      env: { account: "012345678901", region: "ap-northeast-1" },
      bar: "aa",
      baz: "bb",
    });
    // THEN
    template = Template.fromStack(stack);
  });

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

  // アサーションテスト
  describe("リソース数の確認", () => {
    test("SNSトピックが1つ存在する", () => {
      template.resourceCountIs("AWS::SNS::Topic", 1);
    });

    test("Eventsルールが1つ存在する", () => {
      template.resourceCountIs("AWS::Events::Rule", 1);
    });
  });
  
  describe("Eventsルールのプロパティチェック", () => {
    test("ターゲットがSNSトピックである", () => {
      template.hasResourceProperties("AWS::Events::Rule", {
        Name: "QuxRule",
        Targets: Match.arrayWith([
          Match.objectLike({
            Arn: {
              Ref: Match.stringLikeRegexp("QuxTopic"),
            },
          }),
        ]),
      });
    });
    
    test("イベントパターンのソースがfoo.barである", () => {
      template.hasResourceProperties("AWS::Events::Rule", {
        Name: "QuxRule",
        EventPattern: {
          source: ["foo.bar"],
        },
      });
    });
  });
});

(変換後)自作 Construct のテストコード

import { Template, Match } from "aws-cdk-lib/assertions";
import * as cdk from "aws-cdk-lib";
import { Foo } from "../lib/constructs/foo";

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

  beforeEach(() => {
    // テスト用の空スタックを定義
    const stack = new cdk.Stack(new cdk.App(), "TestStack", {
      env: { account: "012345678901", region: "ap-northeast-1" },
    });
    // WHEN
    // 自作 Construct をテスト用の空スタックに紐づけ
    new Foo(stack, "Foo", {
      bar: "aa",
      baz: "bb",
    });
    // THEN
    template = Template.fromStack(stack);
  });

  test("スナップショットテスト", () => {
    expect(template).toMatchSnapshot();
  });
  
  // アサーションテスト ※以下は変換前と同じなので省略
});

自作 Construct への変換により、Construct ID が変わるため、スナップショットテストなどで注意が必要です。例えば、Amazon SNS トピックで QuxTopic40037234 となっていた Construct ID が、{自作 Construct ID}QuxTopic68D45007 のように変わる可能性があります。QuxTopic の前にプレフィックスが追加されるのは、自作 Construct 化により Construct ID を追加で定義する必要があるためです。そのため、スナップショットテストでは、Construct ID に関する差分が相応に発生します。単純な変換であれば、Construct ID 以外の差分は発生しないはずです。

一方、アサーションテストでは stringLikeRegexp(pattern) のメソッドを使っており、 Match.stringLikeRegexp("QuxTopic") のような形で Construct ID を部分一致させています。そのため、上記例のアサーションテストであれば Construct ID 変更の影響を受けません。

もし、既存のテストが全くなかったとしても、スナップショットは簡単に実装できます。そのため、スナップショットだけでも実施することを強く推奨します。

上記は簡略な例ですが、実際のリソース定義は階層化された Construct や分岐処理などで複雑なことが多く、正しく移行できているか不安になります。一方、スナップショットに加えてアサーションテストもあると、より安心して移行できます。スナップショットテストと異なり、アサーションテストでは期待通りの設定値になっているか、細かくチェックします。Construct の構造が変わっても要件が満たされているか確認しやすいのが、アサーションテストの利点です。

当社では以下のような観点で移行前からアサーションテストを書いていたため、安心して移行することができました。

  • リソース数
  • リソース間の連携
  • 分岐処理
  • 命名規則
  • セキュリティなど重要な非機能要件の値

アプリのドメインでないインフラの設定値は優先度が低く、細かいアサーションテストを書くべきでないという意見もあります。しかし、プラットフォームチームからすれば、利用者へ配布するクラウドネイティブな機能の設定は重要なドメインであり、単なるインフラの設定値ではないと考えています。従って、プラットフォームチームが積極的にアサーションテストを書くことはオススメです。

少し脱線しますが、アサーションテスト最大の魅力は変更容易性が高まることであり、少しずつ機能を拡張するアジャイル開発との相性が抜群です。クラウド・アジャイルの組み合わせに自動化されたテストが加わると、とても強力です。そもそも、アサーションテストを書いたことがない方も安心してください。現代は Amazon CodeWhisperer など生成 AI のサポートが強力なので、自然言語のテストケース名からテストコードを生成してくれます[3]。まだ経験のない方はぜひアサーションテストに挑戦してみてください!

なお、AWS Service Catalog 製品が正しく動作するかという観点では、AWS CDK Integration Tests (integ-tests) を活用するというのも有効だと思います。AWS CDK integ-tests を使うと、AWS CDK で定義したリソースを実際の AWS 環境にデプロイして、その挙動を検証することができます。

執筆時点における AWS CDK integ-tests は実験的モジュールの位置づけですが、aws-cdk ライブラリのアルファモジュールなどで積極的に活用されています。実環境で AWS Service Catalog 製品の検証は必須であるため、当社では AWS Service Catalog 製品のテストに AWS CDK integ-tests を活用できないか検討しています。

cdk-nag のススメ

cdk-nag とは、AWS CDK と統合したセキュリティ・コンプライアンスチェックツールです。各社のポリシーに沿った独自ルールパックを生成できます。デプロイ前に、ルール違反している設定に気付けるため、非常に便利です。

AWS Service Catalog 製品のセキュリティ・コンプライアンスをチェックしたい場合、cdk-nag の利用を推奨します。ただし、AWS Service Catalog 製品で cdk-nag を適用するには、通常と異なる方法が必要です。詳細は、以下のリンクをご覧ください。

AWS Service Catalog製品をcdk-nagで静的解析する~図解付き〜 »


おわりに

本稿では、AWS CDK と AWS Service Catalog を使ったセルフサービス型機能の提供について、紹介しました。サービス提供形態で AWS CDK と AWS Service Catalog を採用したことにより、プラットフォーム提供側も優れた開発者体験を得つつ、セルフサービス型機能の開発サイクル高速化を実現することができました。AWS におけるセルフサービス型機能の提供方法、並びに AWS CDK 既存資産の AWS Service Catalog 移行方法について、具体的なステップをご理解いただけたのではないかと思います。

次回は、なぜ当社が Account Factory Customization (AFC) を使えなかったのか、どのようにアカウント発行と連動させて AWS Service Catalog 製品をデプロイしたのか紹介します。次回もお楽しみに !

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

  1. その他の OU で許可していない特殊リージョンの利用など。 ↩︎

  2. 実際の設定では、共有先にOUを指定。 ↩︎

  3. https://qiita.com/y_matsuo_/items/f3f7f0ce6836808d512b ↩︎

参考資料


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

筆者プロフィール

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

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

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

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