AWS CDKでインベントリ管理(SSM Inventory)を実装してみた

今回は、AWS Systems Manager InventoryによるEC2インスタンスのソフトウェア、設定、ファイル、レジストリ情報の自動収集システムをAWS CDKで実装する方法をまとめました。

はじめに

今回は、AWS Systems Manager Inventoryを使用して、EC2インスタンスのソフトウェア情報、システム設定、インストールされているファイル、Windowsレジストリ情報などを自動収集し、S3バケットに保存するアーキテクチャをAWS CDKで実装していきます。

 

今回作成するリソース

  • S3バケット: SSMインベントリデータの保存先
  • バケットポリシー: SSMサービス専用のアクセス権限
  • SSM Association: インベントリ収集の自動実行設定
  • Resource Data Sync: インベントリデータの一元化と同期

 

アーキテクチャ概要

 

AWS CDK ソースコード

S3バケット設定(インベントリ保存)

    const ssmInventoryBucket = new s3.Bucket(this, 'SsmInventoryBucket', {
      bucketName: `s3b-ssm-inventory-bucket`,                                           // バケット名
      blockPublicAccess: s3.BlockPublicAccess.BLOCK_ALL,                                 // パブリックアクセスをすべてブロック
      encryption: s3.BucketEncryption.S3_MANAGED,                                        // 暗号化タイプ:SSE-S3
      autoDeleteObjects: true,                                                           // バケット削除時にオブジェクトも削除 デプロイ成功後にfalseに設定
      enforceSSL: true,                                                                  // SSL/TLS暗号化を強制
      lifecycleRules: [                                                                  // ライフサイクルルール作成
      {
          id: 'Expiration Rule 12 Months',                                               // ライフサイクルルール名
          expiration: cdk.Duration.days(366),                                            // 1年間保持
      }
      ],
      removalPolicy: cdk.RemovalPolicy.DESTROY,                                          // スタック削除時にバケットも削除 デプロイ成功後にRETAINに変更
    });

    // バケットポリシー追加
    ssmInventoryBucket.addToResourcePolicy(new iam.PolicyStatement({                       // バケットポリシー追加
      sid: 'AWSSSMInventoryWrite',                                                          // ステートメントID
      effect: iam.Effect.ALLOW,                                                            // 許可
      actions: [                                                                           // 許可するアクション
        's3:PutObject'                                                                     // オブジェクトの配置を許可
      ],
      resources: [                                                                         // 対象リソース
        `${ssmInventoryBucket.bucketArn}/AWSLogs/${cdk.Stack.of(this).account}/SSM/*`,     // SSMログ用パス
        ssmInventoryBucket.bucketArn,                                                      // バケット自体
      ],
      principals: [new iam.ServicePrincipal('ssm.amazonaws.com')],                         // SSMサービス
      conditions: {                                                                        // 条件
        StringEquals: {                                                                    // 文字列完全一致
          's3:x-amz-acl': 'bucket-owner-full-control',                                     // ACL制御
          'aws:SourceAccount': cdk.Stack.of(this).account                                  // 送信元アカウント制限
        }
      }
    }));

ポイント:

  • セキュア設計: パブリックアクセス完全ブロック、SSL強制
  • 長期保存: コンプライアンス要件に応じた1年間保持
  • 暗号化: SSE-S3による自動暗号化
  • コスト管理: ライフサイクルポリシーによる自動削除
  • 最小権限: SSMサービスのみにPutObject権限を付与
  • アカウント制限: 自アカウントからのアクセスのみ許可
  • ACL制御: バケット所有者の完全制御を保証
  • パス制限: SSM専用パスのみへのアクセス制限

