AWS DevOps Agent の複数 AWS アカウントへの接続を AWS CDK で実装して、各アカウントを調査してみた

AWS DevOps Agent の複数 AWS アカウントへの接続を AWS CDK で実装して、各アカウントを調査してみた

調査依頼にアカウント ID を含めることで調査対象アカウントを指定可能。一方で未指定時は接続済みの全アカウントが対象となる場合がある。
2026.05.23

こんにちは、製造ビジネステクノロジー部の若槻です。

以前の記事で、AWS DevOps Agent のハブアカウント構成を紹介しました。

今回は、その構成の具体的な実装として、Agent Space のセカンダリリソースに別アカウントを追加する実装を AWS CDK で行い、複数アカウントの AWS リソースを DevOps Agent で調査できるようにしました。

構成

今回実装した構成は以下のとおりです。

ハブアカウントの Agent Space がセカンダリアカウントの IAM ロールを AssumeRole することで、セカンダリアカウントの AWS リソースを調査できます。

実装したリソース

リソース アカウント 役割
CfnAgentSpace ハブアカウント Agent Space 本体
IAM ロール(OperatorRole) ハブアカウント コンソールから Agent Space を操作するためのロール
IAM ロール(AgentSpaceRole) ハブアカウント Agent Space が AWS リソースを調査するためのロール
CfnAssociation(primaryAws) ハブアカウント Agent Space とハブアカウントの関連付け
CfnAssociation(sourceAws) ハブアカウント Agent Space とセカンダリアカウントの関連付け
IAM ロール(DevOpsAgentSecondaryAccountRole) セカンダリアカウント ハブアカウントの DevOps Agent が AssumeRole するためのロール

construct 構造

Security スタック(ハブアカウント)
└── DevOpsAgentConstruct
    ├── AgentSpaceConstruct
    │   └── OperatorRoleConstruct
    ├── AwsAssociationConstruct
    │   └── AgentSpaceRoleConstruct
    └── SecondaryAwsAssociationConstruct

DevOpsAgentSecondaryAccount スタック(セカンダリアカウント)
└── DevOpsAgentSecondaryAccountRole

やってみた

セカンダリアカウント用スタック

セカンダリアカウントに IAM ロールを作成するスタックです。

lib/devops-agent-secondary-account-stack.ts
import * as cdk from "aws-cdk-lib";
import * as iam from "aws-cdk-lib/aws-iam";
import { Construct } from "constructs";

/** セカンダリアカウントに作成する IAM ロール名(ハブアカウント側から ARN 参照するため固定値) */
export const SECONDARY_ACCOUNT_ROLE_NAME = "DevOpsAgentSecondaryAccountRole";

interface DevOpsAgentSecondaryAccountStackProps extends cdk.StackProps {
  primaryAccountId: string;
  primaryRegion: string;
}

/**
 * DevOps Agent のセカンダリアカウント用スタック
 *
 * ハブアカウントの Agent Space がこのアカウントのリソースを調査できるよう、
 * ハブアカウントの DevOps Agent が AssumeRole できる IAM ロールを作成する。
 *
 * @see https://docs.aws.amazon.com/devopsagent/latest/userguide/configuring-capabilities-for-aws-devops-agent-connecting-multiple-aws-accounts.html
 */
export class DevOpsAgentSecondaryAccountStack extends cdk.Stack {
  constructor(
    scope: Construct,
    id: string,
    props: DevOpsAgentSecondaryAccountStackProps,
  ) {
    super(scope, id, props);

    const { primaryAccountId, primaryRegion } = props;

    /** ハブアカウントの DevOps Agent サービスプリンシパル */
    const servicePrincipal = new iam.ServicePrincipal("aidevops.amazonaws.com", {
      conditions: {
        StringEquals: {
          "aws:SourceAccount": primaryAccountId,
        },
        ArnLike: {
          // ハブアカウントの任意の Agent Space からの AssumeRole を許可(循環参照回避のためワイルドカード)
          "aws:SourceArn": cdk.Arn.format(
            {
              service: "aidevops",
              region: primaryRegion,
              account: primaryAccountId,
              resource: "agentspace",
              resourceName: "*",
            },
            this,
          ),
        },
      },
    });

    /** セカンダリアカウント用 IAM ロール */
    new iam.Role(this, "AgentSpaceRole", {
      roleName: SECONDARY_ACCOUNT_ROLE_NAME,
      assumedBy: servicePrincipal,
      managedPolicies: [
        iam.ManagedPolicy.fromAwsManagedPolicyName("AIDevOpsAgentAccessPolicy"),
      ],
      inlinePolicies: {
        /** @see https://docs.aws.amazon.com/ja_jp/devopsagent/latest/userguide/configuring-capabilities-for-aws-devops-agent-migrating-from-public-preview-to-general-availability.html#step-1-update-monitoring-roles */
        AllowCreateServiceLinkedRoles: new iam.PolicyDocument({
          statements: [
            new iam.PolicyStatement({
              actions: ["iam:CreateServiceLinkedRole"],
              resources: [
                this.formatArn({
                  service: "iam",
                  region: "",
                  resource: "role",
                  resourceName:
                    "aws-service-role/resource-explorer-2.amazonaws.com/AWSServiceRoleForResourceExplorer",
                }),
              ],
            }),
          ],
        }),
      },
    });
  }
}

