ハンズオンで使う IAM ユーザーを AWS CloudFormation StackSets で一括作成してみた

ハンズオンで使う IAM ユーザーを AWS CloudFormation StackSets で一括作成してみた

2026.05.22

ハンズオン開催のために複数の AWS アカウントに一度に IAM ユーザーを作成したいケースがあったため、AWS CloudFormation StackSets で作成してみました。作成する IAM ユーザーのパスワードは Secrets Manager で生成し、各アカウントのパスワードを管理アカウントからシェルスクリプトで取得できるようにもしてみました。

前提となる環境と作成するリソース

本ブログで紹介する CloudFormation テンプレートは次の状況下で利用することを前提としています。

  • AWS Organizations 環境であること
  • ハンズオン用 AWS アカウントを特定の 1 つの OU 配下にまとめて格納していること

CloudFormation テンプレートで AWS アカウントに作成するリソースは次のとおりです。

  • IAM ユーザー
  • IAM ユーザーのパスワードを生成する Secrets Manager のシークレット
  • StackSets 実行アカウントからスイッチロールし、シークレットの値を取得するための IAM ロール

最後の IAM ロールを利用して、StackSets 実行アカウントから StackSets 展開先の OU 配下のアカウントの IAM ユーザーパスワードを一括で取得するスクリプトも紹介します。各アカウントにサインインして個別にパスワードを確認する手間を省くことができます。
上記のイメージ図です。

create-handson-iam-users-with-stacksets-1

IAM ユーザー作成 CloudFormation テンプレート

StackSets で展開する用の CloudFormaon テンプレートです。
IAM ユーザーに付与する権限は AdministratorAccess としているため、必要に応じて変更する必要があります。

AWSTemplateFormatVersion: '2010-09-09'
Description: Create an IAM user for hands-on

Metadata:
  AWS::CloudFormation::Interface:
    ParameterGroups:
      - Label:
          default: IAM User Settings
        Parameters:
          - UserName
          - PasswordLength
      - Label:
          default: Password Reader Role Settings
        Parameters:
          - AdminAccountId
          - PasswordReaderRoleName
    ParameterLabels:
      UserName:
        default: IAM user name
      PasswordLength:
        default: Generated password length
      AdminAccountId:
        default: Trusted admin account ID
      PasswordReaderRoleName:
        default: Password reader role name

Parameters:
  UserName:
    Type: String
    Description: IAM user name for the hands-on user.
    Default: handson-user
    MinLength: 1
    MaxLength: 64

  PasswordLength:
    Type: Number
    Description: Length of the generated password.
    Default: 20
    MinValue: 8
    MaxValue: 128

  AdminAccountId:
    Type: String
    Description: AWS account ID that runs StackSets and is allowed to assume the password reader role.
    AllowedPattern: '\d{12}'
    ConstraintDescription: Must be a 12-digit AWS account ID.

  PasswordReaderRoleName:
    Type: String
    Description: Name of the IAM role that the admin account assumes to read the password secret.
    Default: handson-password-reader-role
    MinLength: 1
    MaxLength: 64

Resources:
  UserPasswordSecret:
    Type: AWS::SecretsManager::Secret
    Properties:
      Name: !Sub '/handson/iam-user/${UserName}'
      Description: !Sub 'Console login password for IAM user ${UserName}'
      GenerateSecretString:
        SecretStringTemplate: !Sub '{"username": "${UserName}"}'
        GenerateStringKey: password
        PasswordLength: !Ref PasswordLength
        ExcludeCharacters: '"@/\$`'
        RequireEachIncludedType: true

  HandsOnUser:
    Type: AWS::IAM::User
    Properties:
      UserName: !Ref UserName
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/AdministratorAccess
      LoginProfile:
        Password: !Sub '{{resolve:secretsmanager:${UserPasswordSecret}:SecretString:password}}'
        PasswordResetRequired: true

  PasswordReaderRole:
    Type: AWS::IAM::Role
    Properties:
      RoleName: !Ref PasswordReaderRoleName
      Description: !Sub 'Allows account ${AdminAccountId} to read the hands-on user password secret.'
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: Allow
            Principal:
              AWS: !Sub 'arn:aws:iam::${AdminAccountId}:root'
            Action: sts:AssumeRole
      Policies:
        - PolicyName: ReadHandsOnUserPasswordSecret
          PolicyDocument:
            Version: '2012-10-17'
            Statement:
              - Sid: ReadPasswordSecret
                Effect: Allow
                Action:
                  - secretsmanager:GetSecretValue
                  - secretsmanager:DescribeSecret
                Resource: !Ref UserPasswordSecret

パラメータは IAM ユーザーの名前とパスワードの長さ、パスワードを取得するための IAM ロール名とスイッチロール元の AWS アカウントの指定です。

IAM ユーザーのパスワードは、Secrets Manager の GenerateSecretString で乱数を生成して設定しています。そのため、パスワードの値は StackSets 展開後に Secrets Manager から確認することになります。後述するシェルスクリプトでパスワードを取得しない場合は、AWS IAM Identity Center などから StackSets 展開先のアカウントにサインインして確認します。
また、パスワードに関しては PasswordResetRequired: true としており、ハンズオン受講者が初回ログイン時に自分でパスワードを変更する必要があるようにしています

パスワードを取得するための IAM ロールの権限は、パスワードが格納されているシークレットの値を取得するのみの権限にしています。

