メインコンテンツに移動

builders.flash

脱・手動 CIDR 管理 ! インシデントから学んだ Amazon VPC IP Address Manager による NetDevOps の実践 ~ みずほ銀行が AWS CDK で実装したマルチアカウント管理の仕組み 第 5 回

2026-05-07 | Author : 松尾 優成 (株式会社みずほ銀行 上席主任研究員)

Abstract image featuring a variety of colorful wooden blocks stacked and arranged on the right, fading into a pastel gradient background on the left. Represents diversity, structure, and creativity, suitable for a service catalog or case study context.

はじめに

builders.flash 読者の皆さん、こんにちは ! 株式会社みずほ銀行の松尾です。  

(※ 2026 年 4 月 1 日付で、みずほリサーチ&テクノロジーズ株式会社は株式会社みずほ銀行へ統合しました)

当社では AWS Cloud Development Kit (AWS CDK) を活用して、マルチアカウント環境の管理基盤を構築・運用しています。これまでの連載では、AWS Service Catalog によるセルフサービス型機能の提供や、AWS Step Functions を活用したアカウント発行自動化などを紹介してきました。

今回は、インシデントをきっかけに導入した NetDevOps アプローチの実践例をご紹介します。

 

X ポスト » | Facebook シェア » | はてブ »



builders.flash メールメンバー登録

builders.flash メールメンバー登録で、毎月の最新アップデート情報とともに、AWS を無料でお試しいただけるクレジットコードを受け取ることができます。

今すぐ登録 »

背景

当社のプラットフォームでは、複数のテナント (プラットフォーム利用者 ) に対して AWS アカウントを提供しています。各テナントは独自のシステムを開発・運用しており、社内ネットワークと閉域接続する場合は CIDR (IPアドレス範囲 ) の重複を避けるために調整します。社内ネットワークを管理するネットワークチームからは、当社の AWS プラットフォーム用に十分な規模の専用アドレスプール (CIDR) が事前に払い出されていました。

当初、私たちは CIDR の管理表を手動更新していました。テナントから CIDR 払い出しの申請を受けると、管理表を確認して空いている CIDR 範囲を割り当てていました。

しかし、ある日ヒューマンエラーによるインシデントが発生しました。払い出し済みの CIDR レンジ (管理対象範囲 ) から外れる誤った CIDR をテナントに伝えてしまったのです。厳密なチェックプロセスを経ることなく、誤った情報でテナントが開発に着手していました。すでにプロジェクトが動いており変更が困難だったため、社内ネットワークチームと調整の上、例外的に継続利用することになりました。

この経験から、人の目視確認に頼る運用からの脱却が急務となりました。

Missing alt text value

手動 CIDR 管理に潜むリスク

インシデントを振り返ると、手動 CIDR 管理には以下のリスクが潜んでいました。

  1. ヒューマンエラーの発生
    - 管理表の更新内容に対する確認漏れ
    - 複数担当者間でのコミュニケーションミス

  2. 運用負荷の増大
    - テナント数増加に伴う管理コストの増大
    - 手動の申請対応がボトルネックに

  3. 可視化・監査の困難さ
    - 実際の AWS 環境と管理表が一致していることを担保しづらい
    - 払い出した CIDR の利用追跡に難あり

これらの課題を解決するため、私たちはスモールスタートで NetDevOps アプローチの導入を決定しました。


 

NetDevOps の導入

NetDevOps とは、DevOps の原則をネットワーク運用に適用する手法です。Infrastructure as Code (IaC) とバージョン管理を使って構成を担保し、自動チェックとパイプラインで変更を安全かつ迅速に展開します。この仕組みにより、ネットワーク変更 (CIDR 払い出し) のミスや手戻りを抑えられるため、サービス提供までの流れがよりスムーズになります。