ポイントは以下です。

  • 信頼ポリシー: aidevops.amazonaws.com サービスプリンシパルからの AssumeRole を許可。aws:SourceAccountaws:SourceArnハブアカウントの Agent Space からのみに絞ることでアクセス範囲を最小化しています
  • マネージドポリシー: AIDevOpsAgentAccessPolicy をアタッチして DevOps Agent が AWS リソースを調査できるようにします
  • インラインポリシー: Resource Explorer のサービスリンクロール作成権限を追加します(これがないと Resource Explorer の初回セットアップが失敗します)

SecondaryAwsAssociation コンストラクト

ハブアカウント側で Agent Space とセカンダリアカウントを関連付けるコンストラクトです。

lib/constructs/devops-agent/secondary-aws-association/index.ts
import * as devopsagent from "aws-cdk-lib/aws-devopsagent";
import { Construct } from "constructs";

interface SecondaryAwsAssociationConstructProps {
  agentSpace: devopsagent.CfnAgentSpace;
  secondaryAccountId: string;
  secondaryAccountRoleArn: string;
}

/**
 * DevOps Agent のセカンダリ AWS アカウントの関連付け実装
 * @see https://docs.aws.amazon.com/devopsagent/latest/userguide/configuring-capabilities-for-aws-devops-agent-connecting-multiple-aws-accounts.html
 */
export class SecondaryAwsAssociationConstruct extends Construct {
  public readonly association: devopsagent.CfnAssociation;

  constructor(
    scope: Construct,
    id: string,
    props: SecondaryAwsAssociationConstructProps,
  ) {
    super(scope, id);

    const { agentSpace, secondaryAccountId, secondaryAccountRoleArn } = props;

    /** セカンダリ AWS アカウントの関連付け */
    this.association = new devopsagent.CfnAssociation(this, "Default", {
      agentSpaceId: agentSpace.ref,
      serviceId: "aws",
      configuration: {
        sourceAws: {
          assumableRoleArn: secondaryAccountRoleArn,
          accountId: secondaryAccountId,
          accountType: "source",
        },
      },
    });
  }
}

CfnAssociationconfiguration.sourceAwsaccountType: "source" を指定することで、セカンダリリソースとして登録されます。

デプロイ後に Agent Space のコンソールを確認すると、セカンダリリソースとしてセカンダリアカウントが登録されます。


Agent Space の機能画面。セカンダリリソースとしてセカンダリアカウントが「有効」で登録されている

DevOpsAgentConstruct の更新

既存の DevOpsAgentConstructsecondaryAwsAssociation オプションを追加します。AgentSpaceConstructAwsAssociationConstruct など掲載していないコンストラクトの実装については、以前の記事を参照してください。

lib/constructs/devops-agent/index.ts
import * as devopsagent from "aws-cdk-lib/aws-devopsagent";
import { Construct } from "constructs";

import { AgentSpaceConstruct } from "./agent-space";
import { AwsAssociationConstruct } from "./aws-association";
import { SecondaryAwsAssociationConstruct } from "./secondary-aws-association";

interface SecondaryAwsAssociationProps {
  secondaryAccountId: string;
  secondaryAccountRoleArn: string;
}

interface DevOpsAgentConstructProps {
  secondaryAwsAssociation?: SecondaryAwsAssociationProps;
}

/**
 * DevOps Agent リソース実装
 * @see https://github.com/aws-samples/sample-aws-devops-agent-cdk/blob/main/lib/devops-agent-stack.ts
 */
export class DevOpsAgentConstruct extends Construct {
  public readonly agentSpace: devopsagent.CfnAgentSpace;

