ハンズオンで使う IAM ユーザーを AWS CloudFormation StackSets で一括作成してみた
ハンズオン開催のために複数の 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 ユーザーパスワードを一括で取得するスクリプトも紹介します。各アカウントにサインインして個別にパスワードを確認する手間を省くことができます。
上記のイメージ図です。

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」メニューにおいて「スタックを作成」を実行します。

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

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

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

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

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

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 ユーザーを作成する方法を紹介しました。ハンズオン以外にも、検証用アカウントの初期セットアップなどにも応用できると思います。
以上、このブログがどなたかのご参考になれば幸いです。