いきなり NetDevOps を全面導入しようとすると、設計や実装に相応の時間がかかってしまいます。そこで、当社では NetDevOps をスモールスタートで導入することにしました。今回は CIDR 払い出しフローの自動化に絞り、Amazon VPC IP Address Manager (IPAM) と AWS CDK を組み合わせて NetDevOps を実現しました。

(画像出典: NetDevOps: A modern approach to AWS networking deployments)

Missing alt text value

Amazon VPC IP Address Manager とは

Amazon VPC IP Address Manager (IPAM) は、AWS が提供する IP アドレス管理サービスです。IPAM を導入することで、以下のメリットが得られます。

  • 組織全体でのIP管理 : AWS Organizations 連携により、全アカウントの Amazon VPC CIDR を一元管理
  • CIDR 割り当て : IPAM プールの範囲内から重複のない CIDR を払い出し
  • 階層的なプール構造 : 用途別にプールを階層化し、柔軟な管理を実現
  • 監査とコンプライアンス : CIDR 割り当て履歴の追跡、重複検知


IPAM は新規環境だけでなく、既存環境でも導入しやすいのが魅力です。既存 Amazon VPC の CIDR 利用状況も IPAM で検出できるため、段階的に導入できます。詳細は Amazon VPC IP Address Manager (IPAM) AWS Black Belt Online Seminar をご参照ください。

注意点として、IPAM でプライベート IP アドレスを管理する場合、有償のアドバンストティアが必要となります。詳細は Amazon VPC の料金 をご参照ください。本記事では割愛しますが、AWS Organizations 全体の CIDR 管理として、事前のコスト設計や対象範囲の検討も重要です。


(画像出典 : Amazon VPC IP Address Manager マネジメントコンソール)

Missing alt text value

CIDR 定義と階層構造の例

IPAM にはプールという概念があり、CIDR 範囲を階層的に定義できます。最上位プールから用途別プール、さらにテナント個別プールへと階層化することで、柔軟な CIDR 管理が可能です。

当社の AWS プラットフォームでは社内ネットワークと重複しないように、十分な規模の専用アドレスプールを確保しています。専用アドレスプールのセグメントを「10.0.0.0/16」と仮定し、IPAM で用途別に階層化してプール管理するイメージを図に示します。

実際の運用では、「テナント個別払い出し用 (10.0.16.0/20) 」 (第 2 階層) からテナントごとの /24 プール (第 3 階層) を作成し、AWS Resource Access Manager (AWS RAM) 経由で各テナントアカウントに共有しています。

テナント側では共有された/24 プールを参照して、Amazon VPC を構築できます。管理側は、/24 単位の割当と共有を Git の変更履歴で追跡しつつ、IPAM  のマネジメントコンソールで実利用も確認できます。

Missing alt text value

Git 中心の CIDR 払い出しフロー

