Control Towerを有効化する際に作成されるConfigアグリゲータをTerraformで管理してみた

Control Towerを有効化する際に作成されるConfigアグリゲータをTerraformで管理してみた

Clock Icon2025.04.08

はじめに

Control Towerを有効化した際に作成されるConfigアグリゲータをTerraformで管理することがあったのでその流れを記事にします。

Control Towerを有効化すると、管理アカウントとAuditアカウントにアグリゲータが作成されます。
また、それぞれに作成されるアグリゲータはタイプが異なります。
管理アカウント:組織タイプ
Auditアカウント:個々のアカウントタイプ

それぞれの違いは以下のブログが分かりやすいです。
https://dev.classmethod.jp/articles/config-aggregator-in-organization-member-account/

今回は、これらのアグリゲータの管理と、新規のアグリゲータの管理をTerraformで実施したいと思います。

今回やること

Control Towerにより作成されたリソースをTerraformにインポートしたのち、Auditアカウントに組織タイプのアグリゲータの管理を委任します。

  1. 既存リソースのインポート
  2. Auditアカウントに委任
  3. Auditアカウントに組織タイプのアグリゲータを作成

今回の実装を行うにあたり、管理アカウントとAuditアカウントはTerraformで管理していることを前提とします。
アカウントのインポートができていない場合は以下のブログが参考になります。
https://dev.classmethod.jp/articles/aws-organizations-terraform-import/

全体像

今回実施する内容を図にするとこんな感じです。
shapes-1744119786898

既存リソースのインポート

前提として、今回は複数アカウント(管理アカウントとAuditアカウント)のリソースを操作するため、以下のブログを参考にマルチアカウントを操作できるように準備しておきます。
https://dev.classmethod.jp/articles/terraform-multiple-provider-configuration-using-aws-crossaccount/

既存のアグリゲータをインポート

まずはアグリゲータのインポートファイルを用意します。
idにはアグリゲータの名前を入力します。

import.tf
## 管理アカウントのAggregator(組織タイプ)インポート
import {
  to = aws_config_configuration_aggregator.organization
  id = "aws-controltower-ConfigAggregatorForOrganizations"
}

## AuditアカウントのAggregator(個々のアカウントタイプ)インポート
import {
  provider = aws.audit
  to       = aws_config_configuration_aggregator.audit
  id       = "aws-controltower-GuardrailsComplianceAggregator"
}

terraform plan -generate-config-out=config.tfでインポートを実行します。
この時、-generate-config-outで指定したconfig.tfが作成されます。
planを実行するとエラーが出力されます。

$ terraform plan -generate-config-out=config.tf

│ Error: Not enough list items
│
│   with aws_config_configuration_aggregator.organization,
│   on import_config.tf line 10:
│   (source code not available)
│
│ Attribute organization_aggregation_source.0.regions requires 1 item minimum,
│ but config has only 0 declared.
╵
╷
│ Error: Not enough list items
│
│   with aws_config_configuration_aggregator.audit,
│   on import_config.tf line 12:
│   (source code not available)
│
│ Attribute account_aggregation_source.0.regions requires 1 item minimum, but
│ config has only 0 declared.

これはリスト形式を期待しているけど、リストの中身がないことを示しています。
こちらは無視して続けます。
実際に出力されたコードを確認します。

config.tf
resource "aws_config_configuration_aggregator" "organization" {
  name = "aws-controltower-ConfigAggregatorForOrganizations"
  tags = {
    aws-control-tower = "managed-by-control-tower"
  }
  tags_all = {
    aws-control-tower = "managed-by-control-tower"
  }
  organization_aggregation_source {
    all_regions = true
    regions     = [] ←削除する
    role_arn    = "arn:aws:iam::11111111111:role/service-role/AWSControl TowerConfigAggregatorRoleForOrganizations"
  }
}

