こちらで提唱されているようにAWSは用途毎にアカウント自体を分けたほうがセキュリティ向上、作業ミスのリスク低減、コスト把握など様々な点でメリットがあります。
個人的には、開発環境用AWSアカウント、本番環境用AWSアカウントのようにわけることで、Terraformを実行するときに、そのありがたみを感じました。
気軽に destroy をできるので、試行錯誤しやすく(環境を作り直しやすい)、ソースコードがバグってて同じアカウント内の本番リソースを誤って変更してしまったらどうしよう。。。などの不安から解放されます。
ただ分割の弊害として、(何も考えずにやると)それぞれの環境ごとにIAMユーザーを用意し、環境ごとにサインイン、サインアウトを繰り返すハメになります。
特にQRコードのMFAを設定している場合、これらがかなりの手間となります。
スマホのロック解除してアプリ立ち上げて、コード確認して、、、手間すぎて運用できないと思います。(実際にすぐ心折れました)
で、その回避策として、ロールの切り替え(SwitchRole)という便利な機能があるのでそれを設定してみます。
これをすることにより一度サインインすれば、あとはクリックするだけで環境を切り替えられるようになります。
前提
CloudFormationを使用
今回はCloudFormation(CFn)を使用します。
僕は通常、Terraformを使用しているのですが、以下の考えからチームメンバーが使うIAMユーザー、ロールに関してはCloudFormationで操作しています。
- そもそも最初のTerraformを実行するユーザー作成自体は、コンソールから作成する必要がある。
ユーザー作成のコードはTerraforomとCFnを分けずに、CFnにまとめたほうが混乱しなそう。
- メンバー管理は、ディレクターを含むインフラエンジニア以外の人も、なるべくできるようにしたい。
黒い画面でコマンドを叩く Terraform は拒絶反応おこされそう。
環境
AWS環境(アカウント)を3つ使用します。
環境 | AWSアカウントID |
---|---|
踏み台(JUMP) | 111111111111 |
開発(DEV) | 222222222222 |
本番(PROD) | 333333333333 |
完成イメージ
CFnを使用して最終的にできるイメージは以下となります。
踏み台環境(JUMP)のIAMユーザー
メールアドレスでログインするルートユーザーではロールを切り替えることはできません。
スイッチ元アカウントでIAMユーザーを使用します。そもそもルートユーザーを常時使用することは非推奨です。
(ベストプラクティス 個々の IAM ユーザーを作成する)
今回は管理者用ユーザー、開発者用ユーザーの2人を作成します。
踏み台環境(JUMP)のIAMユーザーグループ
アクセス許可はIAMユーザーには直接設定しません。
IAMユーザーグループに割り当て、ユーザーはそのグループに所属させます。
(ベストプラクティス IAM ユーザーにアクセス許可を割り当てるためには、ユーザーグループを使用する)
開発環境(DEV)、本番環境(PROD)のIAMロール
スイッチ先アカウントでIAMロールを作成します。IAMユーザーではないです。
今回はあらかじめAWSで用意されている、「管理者用ポリシーAdministratorAccess」 と 「開発者用ポリシーPowerUserAccess」 をそれぞれ管理者用ロール、開発者用ロールに割り当てます。
マルチアカウント環境の構築手順
それでは設定していきます。
CloudFormation YAMLファイル作成
踏み台環境(JUMP)用のYAMLファイル作成
まずスイッチ元(IAMユーザーにサインインする環境)で適用するYAMLファイルを作成します。ファイル名は任意で大丈夫です。
# 踏み台アカウントでユーザーを作成する
AWSTemplateFormatVersion: '2010-09-09'
Description: IAM User|Group of Jump Account
Mappings:
TagsMap:
CreatedBy:
Value: "CloudFormation"
# 認証情報管理ポリシー
Resources:
ManageOwnAuthPolicy:
Type: 'AWS::IAM::ManagedPolicy'
Properties:
ManagedPolicyName: ManageOwnAuthPolicy
PolicyDocument:
Version: 2012-10-17
Statement:
- Sid: AllowViewAccountInfo
Effect: Allow
Action:
- iam:GetAccountPasswordPolicy
- iam:ListVirtualMFADevices
Resource: "*"
- Sid: AllowManageOwnPasswords
Effect: Allow
Action:
- iam:ChangePassword
- iam:GetUser
Resource: arn:aws:iam::*:user/${aws:username}
- Sid: AllowManageOwnAccessKeys
Effect: Allow
Action:
- iam:CreateAccessKey
- iam:DeleteAccessKey
- iam:ListAccessKeys
- iam:UpdateAccessKey
- iam:GetAccessKeyLastUsed
Resource: arn:aws:iam::*:user/${aws:username}
- Sid: AllowManageOwnVirtualMFADevice
Effect: Allow
Action:
- iam:CreateVirtualMFADevice
- iam:DeleteVirtualMFADevice
Resource: arn:aws:iam::*:mfa/${aws:username}
- Sid: AllowManageOwnUserMFA
Effect: Allow
Action:
- iam:DeactivateMFADevice
- iam:EnableMFADevice
- iam:ListMFADevices
- iam:ResyncMFADevice
Resource: arn:aws:iam::*:user/${aws:username}
- Sid: DenyAllExceptListedIfNoMFA
Effect: Deny
NotAction:
- iam:CreateVirtualMFADevice
- iam:EnableMFADevice
- iam:GetUser
- iam:ListMFADevices
- iam:ListVirtualMFADevices
- iam:ResyncMFADevice
- sts:GetSessionToken
Resource: "*"
Condition:
BoolIfExists:
aws:MultiFactorAuthPresent: 'false'
# 管理者グループ
AdminJumpPolicy:
Type: 'AWS::IAM::ManagedPolicy'
Properties:
ManagedPolicyName: AdminJumpPolicy
PolicyDocument:
Version: 2012-10-17
Statement:
- Sid: AdminAssumeRole
Effect: Allow
Action: 'sts:AssumeRole'
Resource:
- "arn:aws:iam::222222222222:role/AdminRoleFromJump"
- "arn:aws:iam::333333333333:role/AdminRoleFromJump"
AdminJumpGroup:
Type: AWS::IAM::Group
Properties:
GroupName: AdminJumpGroup
ManagedPolicyArns:
- !Ref ManageOwnAuthPolicy
- !Ref AdminJumpPolicy
# 開発者グループ
DevJumpPolicy:
Type: 'AWS::IAM::ManagedPolicy'
Properties:
ManagedPolicyName: DevJumpPolicy
PolicyDocument:
Version: 2012-10-17
Statement:
- Sid: DevAssumeRole
Effect: Allow
Action: 'sts:AssumeRole'
Resource:
- "arn:aws:iam::222222222222:role/DevRoleFromJump"
- "arn:aws:iam::333333333333:role/DevRoleFromJump"
DevJumpGroup:
Type: AWS::IAM::Group
Properties:
GroupName: DevJumpGroup
ManagedPolicyArns:
- !Ref ManageOwnAuthPolicy
- !Ref DevJumpPolicy
# ユーザー定義
User01:
Type: 'AWS::IAM::User'
Properties:
UserName: tester-01
Groups:
- !Ref AdminJumpGroup
Tags:
- Key: "my:createdBy"
Value: !FindInMap [TagsMap,CreatedBy,Value]
User02:
Type: 'AWS::IAM::User'
Properties:
UserName: tester-02
Groups:
- !Ref DevJumpGroup
Tags:
- Key: "my:createdBy"
Value: !FindInMap [TagsMap,CreatedBy,Value]
User03:
Type: 'AWS::IAM::User'
Properties:
UserName: tester-03
Groups:
- !Ref DevJumpGroup
Tags:
- Key: "my:createdBy"
Value: !FindInMap [TagsMap,CreatedBy,Value]
このYAMLファイルのポイントは以下のとおりになります。
認証情報管理ポリシー ManageOwnAuthPolicy
公式サンプル MFA で認証された IAM ユーザーが My Security Credentials ページで自分の認証情報を管理できるようにする を利用しました。
DenyAllExceptListedIfNoMFA の設定により2段階認証されていないと NotAction 以外の操作はできないようになっています。このサンプルでは、実質MFA作成以外の操作はできません。
また今回は、以下4点の修正を行いました。
- AllowManageOwnSigningCertificates デジタル署名用証明書の操作を削除。
利用しない。 - AllowManageOwnSSHPublicKeys CodeCommit の SSH パブリックキーの操作を削除。
利用しない。 - AllowManageOwnGitCredentials CodeCommit の Git 認証情報の操作を削除。
利用しない。 - AllowManageOwnAccessKeys の iam:GetAccessKeyLastUsed を追加。
アクセスキーの最終使用日時は確認できるようにしておきたい。
管理者、開発者各グループ設定 [Admin|Dev]JumpGroup
スイッチ先アカウントで作成するIAMロール名を Resource に定義して、各環境の各ロールにスイッチできるようにします。
ユーザー定義 User
各ユーザーを作成します。Groups にてどのグループに所属させるか設定します。
なお僕の運用している環境は10人前後のメンバー管理なので、この記載方法でなんとかなっています。より大規模な環境ならばループ処理するとかいろいろ考えなければいけないかもです。
開発環境(DEV)、本番環境(PROD)用のYAMLファイル作成
次にスイッチ先(IAMロールにスイッチする環境)で適用するYAMLファイルを作成します。ファイル名は任意で大丈夫です。
# 各AWSアカウント環境でロールを作成する
AWSTemplateFormatVersion: 2010-09-09
Description: IAM Role of Service Account
# 踏み台AWSアカウントIDを引数として入力させる
Parameters:
JumpAccountID:
ConstraintDescription: AWS ID is Only Number with 12digit
MinLength: 12
MaxLength: 12
AllowedPattern: "[0-9]*"
Description: Please Enter Jump AWS Account ID!
Type: String
Mappings:
TagsMap:
CreatedBy:
Value: "CloudFormation"
# 管理者ロール
Resources:
AdminRoleFromJump:
Type: AWS::IAM::Role
Properties:
RoleName: AdminRoleFromJump
AssumeRolePolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Principal:
AWS:
- !Sub "arn:aws:iam::${JumpAccountID}:user/tester-01"
Action:
- 'sts:AssumeRole'
Condition:
Bool:
aws:MultiFactorAuthPresent: true
ManagedPolicyArns:
- "arn:aws:iam::aws:policy/AdministratorAccess"
Tags:
- Key: "my:createdBy"
Value: !FindInMap [TagsMap,CreatedBy,Value]
# 開発者ロール
DevRoleFromJump:
Type: AWS::IAM::Role
Properties:
RoleName: DevRoleFromJump
AssumeRolePolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Principal:
AWS:
- !Sub "arn:aws:iam::${JumpAccountID}:user/tester-02"
- !Sub "arn:aws:iam::${JumpAccountID}:user/tester-03"
Action:
- 'sts:AssumeRole'
Condition:
Bool:
aws:MultiFactorAuthPresent: true
ManagedPolicyArns:
- "arn:aws:iam::aws:policy/PowerUserAccess"
Tags:
- Key: "my:createdBy"
Value: !FindInMap [TagsMap,CreatedBy,Value]
以下がポイントになります。
踏み台AWSアカウントID引数 JumpAccountID
こちらの記事を参考にさせていただきましたmm
CFnでの作成時に踏み台のAWSアカウントID(111111111111) を入力します。他のプロジェクトでもこのYAMLファイルをそのまま利用できたほうが便利かな、と考え引数にしてます。
管理者・開発者各ロール設定 [Admin|Dev]RoleFromJump
Principal で踏み台アカウントのどのユーザーが当該ロールにスイッチできるかを設定します。JumpAccountID には、前述の引数で入力した値が入ります。
ちなみにこの Principal 設定ですが前述のユーザー定義同様、僕は10人前後の管理かつ、環境が開発、ステージング、本番と3つ程度なので、この記載方法でなんとかなっています。
より大規模で便利な仕組みを構築できていない状態であれば、例えば user/hoge-*にするなど許可範囲を広げてユーザー更新作業の運用コストを減らすなど、いろいろ考えなければいけないかもです。
JSON ポリシーの要素: Principal が参考になると思います。
またCondition でMultiFactorAuthPresent: true を指定することにより2段階認証されていないとスイッチできないようにしています。
CloudFormation 実行
設定ファイルを適用します。
踏み台環境(JUMP) でCloudFormation実行
踏み台環境(JUMP) 用のAWSコンソールにログインします。
ほんとに一発目であればIAMユーザーが存在しないのでルートユーザー(メールアドレスでサインインするアカウント)でやります。
既にIAMユーザーがいる場合、そちらで実施しましょう。CloudFormationでIAMの設定変更をするので、AdministratorAccess権限だとハマらずに楽です。
AWSコンソールにログイン後、 画面上部検索窓 [ CloudFormation ] で検索 → 当該サービスをクリック → 画面右上の [ スタックの作成 ] → [ 新しいリソースを使用(標準) ] で以下の通り選択し、 [ ファイルの選択 ] から作成した、踏み台環境(JUMP) 用 YAMLファイルをアップロードし、次へ 。
スタックの名前は、任意のわかりやすい名前をつけて [ 次へ ]、スタックオプションの設定 画面は全部デフォルトで大丈夫です。
レビュー画面の一番下の [ AWS CloudFormation によって IAM リソースがカスタム名で作成される場合があることを承認します。] にチェックを入れ、 [ スタックの作成 ] クリックします。
上記のチェックを入れないと「Requires capabilities : [CAPABILITY_NAMED_IAM]」のエラーが出ます。
作成が完了すると CREATE_COMPLETE となります。
[ リソース ] タブで作成されたリソースを確認でき、名前をクリックするとそれぞれのリソース画面に遷移できます。
ここから各IAMユーザーの機密情報を設定してメンバーに渡していきます。ここらへんの塩梅は、それぞれの運用方針次第かと思います。
僕は作成されたユーザー画面へ遷移して、パスワード、QRコードを設定して、各メンバーへ渡しています。
(公式的にはIAM ユーザーを安全に作成するにはどうすればよいですか? で方針が示されています)
QRコードの設定は、以前書いたこちらの記事を参照ください。
開発環境(DEV)・本番環境(PROD) でCloudFormation実行
スイッチ元の設定が完了したので、次にスイッチ先の設定を行います。
開発環境(DEV)・本番環境(PROD)のAWSコンソールにログインし、踏み台環境(JUMP)と同様の手順で、CloudFormationを実行します。
唯一の違いは、[ スタックの詳細を指定 ] 画面で踏み台AWSアカウントIDをパラメータとして入力する必要があることです。
スイッチロール確認
設定が完了したので、実際にスイッチロールの確認をしてみます。
踏み台環境(JUMP)環境のAWSコンソールにログインし、画面上部の自分のAWSアカウント名をクリックして [ ロールの切り替え ] を選択します。
次の画面で適切に情報を入力し、[ ロールの切り替え ]をクリックすると各環境へスイッチできます。
うまく行かない場合
スイッチの際、「1 つ以上のフィールドに無効な情報があります。情報を確認するか、管理者に連絡してください。」 とエラーになる場合があります。
設定ミス、入力ミス
YAMLで定義した許可設定がスイッチ元、先でそれぞれ合致しているか確認してください。また間違いなく入力できているかも確認してみてください。
2段階認証でログインしてない
踏み台のIAMユーザーで2段階認証を設定する前に、パスワードのみでログインし、MFAを設定、サインアウトせずにそのままスイッチロールをしようとするとエラーとなります。
一度サインアウトしてから再度サインイン、スイッチロールをしてみてください。
Tips
IAMユーザー情報の更新、追加、削除
IAMユーザーの更新、追加、削除などの操作は、CloudFormation の画面右側にあるタブの [ 変更セット ] から変更しましょう。
やり方は、前述の CloudFormation実行 と全く一緒です。
途中で変更セットの確認ができるので誤った変更をしてないか適用前に確認してください。
便利ツール AWS Extend Switch Roles
こちらの記事で紹介されていた、AWS Extend Switch Roles がとても便利です。
今回の例だと以下のような設定をすることで、いつでもすぐに切り替えられます。
[Dev]
role_arn = arn:aws:iam::222222222222:role/AdminRoleFromJump
color = 87ceeb
[Prod]
role_arn = arn:aws:iam::333333333333:role/AdminRoleFromJump
color = ff2600
またスイッチロールの履歴(画面上部の自分のAWSアカウント名をクリックして表示される)は直近の5個しか保存されない点からも、上記の静的設定をしておいたほうが良いと思います。
今回は以上です〜ノシ
参考
アリガト━━━ヾ(´∀`)ノ━━━━♪
公式ドキュメント ロールへの切り替え (コンソール)
Swith Roleで複数のAWSアカウント間を切替える
AWSにおけるマルチアカウント管理の手法とベストプラクティス