AWS Control TowerのメンバーアカウントをTerraformで作成する [Not AFT]

AWS Control TowerのメンバーアカウントをTerraformで作成する [Not AFT]

Clock Icon2025.01.15

どうも、ちゃだいん(@chazuke4649)です。

AWS Control Towerで管理されたメンバーアカウントを発行する方法はいくつかありますが、TerrformによるIaCツールにて実行したい場合は、Account Factory for Terraform (AFT) を利用するのが良いです。

AFTはControl Towerのアカウント管理機能を拡張する強力なソリューションであり、中・大規模のマルチアカウント環境を安全に運用するために役立ちます。

ただし、AFTは準備するのはちょっとオーバーで、もっと気軽にControl TowerメンバーアカウントをTerraformで発行したい場合もあったりします。

今回そんな時の方法を試す機会があったのでご紹介します。

先に結論

結論から言うと、AFTを利用せず、AWS Service CatalogをTerraformにて操作することにより、Control Towerメンバーアカウントを発行することが可能です。

Control Towerのアカウントファクトリーの実体は、Service Catalog 製品 "AWS Control Tower Account Factory" ですが、Terraformでこの製品を起動するというアプローチで、実質アカウントファクトリーからアカウント発行することが可能になります。

20250115_130308

サンプルコード

今回はサンプルとしてLemonという名前のAWSアカウントを作成することにします。

servicecatalog.tf

## アカウントファクトリーの製品IDを引数とし、プロビジョニングアーティファクト(=製品の各バージョンごとの情報)のリストを取得
data "aws_servicecatalog_provisioning_artifacts" "ct" {
  product_id = local.ct_product_id
}

locals {
  ct_product_id = "prod-xxxxxxxxx" ## CTホームリージョンのAWS Control Tower Account Factoryの製品ID
  provisioning_params = { ## Control Towerコンソールで入力が求められるパラメータ同様
    lemon = {
      AccountEmail              = "lemon@example.com"
      AccountName               = "Lemon"
      ManagedOrganizationalUnit = "Sandbox"
      SSOUserEmail              = "management@example.com"
      SSOUserFirstName          = "NOT"
      SSOUserLastName           = "USE"
    }
  }
  ## 製品バージョンのリストのうち、最新版(=active)のみを取得するためfor文とif文を活用
  active_pa = [
    for artifact in data.aws_servicecatalog_provisioning_artifacts.ct.provisioning_artifact_details :
    artifact if artifact.active == true
  ]
}

resource "aws_servicecatalog_provisioned_product" "lemon" {
  name                     = local.provisioning_params.lemon.AccountName
  product_id               = local.ct_product_id
  provisioning_artifact_id = local.active_pa[0].id ## ランディングゾーンを更新するとIDが変わるので、データソースより動的に取得

  ## ベタ書きだと冗長的になるnestedブロックをdynamicブロックでくり返しを回避
  dynamic "provisioning_parameters" {
    for_each = local.provisioning_params.lemon
    content {
      key   = provisioning_parameters.key
      value = provisioning_parameters.value
    }
  }
  tags = {
    "created-by" = "terraform"
  }
}

製品を起動する

では実際にデプロイしてみたいと思います。
まずはterraform planを実行します。

% terraform plan

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following
symbols:
  + create

Terraform will perform the following actions:

  # aws_servicecatalog_provisioned_product.lemon will be created
  + resource "aws_servicecatalog_provisioned_product" "lemon" {
      + accept_language                        = "en"
      + arn                                    = (known after apply)
      + cloudwatch_dashboard_names             = (known after apply)
      + created_time                           = (known after apply)
      + id                                     = (known after apply)
      + ignore_errors                          = false
      + last_provisioning_record_id            = (known after apply)
      + last_record_id                         = (known after apply)
      + last_successful_provisioning_record_id = (known after apply)
      + launch_role_arn                        = (known after apply)
      + name                                   = "Lemon"
      + outputs                                = (known after apply)
      + path_id                                = (known after apply)
      + product_id                             = "prod-xxxxxxxxxx"
      + provisioning_artifact_id               = "pa-xxxxxxxxxx"
      + retain_physical_resources              = false
      + status                                 = (known after apply)
      + status_message                         = (known after apply)
      + tags                                   = {
          + "created-by" = "terraform"
        }
      + type                                   = (known after apply)

      + provisioning_parameters {
          + key   = "AccountEmail"
          + value = "lemon@exampl.com"
        }
      + provisioning_parameters {
          + key   = "AccountName"
          + value = "Lemon"
        }
      + provisioning_parameters {
          + key   = "ManagedOrganizationalUnit"
          + value = "Sandbox"
        }
      + provisioning_parameters {
          + key   = "SSOUserEmail"
          + value = "management@example.com"
        }
      + provisioning_parameters {
          + key   = "SSOUserFirstName"
          + value = "NOT"
        }
      + provisioning_parameters {
          + key   = "SSOUserLastName"
          + value = "USE"
        }
    }

Plan: 1 to add, 0 to change, 0 to destroy.

続いて、terraform applyを実行します。

% terraform apply

## 中略

aws_servicecatalog_provisioned_product.lemon: Still creating... [10m20s elapsed]
aws_servicecatalog_provisioned_product.lemon: Creation complete after 10m21s [id=pp-xxxxxxx]

Apply complete! Resources: 1 added, 0 changed, 0 destroyed.

以下の通り、プロビジョニングされた製品が作成されました。

20250109_164303

プロビジョニングされた製品を削除する

ちなみに、Terraformにて、aws_servicecatalog_provisioned_productを削除すると、AWSアカウント自体が削除されるわけではなく、Service Catalogのリソースのみが削除され、いわゆるAWSアカウントがControl Towerの管理から解除された状態になります。

20250109_184833

おわりに

Control Towerのメンバーアカウント発行をTerraformにてService Catalogレベルで行いました。
AFTのような本格的なマルチアカウント管理システムが不要で、一旦IaCツールで作成できればよいといった場合に良さそうです。
ぜひお試しください。

それでは今日はこの辺で。ちゃだいん(@chazuke4649)でした。

参考情報

aws_servicecatalog_provisioned_product

AWS 開発基盤として、セキュリティを考慮したマルチアカウント構成のススメ | ブログ | Serverless Operations

Share this article

facebook logohatena logotwitter logo

© Classmethod, Inc. All rights reserved.