resource "aws_config_configuration_aggregator" "audit" {
  provider = aws.audit
  name     = "aws-controltower-GuardrailsComplianceAggregator"
  tags = {
    aws-control-tower = "managed-by-control-tower"
  }
  tags_all = {
    aws-control-tower = "managed-by-control-tower"
  }
  account_aggregation_source {
    account_ids = ["11111111111", "222222222222", "33333333333", "44444444444"]
    all_regions = true
    regions     = [] ←削除する
  }
}

account_aggregation_sourceにregionsという不要なオプションが入っています。これが先ほどplanを実行した際に出力されたエラーの原因です。
このオプションは使用していないので削除します。
あとはterraform applyを実行してインポートを完了します。

Auditアカウントに委任

インポートができたので、AuditアカウントにConfigアグリゲータの管理を委任します。
delegated.tfに以下を追加してデプロイします

delegated.tf
resource "aws_organizations_delegated_administrator" "config_admin" {
  account_id       = aws_organizations_account.audit.id
  service_principal = "config.amazonaws.com"
}

これでAWS Configの組織タイプのアグリゲータを作成できるようになります。

Auditアカウントの組織タイプアグリゲータを作成

Auditアカウントに組織タイプのアグリゲータを作成します。
最初にインポートしたconfig.tfに以下のコードを追加します。

config.tf
# AuditアカウントのAggregator(組織タイプ)
resource "aws_config_configuration_aggregator" "audit_org" {
  provider   = aws.audit
  name       = "aws-ConfigOrganizationAggregatorForAudit"
  depends_on = [aws_iam_role_policy_attachment.organization]
  organization_aggregation_source {
    all_regions = true
    role_arn    = aws_iam_role.config.arn
  }
}

data "aws_iam_policy_document" "assume_role" {
  provider = aws.audit
  statement {
    effect = "Allow"

    principals {
      type        = "Service"
      identifiers = ["config.amazonaws.com"]
    }

    actions = ["sts:AssumeRole"]
  }
}

resource "aws_iam_role" "config" {
  provider           = aws.audit
  name               = "demo-role"
  assume_role_policy = data.aws_iam_policy_document.assume_role.json
}

resource "aws_iam_role_policy_attachment" "config" {
  provider   = aws.audit
  role       = aws_iam_role.config.name
  policy_arn = "arn:aws:iam::aws:policy/service-role/AWSConfigRoleForOrganizations"
}

Auditアカウントのマネジメントコンソールからアグリゲータが作成されていることを確認します。
CleanShot 2025-04-08 at 18.40.46@2x

  • Control Towerによって作成された個々のアカウントタイプのアグリゲータ
  • Terraformで作成した組織タイプのアグリゲータ

の2点が作成されていることが分かります。
これで最初に記載した全体像が完成しました。

Terraform管理下に取り込む場合の留意点

今回はControl Towerによって作成されたConfigのアグリゲータをTerraform管理下に取り込みました。
しかし、Control Tower環境ではSCPでConfigリソースの変更を禁止している環境も少なくないと思います。
そのような環境では、Terraform管理下に置くことは可能ですが、リソースの変更ができないため、Terraform管理下に置くメリットは限定的になります。

また、Control Towerの利用ガイダンスとしてControl Towerで作成されるリソースについて以下の記載があります。

AWS Control Tower によって作成されたリソースを変更または削除しないでください。これらのリソースを変更すると、ランディングゾーンの更新または OU の再登録が必要になる場合があります。また、変更によってコンプライアンスレポートが不正確になる可能性があります。
参照: AWS Control Tower リソースの作成と変更に関するガイダンス

今回私が検証した環境では記載のようなランディングゾーンの更新等は必要ありませんでしたが、Control Tower管理のリソースをTerraformでも管理する場合は何かしら影響が出る可能性があることも考慮する必要があります。

まとめ

Control Towerを有効化すると、管理アカウントとAuditアカウントにそれぞれアグリゲータが作成されます。
最小権限の原則に従い、Configの管理はAuditアカウントに委任することが推奨されています。
これをTerraformで実装する場合は、インポート、委任、Auditアカウントで組織タイプのアグリゲータ作成という流れでTerraformで管理できることがわかりました。

Share this article

facebook logohatena logotwitter logo

© Classmethod, Inc. All rights reserved.