[マルチアカウント]HCP Terraformのdynamic credentials用のAWS IAM RoleをAWS CloudFormation StackSetで作成してみた

[マルチアカウント]HCP Terraformのdynamic credentials用のAWS IAM RoleをAWS CloudFormation StackSetで作成してみた

Clock Icon2025.03.13

AWS CloudFormation StackSetを使用して、HCP Terraform用のIAM Roleを作成してみました。

なぜCloudFormation StackSetを使うか

アカウント数が多い環境のHCP Terraform用のIAM Role作成の手間を削減するためです。

HCP TerraformからAWSアカウントを操作する際は、IAMロールまたはIAMユーザーを事前に作成する必要があります。

これを作成するまでは、HCP TerraformからAWSアカウントは操作できないため、IAMロールの作成の自動化は工夫が必要です。

「手動で作成」「CloudFormationで作成」「Workspaceの実行モードをlocalで実行」等方法は色々あります。

多くの方法では、AWSアカウント数分作業が必要です。

CloudFormation StackSetを利用することで手動作業を最小限に、HCP Terraform IAMロール作成を自動化できます。

構成図

vscode-drop-1741757226992-tkalju6gmmd.png

CloudFormation StackSet管理アカウントに、HCP Terraform用のIAM Roleを作成します。

メンバーアカウントは、TerraformからCloudFormation StackSet経由でIAM Roleを作成します。

tfe providerを使えば、TerraformでHCP TerraformのVariable setsも作成できます。

Variable setの作成と同時に、作成したIAM Role ARNをセットします。

Variable setに登録されるVariable
TFC_AWS_PROVIDER_AUTH=true
TFC_AWS_RUN_ROLE_ARN=<IAM Role ARN>

この構成を取ることで、メンバーアカウントのIAM Role作成からAWS環境へのアクセス用のVariable set作成までを自動化できます。

やってみた

CloudFormation StackSet管理アカウントにHCP Terraform用のIAM Roleを作成する

以下を参考にCloudFormation StackSet管理アカウントでHCP Terraform用IAM Roleを作成します。

https://developer.hashicorp.com/terraform/cloud-docs/workspaces/dynamic-provider-credentials/aws-configuration

上記をCloudFormationで作成する手順もありますので、ご参考までに。

https://dev.classmethod.jp/articles/hcp-terraform-iam-role-cfn/

CloudFormation StackSet管理アカウント アクセス用のVariable setsを作成する

HCP Terraformにアクセスして、Settings -> Variable set -> Create organization variable setの順に選択します。

任意のName・Descriptionを設定します。

参考までに、私は以下のように設定することが多いです。

Name: auth-aws-<AWSアカウントID>
Description: AWS credentials for <AWSアカウント名>

次の手順で作成するWorkspaceに紐づけるため、Variable set scopeはデフォルトの状態(workspace,project未選択)でOKです。

Variablesは以下を設定してください。iam_role_arnは前の手順で作成したIAM RoleのARNを設定します。

Variable category Key Value Sensitive
Environment variable TFC_AWS_PROVIDER_AUTH true No
Environment variable TFC_AWS_RUN_ROLE_ARN <iam_role_arn> No

最後にCreate variable setを選択して、Variable setを作成します。

vscode-drop-1741757961186-vtsrv6plcok.png

HCP Terraform Token作成を作成する

HCP Terraform Workspace上でtfe providerを使って、Variable setを作ります。

tfe providerを使うために、認証用のHCP Terraform Tokenが必要です。

HCP Terraformのコンソール上で、Settings -> API tokens ->Team Tokens -> Create a team tokenの順に選択します。

Variable setsが作成可能なTeam(※)を選択し、Createを選択します。

Tokenが表示されるため、控えておきます。

「CloudFormation StackSet管理アカウント アクセス用のVariable setsを作成する」と同様にVariable setsを作成します。

こちらもVariable set scopeは未設定で問題ありません。

Name: auth-hcp-tf-<HCP Terraform Team名>
Description: AWS credentials for <HCP Terraform Team名>
Variable category Key Value Sensitive
Environment variable TFE_TOKEN <Token> Yes

※ Teamをまだ作っていない場合は、ownersを選びましょう。ownersはデフォルトで作成されるTeamで管理者権限を持っています。

CloudFormationテンプレートとTerraformコードを用意する

HCP Terraform WorkspaceのWorkflow TypeにVCS Driven Workflowを使います。

これは、GitHub等のVCS上のコードを使ってHCP TerraformでTerraformを実行するWorkflowです。

次のファイルを作成して、任意のGitHubリポジトリにPushしてください。

files/cfn.yaml
AWSTemplateFormatVersion: '2010-09-09'
Description: 'HCP Terraform OIDC Provider and IAM Role'