StackSets でデプロイ

実際に展開してみます。今回は管理アカウントからデプロイします。
マネジメントコンソールの AWS CloudFormation を開き、「StackSets」メニューにおいて「スタックを作成」を実行します。

create-handson-iam-users-with-stacksets-2

事前にテンプレートのファイル名を handson-iam-user.yaml にして保存しておき、アップロードします。

create-handson-iam-users-with-stacksets-3

パラメータを入力します。「Trusted admin account ID」には StackSets を展開しているアカウント ID を入力しています。

create-handson-iam-users-with-stacksets-4

「AWS CloudFormation によって IAM リソースがカスタム名で作成される場合があることを承認します。」にチェックを入れて次に進みます。

create-handson-iam-users-with-stacksets-5

デプロイターゲットにはハンズオンアカウントが格納されている OU を指定します。自動デプロイオプションは、新規ハンズオンアカウントを発行した際に自動で設定されるように自動デプロイをアクティブ化しています。また、展開先のリージョンの指定は IAM がグローバルリソースであることから 1 つのリージョンの指定のみでよいです。今回は東京リージョンを指定します。

create-handson-iam-users-with-stacksets-6

最後に設定内容を確認して問題なければ、「送信」を実行して完了です。
デプロイ後にスタックインスタンスタブで各アカウントへの展開が成功したかどうかを確認できます。下記画像の 2 アカウントに展開した例です。

create-handson-iam-users-with-stacksets-7

IAM ユーザーのパスワード取得シェルスクリプト

StackSets 実行アカウント(多くの場合は管理アカウントか StackSets の委任アカウントだと思います)から StackSets を展開した OU 配下の各アカウントの IAM ロールに順次スイッチロールし、Secrets Manager に保管されているパスワードを取得するシェルスクリプトです。注意点として、指定した OU_ID 直下のアカウントのみを対象としており、階層化された OU 構成には対応していません。

#!/bin/sh

OU_ID="ou-xxxx-xxxxxxxx"
USER_NAME="handson-user"
ROLE_NAME="handson-password-reader-role"
SECRET_ID="/handson/iam-user/${USER_NAME}"

echo "account_id,account_name,username,password"

aws organizations list-accounts-for-parent \
  --parent-id "${OU_ID}" \
  --query 'Accounts[?Status==`ACTIVE`].[Id,Name]' \
  --output text |
while read -r ACCOUNT_ID ACCOUNT_NAME; do
  CREDS=$(aws sts assume-role \
    --role-arn "arn:aws:iam::${ACCOUNT_ID}:role/${ROLE_NAME}" \
    --role-session-name "fetch-handson-password" \
    --query 'Credentials.[AccessKeyId,SecretAccessKey,SessionToken]' \
    --output text)

  AWS_ACCESS_KEY_ID=$(echo "${CREDS}" | awk '{print $1}')
  AWS_SECRET_ACCESS_KEY=$(echo "${CREDS}" | awk '{print $2}')
  AWS_SESSION_TOKEN=$(echo "${CREDS}" | awk '{print $3}')

  PASSWORD=$(AWS_ACCESS_KEY_ID="${AWS_ACCESS_KEY_ID}" \
    AWS_SECRET_ACCESS_KEY="${AWS_SECRET_ACCESS_KEY}" \
    AWS_SESSION_TOKEN="${AWS_SESSION_TOKEN}" \
    aws secretsmanager get-secret-value \
      --secret-id "${SECRET_ID}" \
      --query SecretString --output text |
    jq -r .password)

  echo "${ACCOUNT_ID},${ACCOUNT_NAME},${USER_NAME},${PASSWORD}"
done

下表の変数を指定する必要があります。前述した StackSets の展開先 OU と指定したパラメータに合わせる必要があります。

変数 入力する値
OU_ID StackSets を展開した OU
USER_NAME CloudFormation パラメータで指定した IAM ユーザー名
ROLE_NAME CloudFormation パラメータで指定した IAM ロール名

スクリプトの動作はシンプルです。OU_ID 配下のアカウント一覧を取得し、各アカウントにスイッチロールしてパスワードが格納されたシークレットの値を取得するだけです。エラーハンドリングはしていません。

実行結果例

AWS CloudShell で実行する場合は、シェルスクリプトをアップロードして実行します。
スクリプト名を fetch-handson-passwords.sh とした場合の実行コマンドは下記です。

sh fetch-handson-passwords.sh

実行結果例です。CloudFormation のパラメータでパスワード長を 15 桁に指定している場合の出力例です。

$ sh fetch-handson-passwords.sh 
account_id,account_name,username,password
111122223333,Sandbox01,handson-user,Nd&JEK9s-6x4b}d
444455556666,Sandbox02,handson-user,c'Ho4ki9^TVS5n]

なお、ファイルに出力したい場合は次のコマンドで実現できます。

sh fetch-handson-passwords.sh > output.csv

さいごに

AWS CloudFormation StackSets と Secrets Manager を組み合わせることで、複数の AWS アカウントにハンズオン用 IAM ユーザーを作成する方法を紹介しました。ハンズオン以外にも、検証用アカウントの初期セットアップなどにも応用できると思います。
以上、このブログがどなたかのご参考になれば幸いです。

この記事をシェアする

AWSのお困り事はクラスメソッドへ

関連記事