SSM Association設定

    const inventoryAssociation = new ssm.CfnAssociation(this, 'InventoryAssociation', {
      name: 'AWS-GatherSoftwareInventory',                                                        // 実行するSSMドキュメント名
      associationName: 'ssm-inventory',                                                           // インベントリ名
      targets: [{                                                                                 // ターゲット
        key: 'InstanceIds',                                                                       // 対象キー
        values: ['*']                                                                             // 対象値
      }],
      parameters: {
          'applications': ['Enabled'],                                                            // アプリケーション情報を収集
          'awsComponents': ['Enabled'],                                                           // AWSコンポーネント情報を収集
          'networkConfig': ['Enabled'],                                                           // ネットワーク設定情報を収集
          'windowsUpdates': ['Enabled'],                                                          // Windowsアップデート情報を収集 (Windowsのみ)
          'instanceDetailedInformation': ['Enabled'],                                             // インスタンスの詳細情報を収集
          'services': ['Enabled'],                                                                // サービス設定を収集 (Windowsのみ)
          'windowsRoles': ['Enabled'],                                                            // Windowsロール設定を収集 (Windowsのみ)
          'customInventory': ['Enabled'],                                                         // カスタムインベントリデータを収集
          'billingInfo': ['Enabled'],                                                             // ライセンス含有アプリケーションの課金情報を収集
          'files': ['[{"Path":"C:\\Program Files","Pattern":["*.exe"],"Recursive":true}]'],                                                                   // ファイルを収集
          'windowsRegistry': ['[{"Path":"HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion","ValueNames":["ProductName"],"Recursive":false}]'],    // Windowsレジストリデータを収集
      },
      scheduleExpression: 'rate(30 minutes)',                                                     // インベントリの収集頻度:30分ごと
      outputLocation: {                                                                           // 実行結果出力先指定
        s3Location: {
          outputS3BucketName: ssmInventoryBucket.bucketName,                                      // インベントリ用のバケットに出力
          outputS3KeyPrefix: `AWSLogs/${cdk.Stack.of(this).account}/SSM/Logs`                     // ディレクトリを作成
        }
      }
    })

ポイント:

  • 包括的収集: ソフトウェア、設定、ファイル、レジストリの全面的な情報収集
  • 定期実行: 30分間隔での自動収集(要件に応じて調整可能)
  • カスタマイズ: ファイルパスやレジストリキーの設定
  • 構造化出力: JSON形式での構造化されたデータ保存

Resource Data Sync設定

    //resourceデータの同期設定
    const resourceDataSync = new ssm.CfnResourceDataSync(this, 'ResourceDataSync', {              // リソースデータの同期
      syncName: 'resource-data-sync',                                                             // 同期名
      s3Destination:{
        bucketName: ssmInventoryBucket.bucketName,                                                // バケット名
        bucketPrefix: `AWSLogs/${cdk.Stack.of(this).account}/SSM/`,                               // バケットプレフィックス名
        bucketRegion: cdk.Stack.of(this).region,                                                  // バケットのリージョン:東京リージョン
        syncFormat: 'JsonSerDe'                                                                   // 同期形式:JSON
      }
    });
    // 依存関係の設定
    resourceDataSync.node.addDependency(inventoryAssociation);                                    // 先にSSMインベントリ作成

ポイント:

  • データ一元化: 複数インスタンスのインベントリデータを統合管理
  • 標準形式: JsonSerDe形式による構造化データ
  • リージョン対応: マルチリージョン環境での統合管理
  • 依存関係: Association作成後の実行を保証

インベントリ収集項目と活用方法

収集項目詳細

カテゴリ 収集内容 用途
Applications インストール済みソフトウェア一覧 ライセンス管理、セキュリティ監査
AWS Components AWS CLI、CloudWatch Agent等 AWS管理ツールの導入状況
Network Config ネットワーク設定情報 ネットワーク構成管理
Windows Updates 適用済み更新プログラム パッチ管理、脆弱性対応
Instance Details CPU、メモリ、OS情報 資産管理、容量計画
Services Windowsサービス状態 サービス管理、異常検知
Windows Roles Windowsサーバーロール 機能構成管理
Files 指定パスのファイル情報 セキュリティ監査、変更管理
Registry Windowsレジストリ値 システム設定管理

注意点

  • Association制限: インスタンス単位で1つのInventory Associationのみ設定可能です

 

今回実装したコンストラクトファイルまとめ

import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';
import * as ssm from 'aws-cdk-lib/aws-ssm';
import * as s3 from 'aws-cdk-lib/aws-s3';
import * as iam from 'aws-cdk-lib/aws-iam';

export interface SsmInventoryConstructProps {
}