Parameters:
  HCPTerraformOrgName:
    Type: String
    Description: 'HCP Terraform Organization Name'
  HCPTerraformIAMRoleName:
    Type: String
    Description: 'HCP Terraform IAM Role Name'

Resources:
  HCPTerraformOIDCProvider:
    Type: 'AWS::IAM::OIDCProvider'
    Properties:
      Url: 'https://app.terraform.io'
      ClientIdList:
        - 'aws.workload.identity'

  HCPTerraformRole:
    Type: 'AWS::IAM::Role'
    Properties:
      RoleName: !Ref HCPTerraformIAMRoleName
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: Allow
            Principal:
              Federated: !GetAtt HCPTerraformOIDCProvider.Arn
            Action: 'sts:AssumeRoleWithWebIdentity'
            Condition:
              StringEquals:
                'app.terraform.io:aud': 'aws.workload.identity'
              StringLike:
                'app.terraform.io:sub': !Sub 'organization:${HCPTerraformOrgName}:project:*:workspace:*:run_phase:*'

  HCPTerraformRolePolicyAttachment:
    Type: 'AWS::IAM::RolePolicy'
    Properties:
      PolicyName: 'AdministratorAccess'
      RoleName: !Ref HCPTerraformRole
      PolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: Allow
            Action: '*'
            Resource: '*'

Outputs:
  RoleArn:
    Description: 'ARN of the created IAM Role'
    Value: !GetAtt HCPTerraformRole.Arn

まずはStackSetで利用するCloudFormationテンプレートです。

HCP Terraform用のIAM RoleとOIDC Providerを作成します。

詳しくは以下のブログをご確認ください。

https://dev.classmethod.jp/articles/hcp-terraform-iam-role-cfn/

次は、Terraformコードです。

main.tf
terraform {
  required_version = ">= 1.11.1"
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = ">= 5.82.2"
    }
    tfe = {
      source  = "hashicorp/tfe"
      version = ">= 0.62.0"
    }
  }
}

provider "aws" {
  region = var.aws_region
}

provider "tfe" {
  organization = var.hcp_tf_organization_name
}

resource "aws_cloudformation_stack_set" "this" {
  name             = "hcp-tf-role"
  permission_model = "SERVICE_MANAGED"

  auto_deployment {
    enabled                          = true
    retain_stacks_on_account_removal = false
  }

  parameters = {
    HCPTerraformOrgName     = var.hcp_tf_organization_name
    HCPTerraformIAMRoleName = var.hcp_tf_iam_role_name
  }
  template_body = file("${path.module}/files/cfn.yaml")
  capabilities  = ["CAPABILITY_NAMED_IAM"]
}

resource "aws_cloudformation_stack_set_instance" "this" {
  stack_set_name = aws_cloudformation_stack_set.this.name
  deployment_targets {
    organizational_unit_ids = [var.aws_ou_id]
  }
  region = var.aws_region
}

data "aws_organizations_organization" "org" {}

data "aws_organizations_organizational_unit_child_accounts" "accounts" {
  parent_id = var.aws_ou_id
}

locals {
  accounts_map = { for account in data.aws_organizations_organizational_unit_child_accounts.accounts.accounts : account.id => account }
}

resource "tfe_variable_set" "this" {
  for_each    = local.accounts_map
  name        = "auth-aws-${each.key}"
  description = "AWS credentials for ${each.value.name}"
}

resource "tfe_variable" "aws_provider_auth" {
  for_each = tfe_variable_set.this

  key             = "TFC_AWS_PROVIDER_AUTH"
  value           = "true"
  category        = "env"
  variable_set_id = each.value.id
}

resource "tfe_variable" "aws_run_role_arn" {
  for_each = tfe_variable_set.this

  key = "TFC_AWS_RUN_ROLE_ARN"
  # StackインスタンスのOutputをStackSet実行アカウントでは取得できないため、IAMロールのARNを直接指定する
  value           = "arn:aws:iam::${each.key}:role/${var.hcp_tf_iam_role_name}"
  category        = "env"
  variable_set_id = each.value.id
}
variables.tf
variable "aws_region" {
  description = "The AWS region to deploy the IAM role to"
  type        = string
  default     = "ap-northeast-1"
}

variable "aws_ou_id" {
  description = "The id of the OU to deploy the IAM role to"
  type        = string
}

variable "hcp_tf_organization_name" {
  description = "The name of the HCP Terraform organization"
  type        = string
}

variable "hcp_tf_iam_role_name" {
  description = "The name of the IAM role to create"
  type        = string
  default     = "hcp-tf-role"
}

Terraformコードでは、CloudFormation StackSetとVariable Setを作成します。

StackSetのデプロイ先はOUを指定しています。