  constructor(scope: Construct, id: string, props: DevOpsAgentConstructProps) {
    super(scope, id);

    const { secondaryAwsAssociation } = props;

    /** Agent Space 本体とオペレーターロールの作成 */
    const agentSpaceConstruct = new AgentSpaceConstruct(this, "AgentSpace");
    this.agentSpace = agentSpaceConstruct.agentSpace;

    /** Agent Space と AWS アカウントの関連付け */
    const awsAssociation = new AwsAssociationConstruct(this, "AwsAssociation", {
      agentSpace: agentSpaceConstruct.agentSpace,
    });
    awsAssociation.association.addDependency(agentSpaceConstruct.agentSpace);

    /** Agent Space とセカンダリ AWS アカウントの関連付け */
    if (secondaryAwsAssociation) {
      const secondaryAssociation = new SecondaryAwsAssociationConstruct(
        this,
        "SecondaryAwsAssociation",
        {
          agentSpace: agentSpaceConstruct.agentSpace,
          ...secondaryAwsAssociation,
        },
      );
      secondaryAssociation.association.addDependency(
        agentSpaceConstruct.agentSpace,
      );
    }
  }
}

secondaryAwsAssociation はオプショナルにしており、指定がある場合のみ SecondaryAwsAssociationConstruct を作成します。また、agentSpace を公開することで呼び出し側から参照できるようにしています。

デプロイ

セカンダリアカウントとハブアカウントの 2 段階でデプロイします。

Step 1: セカンダリアカウントにデプロイ

npx cdk deploy DevOpsAgentSecondaryAccount -c environment=dev --profile <secondary-profile>

セカンダリアカウントに DevOpsAgentSecondaryAccountRole が作成されます。

Step 2: ハブアカウントにデプロイ

npx cdk deploy Security -c environment=dev --profile <hub-profile>

ハブアカウントの Agent Space にセカンダリアカウントの Association が追加されます。

動作確認

さて、実装したスペースで実際に調査依頼を行ってみるのですが、ここで気になるのは、調査依頼の内容に応じて意図したアカウントに対して調査が行われるのか? という点です。それを踏まえて以下の3パターンで動作確認をしてみます。

  • ハブアカウントのリソースを調査
  • セカンダリアカウントのリソースを調査
  • 対象アカウントを明示的に指定しない場合

ハブアカウントのリソースを調査

まずはハブアカウントに対する調査依頼です。

以下のプロンプトで調査を指示しました。ARN にハブアカウントの ID が含まれるようにしています。

arn%3Aaws%3Acloudformation%3Aap-northeast-1%3A<プライマリアカウントID>%3Astack%2FSecurity%2F7c2ed0b0-d7b5-11ef-98d3-0ee07494c8a5&operationId=d43736f0-0d8a-4377-ab54-36ce6f83a940
上記のデプロイで発生したエラーについて調査して。


調査タイムライン。ハブアカウントの CloudFormation デプロイエラーに対する調査依頼

実際のデプロイエラーの原因調査ができており、ハブアカウントのリソースを調査対象とすることができました。

セカンダリアカウントのリソースを調査

次にセカンダリアカウントに対する調査依頼です。

以下のプロンプトで調査を指示しました。こちらも ARN にセカンダリアカウントの ID が含まれるようにしています。

2026-05-19 20:24:00 LOCAL 頃に、アラート arn:aws:cloudwatch:ap-northeast-1:<セカンダリアカウントID>:alarm:LambdaErrors でアラームが発生している。調査を実施して。

調査タイムラインの様子です。アラームが3つ発火していることが示されています。


調査タイムライン。アラームの発火・エラー発生・回復の時刻が特定されている

セカンダリアカウントでは実際に Lambda のエラーが発生していました。


セカンダリアカウントの CloudWatch アラーム「LambdaErrors」。05/19 にアラームが発生していた

セカンダリアカウントのリソースを調査対象とすることができました。

対象アカウントを明示的に指定しない場合

最後に、対象アカウントを明示的に指定しない場合はどうなるでしょうか。

以下のプロンプトで調査を指示しました。指示文に対象アカウントを特定する情報は含まれていません。

Security Hub で最近3日間に新規作成された検出結果を教えて。

調査タイムラインは以下となりました。プライマリおよびセカンダリのいずれに対しても調査が行われる挙動となりました。


調査タイムライン。対象アカウントを指定しない場合

対象アカウントを指定しない場合、プライマリおよびセカンダリの両アカウントが調査対象となる場合があることがわかりました。

終わりに

AWS DevOps Agent の複数 AWS アカウントへの接続を AWS CDK で実装して、各アカウントを調査してみました。

調査依頼の文面にアカウント ID を含めることで調査対象アカウントを指定可能とない、一方で未指定時は接続済みの全アカウントが対象となる場合があることが分かりました。

以前の記事で書いた通り、単一の GitHub Organization に対して複数の AWS アカウントを接続することはできないため、複数アカウントを接続する場合は今回のようなハブアカウント構成が必要になります。

ハブアカウント構成を採用する場合は、調査依頼の文面に対象アカウントを特定する情報を含める運用が必要になることに注意するようにしましょう。

以上

この記事をシェアする

関連記事