IPAM の設定値を定義する IaC ツールは、これまでの連載と同様に AWS CDK を利用しています。また、ソースコードの管理には GitHub Enterprise を利用しています。これらを活用し、Git を中心とした CIDR 払い出しフローが実現できました [*1]。

    [*1] : 前提として、テナントユーザーからプラットフォームチームへ申請してもらう運用にしています。

      このフローは、NetDevOps の各要素を実装したものです。「コードによる定義 (IaC ) 」・「Git によるバージョン管理」・「CI/CD による自動デプロイ」という一連の流れにより、手動運用のリスクを排除しています。

        また、テナントの CIDR 利用状況は、管理アカウントから IPAM のマネジメントコンソールで確認可能です。NetDevOps ではネットワークリソースの払い出しだけでなく、反映後に状態を観測できることも重要です。

        IPAM 構成図

        IPAM バリデーションによる誤り検知

        もし、管理範囲外の CIDR 指定や CIDR 重複などの不整合があった場合、デプロイ時に CloudFormation で IPAM プールのバリデーションエラーが発生します。誤った設定がテナントへ伝わる前に検知できるため、インシデントの再発を防止できます。

        bash
        # CIDRがIPAMプール範囲外だった場合のエラーメッセージ例
        10:00:00 AM | CREATE_FAILED        | AWS::EC2::IPAMPool         | TenantIpamPoolSample
        ❌ Resource handler returned message: "IpamPoolCidrFailureReason(Message=The CIDR is not available in the parent pool.)" 
        

        セルフサービス化の検討

        IPAM ではセルフサービスで、CIDR を利用してもらう運用も可能です。事前に第 2 階層相当のプールを OU (組織単位) へ共有しておき、セルフサービスで空きの CIDR から自由に利用してもらう方式です。ただし、以下理由でセルフサービス化を見送りました。

          • AWS IPAM はテナントに馴染みが薄く、セルフサービスだとサポートコストが増大する懸念あり
          • CIDR 払い出しの頻度は多くなく、払い出しを管理者が確実に把握しておきたい

           

           

          実装

          ここからは当社のソースコードから一部を抜粋して、具体的な実装方法を解説します。以降のコード例では、説明に不要な import 文の一部を省略しています。

          #
          ライブラリ
          バージョン
          1

          AWS CDK CLI

          2.1100.3

          2

          AWS CDK ライブラリ

          2.233.0

          3

          TypeScript

          5.9.3

          ディレクトリ構成

          AWS CDK プロジェクトの基本的なディレクトリ構成は以下の通りです。cdk init コマンドで生成される標準構成をベースにしています。

          bash
          network-repo/
          ├── bin/
          │   └── app.ts           # CDK アプリケーションのエントリーポイント
          ├── lib/
          │   ├── ipamStack.ts     # IPAM スタック定義
          │   └── pipelineStack.ts # CI/CD パイプラインスタック定義
          ├── test/
          │   └── ipamStack.test.ts # 単体テスト
          ├── parameter.ts          # CIDR 定義などのパラメータファイル
          ├── cdk.json              # CDK 設定ファイル
          ├── package.json
          └── tsconfig.json

          通常の CIDR 払い出し運用では、parameter.ts のみを編集します。初期構築後のスタック定義 (lib/ 配下) やテストコード (test/ 配下) は、仕様変更時にのみ修正します。

          コードベースの CIDR 管理

          CIDR 定義は、TypeScript のパラメータファイル (parameter.ts) で管理しています。実際のコード例を簡略化して示します。

          typescript
          export const TenantCidrDefinition = [
            {
              accountName: 'ProdTenant', // テナント識別名
              cidr: '10.0.224.0/24',     // 割り当てる CIDR
              accountId: '123456789012', // テナントの AWS アカウント ID
            },
            {
              accountName: 'DevTestTenant',
              cidr: '10.0.225.0/24',
              accountId: '222233334444',
            },
            // 新規 CIDR 追加時はここに追記
          ];

          CIDR 払い出し時、上記のシンプルな配列定義のみを更新します。通常の運用では IPAM リソースの複雑な定義を意識する必要がありません。なぜなら、後述のスタック側に IPAM プール定義の繰り返し処理が組み込まれているためです。

          IPAM スタックの定義

          以下に IPAM スタックのサンプルコード (ipamStack.ts) を掲載します。(*)

          typescript
          export class IpamStack extends Stack {
            readonly operatingRegions = [{ regionName: 'ap-northeast-1' }];
          
            constructor(scope: Construct, id: string, props?: StackProps) {
              super(scope, id, props);
              
              // IPAM 本体
              const ipam = new ec2.CfnIPAM(this, 'Ipam', {
                operatingRegions: this.operatingRegions,
                tier: 'advanced', // AWS Organizations 連携・監査機能を利用
                // IP 追跡対象外の OU を指定 (コスト抑制のため)
                defaultResourceDiscoveryOrganizationalUnitExclusions: [{
                  organizationsEntityPath: 'o-a1b2c3d4e5/r-f6g7h8i9j0example/ou-ghi0-awsccccc/*',
                }],
              });
              
              // 最上位プール(第1階層) 
              const overallPool = new ec2.CfnIPAMPool(this, 'OverallPool', {
                addressFamily: 'ipv4',
                ipamScopeId: ipam.attrPrivateDefaultScopeId,
                provisionedCidrs: [{ cidr: '10.0.0.0/16' }],
                tags: [{ key: 'Layer', value: '1' }], // タグで階層を可視化
              });
              
              // テナント個別払い出し用プール(第2階層) ※実際は第2階層も繰り返し処理で定義しているが省略
              const tenantPool = new ec2.CfnIPAMPool(this, 'TenantPool', {
                addressFamily: 'ipv4',
                ipamScopeId: ipam.attrPrivateDefaultScopeId,
                sourceIpamPoolId: overallPool.attrIpamPoolId,
                provisionedCidrs: [{ cidr: '10.0.224.0/20' }],
                autoImport: false, 
                tags: [{ key: 'Layer', value: '2' }],
              });
              
              // パラメータファイルから各テナント用プール(第3階層)を生成
              TenantCidrDefinition.forEach((tenant) => {
                const individualPool = new ec2.CfnIPAMPool(this, `${tenant.accountName}Pool`, {
                  addressFamily: 'ipv4',
                  ipamScopeId: ipam.attrPrivateDefaultScopeId,
                  sourceIpamPoolId: tenantPool.attrIpamPoolId,
                  provisionedCidrs: [{ cidr: tenant.cidr }],
                  autoImport: true, // 第3階層は特定テナントアカウント専用のため自動インポート有効
                  allocationDefaultNetmaskLength: 24,
                  allocationMinNetmaskLength: 24,
                  allocationMaxNetmaskLength: 28,
                  tags: [{ key: 'Layer', value: '3' }],
                });
                
                // AWS RAM 共有設定
                new CfnResourceShare(this, `${tenant.accountName}Share`, {
                  name: tenant.accountName,
                  allowExternalPrincipals: false,
                  principals: [tenant.accountId], // 特定テナントアカウントに共有
                  resourceArns: [individualPool.attrArn],
                  permissionArns: ['arn:aws:ram::aws:permission/AWSRAMDefaultPermissionsIpamPool'],
                });
              });
            }
          }

          * AWS CDK 標準ライブラリ2.233.0時点において、IPAM は L1 Construct のみ提供されています。

          TenantCidrDefinition.forEach の繰り返し処理により、通常運用でコードの修正は不要です。parameter.ts の配列を編集するだけで、新しい IPAM プールと AWS RAM 共有設定が自動生成されます。

          単体テスト

          AWS CDK で以下のシンプルな単体テストを定義しています。

          • スナップショットテスト : 生成される AWS CloudFormation テンプレートの差分を検知
          • アサーションテスト : IPAM 設定の一部プロパティが想定通りか検証 (インポート設定の有無など)
          • cdk-nag  : セキュリティルールに準拠しているか検証

          ここでは、スナップショットテストに着目して解説します。スナップショットテストはとても簡単に実装できるので、ぜひ導入をオススメします。以下のように、とても少ないテストコード (ipamStack.test.ts) で実装可能です。

          テストコード

          typescript
          describe('IpamStackのテスト', () => {
            let template: Template;
            let stack: cdk.Stack;
          
            beforeAll(() => {
              const app = new cdk.App();
              stack = new IpamStack(app, 'Ipam', {
                env: { account: '012345678901', region: 'ap-northeast-1' },
              });
              template = Template.fromStack(stack);
            });
          
            test('スナップショットテスト', () => {
              expect(template).toMatchSnapshot();
            });
          
            // その他テストは省略
          });

          テストの実行

          テナントの CIDR 定義を追加後に npm test を実行すると、スナップショットテストの結果を確認できます。スナップショット未更新の場合はテストが失敗し、追加リソースを確認できます。

          bash
          FAIL  test/ipamStack.test.ts
            ● IpamStackのテスト › スナップショットテスト:ipam
          
              - Snapshot  -  0
              + Received  + 55
          
              +     "RamResourceShareDevTestTenant": {
              +       "Properties": {
              +         "AllowExternalPrincipals": false,
              +         "Name": "DevTestTenant",
              +         "PermissionArns": [
              +           "arn:aws:ram::aws:permission/AWSRAMDefaultPermissionsIpamPool",
              +         ],
              +         "Principals": [
              +           "222233334444",
              +         ],
              +         "ResourceArns": [
              +           {
              +             "Fn::GetAtt": [
              +               "TenantIpamPoolDevTestTenant",
              +               "Arn",
              +             ],
              +           },
              +         ],
              +       },
              +       "Type": "AWS::RAM::ResourceShare",
              +     },
              +     "TenantIpamPoolDevTestTenant": {
              +       "Properties": {
              +         "AddressFamily": "ipv4",
              +         "AllocationDefaultNetmaskLength": 24,
              +         "AllocationMaxNetmaskLength": 28,
              +         "AllocationMinNetmaskLength": 24,
              +         "AutoImport": true,
              +         "ProvisionedCidrs": [
              +           { "Cidr": "10.0.225.0/24" },
              +         ],
              +       },
              +       "Type": "AWS::EC2::IPAMPool",
              +     },

          この差分により、IPAM プール (AWS::EC2::IPAMPool) と AWS RAM (AWS::RAM::ResourceShare) が指定したテナント分だけ追加されることを確認できます。差分を確認したうえで、意図した変更であれば npm test -- -u でスナップショットを更新します。スナップショット更新後は、コミットログやプルリクエストの差分で新規追加分を確認できます。

          CI/CD パイプライン

          CI/CD パイプラインでは CDK Pipelines を利用しています。以下に、パイプラインスタックのコード (pipelineStack.ts) を簡略化して示します。

          CDK Pipelines を使用した継続的インテグレーションと継続的デリバリー (CI/CD)
          typescript
          export class PipelineStack extends cdk.Stack {
            constructor(scope: Construct, id: string, props: PipelineProps) {
              super(scope, id, props);
              // 省略
          
              const ipamPipeline = new CodePipeline(this, 'Pipeline', {
                pipelineName: 'IpamPipeline',
                synth: new ShellStep('Synth', {
                  input: CodePipelineSource.connection('sample/network-repo', 'main', {connectionArn: 'xx'}),
                  installCommands: ['npm ci'],
                  commands: [
                    'npx cdk --version',
                    'npm run build && npm test',
                    'npx cdk synth',
                  ],
                  crossAccountKeys: true,
                }),
              });
          
              ipamPipeline.addStage(new PipelineStage(this, 'Deploy', {
                env: { account: '111122223333', region: 'ap-northeast-1' },
              }));
              ipamPipeline.buildPipeline();
          
              // パイプライン実行失敗時に通知
              new NotificationRule(this, 'NotificationRule', {
                source: ipamPipeline.pipeline,
                events: ['codepipeline-pipeline-pipeline-execution-failed'],
                targets: [new Topic(this, 'IpamPipelineExecutionFailedTopic', {
                  displayName: 'IPAM Pipeline Execution Failed',
                })],
              });
            }
          }
          
          // IPAM スタックのデプロイステージ
          export class PipelineStage extends Stage {
            constructor(scope: Construct, id: string, props?: StageProps) {
              super(scope, id, props);
          
              // cdk-nag のカスタムルールパックを適用
              cdk.Aspects.of(this).add(new CommonParametersChecks({ verbose: true }));
          
              new IpamStack(this, 'Ipam');
            }
          }

          パイプライン自体はシンプルな構成です。CIDR 誤りなどでデプロイが失敗した場合、すぐ対応できるように SNS トピックへ通知しています。

          Git を中心とした CIDR 払い出しの承認プロセス

          上記コードを前提として、CIDR 払い出しの承認プロセスを解説します。

          1. イシュー作成
          テナントの申請を契機に、Git リポジトリでイシューと作業ブランチを作成

          2. コード変更作業 

          • parameter.ts 編集 : TenantCidrDefinition 配列に新規 CIDR 定義を追加
          • テスト実行 : 新規追加したプールと AWS RAM 共有リソースが追加されることを確認

                        npm test

          • スナップショット更新 : 上記差分に問題なければ以下を実行

                        npm test -- -u

          3. プルリクエスト作成 :  
          Git リポジトリで main ブランチへのプルリクエストを作成

          4. プルリクエスト上で自動チェック :  

          • AWS CDK 単体テスト
          • コードスキャン & セキュリティスキャン (GitHub Advanced Security の機能を活用)
          • AI レビュー

          5. 承認 & マージ :  
          プラットフォーム管理者が変更内容を確認し、プルリクエストを承認&マージ

          6. 自動デプロイ
          CI/CD パイプラインが自動実行され、IPAM プールをテナントへ共有 (CIDR誤りがあれば自動停止) 


          このフローにより、すべての変更が Git に記録され、誰がいつどの CIDR を割り当てたか追跡できます。

          なお、4 と 6 のプロセスは自動実行前提ですが、2~3 のステップも AI 支援ツールにアサインできますね。人・AI のどちらが作業を実行する場合でも、イシューテンプレートで申請項目・作業内容を標準化しておくと、さらに堅牢なフローとなります。


           

          おわりに

          本記事では、CIDR 払い出しフローにおける NetDevOps の実践例を紹介しました。本アプローチのポイントは以下の通りです。

          • IaC 化 : CIDR 定義を AWS CDK でコード化し、変更点を可視化
          • 自動チェック : 各種テスト・スキャンで品質担保
          • レビュー : プルリクエスト中心の承認プロセスで属人性を排除
          • デプロイ : CI/CD で自動反映し、CloudFormation 実行時のバリデーションで誤りを検知 (自動停止) 
          • モニタリング/運用 : IPAM で払い出した CIDR の利用状況を確認し、想定外の利用を検知


          Git と CI/CD を中心に据えたワークフローは、ネットワーク運用においても非常に有効でした。すべての変更がコードとして記録され、レビュー・自動テストが実行されることで、属人化排除と品質向上を実現できました。

          マネジメントコンソールで IPAM を直接操作する場合と比較して、Git を経由するプロセスは手間が増えるように見えるかもしれません。しかし、この取り組みの真価は「作業の速さ」ではなく、「堅牢なプロセスの確立」にあります。  

          自動チェックにより人為的な設定ミスを排除し、Git 履歴によって「いつ・誰が・なぜ変更したか」というトレーサビリティを確保できたことは、プラットフォームの信頼性向上に大きく寄与しました。

          現在は、一部の OU で AWS Service Catalog を活用した Amazon VPC・AWS Transit Gateway 関連の自動プロビジョニングも運用されています。このような発展的な活用については、機会があれば別途ご紹介したいと思います。

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


           

          筆者プロフィール

          松尾 優成
          株式会社みずほ銀行
          情報数理工学研究所
          デジタル技術開発部 上席主任研究員

          入社後、国際系金融システムの開発・保守業務を担当。その後、CCoE 活動を経て、社内共通プラットフォーム・AWS 案件を推進。
          現在はプラットフォーム上のGolden Pathを整備し、エンタープライズにおける開発者体験・生産性向上に従事。

          その他、〈みずほ〉グループ横断コミュニティ「コクリエ」を運営し、クラウドネイティブ開発に特化したLTやハンズオンなどのイベント企画を主導。

          A professional business portrait of a smiling man wearing glasses and a dark suit, with a light blue shirt and striped tie, posed in front of a plain light background.