OU内のAWSアカウントに先程のCloudFormationテンプレートで定義されたHCP Terraform用のIAM Role関連リソースが作成されます。

作成したIAM Role ARNを登録したHCP TerraformのVariable Setも同時に作成されます。

StackSetで作成したスタックのOutputをStackSet実行側のAWSアカウントで取ることができません。

そのため、ResourceからIAM Role ARNを渡す形ではなく、AWSアカウントIDとIAM Role名からARNを指定する形としています。

GitHub Pushまでのコマンド例
mkdir hcp-tf-iam-role-stack-set  && cd "$_"
vi variables.tf
vi main.tf
touch files/cfn.yaml
git add .
git commit -m "HCP Terraform IAM Role and OIDC ProviderのCloudFormationスタックセットを追加"
git push origin HEAD

Workspaceの作成(Version Control Workflow)

Terraform実行用のWorkspaceを作成します。

HCP Terraformのコンソールから、Workspaces-> New -> Workspace -> <任意のProject>を選択します。

Version Control Workflow -> <前の手順のTerraformコードをあげたリポジトリ> 選択します。

Workspace名は任意の名前を設定してください。

私は以下としました。

sato-org-hcp-tf-iam-role

Terraformコードがリポジトリのルート直下にない場合は、Terraform Working Directoryから設定してください。

他はデフォルトのままでOKです。Createを選択してWorkspaceを作成します。

vscode-drop-1741772695885-p8je17kemji.png

Terraform variableの設定が求められるため、環境にあった値を設定し、Save variablesを選択します。

vscode-drop-1741772844695-ev57sn3qinf.png

Variable setをWorkspaceにセットします。

作成したWorkspaceのVariablesを選択し、Apply variable setから作成した以下2つのVariablesをセットします。

  • StackSet管理アカウントアクセス用IAM Role(auth-aws-<AWSアカウントID>)
  • HCP Terraform Token(auth-hcp-tf-<HCP Terraform Team名>)

以下のようにセットされたらOKです。

vscode-drop-1741773366111-bfkl4qyrx2u.png

TerraformでCloudFormation StackSetを作成する(メンバーアカウントIAMロール作成・Variable sets作成)

WorkspaceのRuns -> New run -> (Run nameは設定しなくてもOK) -> Startを選択します。

Planが実行されます。内容を確認し、問題なければConfirm & applyを選択して、Applyを実行します。

Applyが成功したらリソースの作成は完了です。

vscode-drop-1741774403140-8ixm8jqh9ze.png

動作確認

CloudFormation StackSet管理AWSアカウントにログインして、リソースを確認してみます。

vscode-drop-1741774610491-gusy7ydvau.png

以下を確認できました。

  • StackSetが作成されている
  • 2つのAWSアカウントにスタックインスタンスが作成されている

HCP Terraformを確認してみます。

vscode-drop-1741774766703-y8vz6jp3y7.png

AWSアカウントに対応する2つのVariable setが作成されていることを確認できました。

最後にVariable setを使って、WorkspaceのPlan・Applyがうまくいくか確認してみます。

EC2インスタンスを作成するTerraformコードを実行しました。

Plan/Applyが成功し、メンバーアカウントにリソースを作成できました。

各アカウントのIAM RoleとVariable setが正常に設定できているようです。

vscode-drop-1741775629052-tprfwiksmr.png

vscode-drop-1741775464515-vgsdd299e9.png

補足: AWSアカウントが新規に作成された場合の対応

CloudFormation StackSetの自動デプロイは有効にしています。

AWSアカウントがOU内に移動してきたタイミングで、IAM Role関連のリソースがAWSアカウントにデプロイされます。

しかし、Variable setはTerraformで管理しているため自動的に作成されません。

Terraformをもう一度実行することで、Variable setが自動的に作成されます。

Variable set作成対象のAWSアカウントIDをOU IDからdataリソースで取得する形にしているため、Terraformコード自体の変更は不要で対応できます。

おわりに

AWSアカウントにHCP Terraformからアクセスできない状態なので、最初のIAM Role作成は悩ましい問題だなと感じました。

今回はCloudFormation StackSetを使った方法を紹介しました。

最初だけCloudFormation StackSet実行アカウントでは手作業が必要になりますが、以降の運用はかなり楽になるかと思います。

最初の手作業部分もWorkspace実行モードlocalでやれば、Terraformでほぼ完結できそうですね。

AFT導入済み環境だったら、AFTでやるのが一番シンプルで良いかと思います。(これのためだけに、AFTを入れるのはオーバースペックな気もします)

以上、AWS事業本部の佐藤(@chari7311)でした。

Share this article

facebook logohatena logotwitter logo

© Classmethod, Inc. All rights reserved.