export class SsmInventoryConstruct extends Construct {
  constructor(scope: Construct, id: string, props?: SsmInventoryConstructProps) {
    super(scope, id);

    //===========================================
    // ssmインベントリ用のS3バケット作成
    //===========================================
    const ssmInventoryBucket = new s3.Bucket(this, 'SsmInventoryBucket', {
      bucketName: `s3b-ssm-inventory-bucket`,                                          // バケット名
      blockPublicAccess: s3.BlockPublicAccess.BLOCK_ALL,                                 // パブリックアクセスをすべてブロック
      encryption: s3.BucketEncryption.S3_MANAGED,                                        // 暗号化タイプ:SSE-S3
      autoDeleteObjects: true,                                                           // バケット削除時にオブジェクトも削除 デプロイ成功後にfalseに設定
      enforceSSL: true,                                                                  // SSL/TLS暗号化を強制
      lifecycleRules: [                                                                  // ライフサイクルルール作成
      {
          id: 'Expiration Rule 12 Months',                                               // ライフサイクルルール名
          expiration: cdk.Duration.days(366),                                            // 1年間保持
      }
      ],
      removalPolicy: cdk.RemovalPolicy.DESTROY,                                          // スタック削除時にバケットも削除 デプロイ成功後にRETAINに変更
    });

    // バケットポリシー追加
    ssmInventoryBucket.addToResourcePolicy(new iam.PolicyStatement({                       // バケットポリシー追加
      sid: 'AWSSSMInventoryWrite',                                                          // ステートメントID
      effect: iam.Effect.ALLOW,                                                            // 許可
      actions: [                                                                           // 許可するアクション
        's3:PutObject'                                                                     // オブジェクトの配置を許可
      ],
      resources: [                                                                         // 対象リソース
        `${ssmInventoryBucket.bucketArn}/AWSLogs/${cdk.Stack.of(this).account}/SSM/*`,     // SSMログ用パス
        ssmInventoryBucket.bucketArn,                                                      // バケット自体
      ],
      principals: [new iam.ServicePrincipal('ssm.amazonaws.com')],                         // SSMサービス
      conditions: {                                                                        // 条件
        StringEquals: {                                                                    // 文字列完全一致
          's3:x-amz-acl': 'bucket-owner-full-control',                                     // ACL制御
          'aws:SourceAccount': cdk.Stack.of(this).account                                  // 送信元アカウント制限
        }
      }
    }));

    //===========================================
    // SSMインベントリ作成
    //=========================================== 
    const inventoryAssociation = new ssm.CfnAssociation(this, 'InventoryAssociation', {
      name: 'AWS-GatherSoftwareInventory',                                                        // 実行するSSMドキュメント名
      associationName: 'ssm-inventory',                                                           // インベントリ名
      targets: [{                                                                                 // ターゲット
        key: 'InstanceIds',                                                                       // 対象キー
        values: ['*']                                                                             // 対象値
      }],
      parameters: {
          'applications': ['Enabled'],                                                            // アプリケーション情報を収集
          'awsComponents': ['Enabled'],                                                           // AWSコンポーネント情報を収集
          'networkConfig': ['Enabled'],                                                           // ネットワーク設定情報を収集
          'windowsUpdates': ['Enabled'],                                                          // Windowsアップデート情報を収集 (Windowsのみ)
          'instanceDetailedInformation': ['Enabled'],                                             // インスタンスの詳細情報を収集
          'services': ['Enabled'],                                                                // サービス設定を収集 (Windowsのみ)
          'windowsRoles': ['Enabled'],                                                            // Windowsロール設定を収集 (Windowsのみ)
          'customInventory': ['Enabled'],                                                         // カスタムインベントリデータを収集
          'billingInfo': ['Enabled'],                                                             // ライセンス含有アプリケーションの課金情報を収集
          'files': ['[{"Path":"C:\\Program Files","Pattern":["*.exe"],"Recursive":true}]'],                                                                   // ファイルを収集
          'windowsRegistry': ['[{"Path":"HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion","ValueNames":["ProductName"],"Recursive":false}]'],    // Windowsレジストリデータを収集
      },
      scheduleExpression: 'rate(30 minutes)',                                                     // インベントリの収集頻度:30分ごと
      outputLocation: {                                                                           // 実行結果出力先指定
        s3Location: {
          outputS3BucketName: ssmInventoryBucket.bucketName,                                      // インベントリ用のバケットに出力
          outputS3KeyPrefix: `AWSLogs/${cdk.Stack.of(this).account}/SSM/Logs`                     // ディレクトリを作成
        }
      }
    })
    //resourceデータの同期設定
    const resourceDataSync = new ssm.CfnResourceDataSync(this, 'ResourceDataSync', {              // リソースデータの同期
      syncName: 'resource-data-sync',                                                             // 同期名
      s3Destination:{
        bucketName: ssmInventoryBucket.bucketName,                                                // バケット名
        bucketPrefix: `AWSLogs/${cdk.Stack.of(this).account}/SSM/`,                               // バケットプレフィックス名
        bucketRegion: cdk.Stack.of(this).region,                                                  // バケットのリージョン:東京リージョン
        syncFormat: 'JsonSerDe'                                                                   // 同期形式:JSON
      }
    });
  }
}

 

まとめ

今回は、AWS Systems Manager Inventoryを活用した包括的なインベントリ管理システムをAWS CDKで実装しました。
皆さんのお役に立てば幸いです。

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