【AWS】複数のAWSアカウントを管理するためにStackSetを活用する #2/2【Multi Accounts】

# はじめに
この記事は全部で2部構成となります。

[【AWS】複数のAWSアカウントを管理するためにStackSetを活用する #1/2【Multi Accounts】](https://qiita.com/tmiki/items/6a72b9ce67ed0a85e243)
[【AWS】複数のAWSアカウントを管理するためにStackSetを活用する #2/2【Multi Accounts】](https://qiita.com/tmiki/items/aca0f1b549fd8723d38e)　**←いまココ**

本稿では、AWS OrganizationsとCloudFormation StackSetを利用し、複数AWSアカウント管理のための最低限の基盤を作ります。
最終的に、新規で作成されるAWSアカウントに対し、下記のAWSリソースが作成されます。

- 新規AWSアカウント上に作成されるAWSリソース
    - AWS Organizationsで作成されるIAM Role
        - PermissionsはAdministratorAccessと同等のPolicyを持つ
        - Trust Relationshipsは、OrganizationsのmasterアカウントのPrincipalであれば誰でもAssumeRole出来る
    - CloudFormation StackSetで作成する、各権限用のIAM Role
        - それぞれ、管理者、経理部門などの権限に応じたPermissionsが付与されている
        - すべての子AWSアカウントに対して同等のIAM Roleを作成できる

**※2020/2/28追記**
記事を書いているときに気付かなかったのですが、2020/2/12にこのようなAWS Blog記事がありました。

New: Use AWS CloudFormation StackSets for Multiple Accounts in an AWS Organization | AWS News Blog
https://aws.amazon.com/jp/blogs/aws/new-use-aws-cloudformation-stacksets-for-multiple-accounts-in-an-aws-organization/

StackSetをOrganizationsに紐づけて、対象のAWSアカウントにStack Instanceをデプロイすることが出来るようです。
本稿で手動で実施していることをより簡単に実現できる方法なので、こちらを使うのが良いと思います。


# 前提条件／検証環境
- masterアカウント（※StackSetのドキュメント上、Administrator accountに該当）
    - AWSアカウントID: 123456789012
    - AWS CLIのプロファイル名: master
    - AWS Organizationsを有効にしておく
- 子AWSアカウント（新規作成するAWSアカウント。※StackSetのドキュメント上、Target accountに該当）
    - AWSアカウントID: 987654321098
    - AWSアカウントのエイリアス名: investigate-multi-accounts
    - AWS CLIのプロファイル名: investigate-multi-accounts
    - 管理用のIAM Role: OrganizationAccountAccessRole
- StackSet構成
    - masterアカウント
        - StackSet用のIAM Role: AWSCloudFormationStackSetAdministrationRole
    - 子AWSアカウントに、StackSetを利用して追加するAWSリソース
        - IAM Role
            - FullAdministratorRole
            - AccountingRole

# 手順の概要
- masterアカウントに、CloudFormation StackSet用のRoleを作成する
- AWS Organizationsを利用して、新規にAWSアカウントを作成する
- 作業用環境にSwitch Roleの設定をする
	- AWS CLI
	- AWS Management Console
- masterアカウントにStackSetを作る
	- 子AWSアカウントに作成するAWSリソースを定義したCloudFormation Templateを作る
	- masterアカウントにStackSetを作る
- 子AWSアカウントにStack Instanceを作り、必要なAWSリソースを作成する
	- masterアカウント上でStack Instance作成操作を実行する
- 子AWSアカウントで、作成されたStack Instanceを確認する
	- Stack Instanceの確認
	- 作成されたIAM Roleの確認

# 手順詳細
## masterアカウントに、CloudFormation StackSet用のRoleを作成する
CloudFormationサービスが子AWSアカウントのCFn Stack Instanceを作る際のIAM Roleが必要となりますので、これをまず作成します。
Role名は「AWSCloudFormationStackSetAdministrationRole」となります。

CloudFormationサービスが利用するIAM Role、AssumeRoleの順序については下記にまとめてあります。
https://qiita.com/tmiki/items/6a72b9ce67ed0a85e243#cfn-stackset%E3%81%AB%E3%82%88%E3%82%8Btarget-account%E3%81%B8%E3%81%AEstack%E4%BD%9C%E6%88%90%E3%81%AE%E4%BB%95%E7%B5%84%E3%81%BF

以下のPermissions、Trust Relationshipsを持つIAM Roleを、「AWSCloudFormationStackSetAdministrationRole」という名称で作成します。

```json:Permissions
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Action": [
                "sts:AssumeRole"
            ],
            "Resource": [
                "arn:aws:iam::*:role/OrganizationAccountAccessRole"
            ],
            "Effect": "Allow"
        }
    ]
}

```


```json:Trust&nbsp;Relationships
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Service": "cloudformation.amazonaws.com"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}
```

同等のIAM Roleを作成できるCFn templateを下記にも置いておきます。
https://github.com/tmiki/cloud-formation-templates/blob/master/stack-sets/cfn-stack-set-01-administrator-account.yml

## AWS Organizationsを利用して、新規にAWSアカウントを作成する
まずAWS CLIの設定を行い、masterアカウントに対して各APIが実行できることを確認します。

```:aws&nbsp;sts&nbsp;get-caller-identity
$ aws --profile master sts get-caller-identity
{
    "Account": "123456789012",
    "UserId": "AIDA.................",
    "Arn": "arn:aws:iam::123456789012:user/tmiki"
}
```

次にmasterアカウントでorganizations create-accountを実行し、子となるAWSアカウントを作ります。

新規作成対象のAWSアカウントには、「OrganizationAccountAccessRole」という名称のIAM Roleが併せて作成されます。これはAdministrators権限を持ち、masterアカウントからのAssumeRoleを許可するTrustRelationshipを持ちます。
このIAM Role名は「--role-name」オプションで変更することが可能です。

create-account — AWS CLI 1.18.5 Command Reference
https://docs.aws.amazon.com/cli/latest/reference/organizations/create-account.html

```:aws&nbsp;organizations&nbsp;create-account
$ aws --profile master organizations create-account \
  --email youremail+investigate-multi-accounts@gmail.com \
  --account-name investigate-multi-accounts \
  --iam-user-access-to-billing ALLOW

{
    "CreateAccountStatus": {
        "RequestedTimestamp": 1582527438.181,
        "State": "IN_PROGRESS",
        "Id": "car-e5..............................",
        "AccountName": "investigate-multi-accounts"
    }
}
```

上記のレスポンスからは、新規作成したAWSアカウントのIDが分からないので、organizations list-accountsを実行し、AWSアカウントIDを確認します。
queryオプションを指定し、Nameが「investigate-multi-accounts」となっているもののみを抽出します。

list-accounts — AWS CLI 1.18.5 Command Reference
https://docs.aws.amazon.com/cli/latest/reference/organizations/list-accounts.html

```:aws&nbsp;organizations&nbsp;list-accounts 
$ aws --profile master organizations list-accounts \
  --query "Accounts[?Name=='investigate-multi-accounts']"
[
    {
        "Status": "ACTIVE",
        "Name": "investigate-multi-accounts",
        "Email": "youremail+investigate-multi-accounts@gmail.com",
        "JoinedMethod": "CREATED",
        "JoinedTimestamp": 1582527440.143,
        "Id": "987654321098",
        "Arn": "arn:aws:organizations::123456789012:account/o-**********/987654321098"
    }
]
```

なお、AWSアカウントIDがすでに分かっていれば、organizations describe-accountでも同等の結果を得られます。

describe-account — AWS CLI 1.18.5 Command Reference
https://docs.aws.amazon.com/cli/latest/reference/organizations/describe-account.html

```:aws&nbsp;organizations&nbsp;describe-account
$ aws --profile master organizations describe-account \
  --account-id 987654321098

{
    "Account": {
        "Status": "ACTIVE",
        "Name": "investigate-multi-accounts",
        "Email": "youremail+investigate-multi-accounts@gmail.com",
        "JoinedMethod": "CREATED",
        "JoinedTimestamp": 1582527440.143,
        "Id": "987654321098",
        "Arn": "arn:aws:organizations::123456789012:account/o-**********/987654321098"
    }
}
```

AWS Management Consoleからも、新規作成したAWSアカウントを確認できます。
![2020-02-24_15h58_37.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/137777/e8454794-61a3-75b3-cf29-7de0f20ac0c5.png)


## 作業用環境にSwitch Roleの設定をする
作成した子AWSアカウントの操作をするため、AWS CLI、AWS Management ConsoleにSwitch Roleの設定を追加します。

### AWS CLI
AWS CLIの設定に、新規作成したAWSアカウントのIAM RoleをAssumeRoleするための設定を追加します。
予め「master」という名称のprofileを設定済みであるものとします。

```:~/.aws/config
$ cat ~/.aws/config
[profile master]
aws_account_id = your-aws-master-account
output = json
```

```:~/.aws/credentials
$ cat ~/.aws/credentials
[master]
aws_access_key_id = AKIA*************
aws_secret_access_key = ****************************************
```

AWS CLIの設定ファイルに以下の内容を追記します。

```:vi&nbsp;~/.aws/config
$ vi ~/.aws/config
### 下記を追記する

[profile investigate-multi-accounts]
source_profile = master
role_arn = arn:aws:iam::987654321098:role/OrganizationAccountAccessRole
```

sts get-caller-identityを発行し、AssumeRole出来ることを確認します。

get-caller-identity — AWS CLI 1.18.5 Command Reference
https://docs.aws.amazon.com/cli/latest/reference/sts/get-caller-identity.html

```:aws&nbsp;--profile&nbsp;investigate-multi-accounts&nbsp;sts&nbsp;get-caller-identity
$ aws --profile investigate-multi-accounts sts get-caller-identity
{
    "Account": "987654321098",
    "UserId": "AROA*****************:botocore-session-1582532666",
    "Arn": "arn:aws:sts::987654321098:assumed-role/OrganizationAccountAccessRole/botocore-session-1582532666"
}
```

### AWS Management Console
必要に応じて、AWS Management Consoleから、新規作成したAWSアカウントにAssumeRole出来るよう設定します。
Chromeを利用しているのであれば、AWS Extend Switch Rolesというプラグインを利用すると便利です。

https://github.com/tilfin/aws-extend-switch-roles

これはAWS CLIと同一のフォーマット（i.e. 上記で追加した~/.aws/configの内容そのまま）で設定を追加できます。

## masterアカウントにStackSetを作る
次から、いよいよCloudFormation StackSetを利用して、Stackを作っていきます。

### 子AWSアカウントに作成するAWSリソースを定義したCloudFormation Templateを作る

子AWSアカウントには、とりあえず管理者用IAM Role「FullAdministratorRole」と、財務部門向けIAM Role「AccountingRole」を作ることとします。
以下のCFnテンプレートを「iam-roles-in-the-target-account.yaml」というファイル名で作成します。

下記＋αを記載したCFnテンプレートを、GitHubにも置いておきます。
https://github.com/tmiki/cloud-formation-templates/blob/master/stack-sets/cfn-stack-set-11-resources.yml

```yaml:iam-roles-in-the-target-account.yaml
AWSTemplateFormatVersion: '2010-09-09'

# ------------------------------------------------------------
# Parameters section
# ------------------------------------------------------------
Parameters:
  AdministratorAwsAccountId:
    Description:
      An AWS account id where all IAM users created in and they will be principals placed into each Role's trust relationship policy.
    Type: Number
    Default: 123456789012
  FullAdministratorRoleName:
    Description:
      The IAM Role name of Full Administrators.
    Type: String
    Default:
      FullAdministratorRole
  AccountingRoleName:
    Description:
      The IAM Role name of Accountants.
    Type: String
    Default:
      AccountingRole


# ------------------------------------------------------------
# Resources section
# ------------------------------------------------------------
Resources:
  # AWS::IAM::Role - AWS CloudFormation
  # https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-iam-role.html
  FullAdministratorRole:
    Type: AWS::IAM::Role
    Properties: 
      RoleName: !Ref FullAdministratorRoleName
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: "Allow"
            Principal:
              AWS:
                - !Ref AdministratorAwsAccountId
            Action:
              - sts:AssumeRole
      Description:
        This role allows IAM users in the administrator account to perform AssumeRole to obtain the privileges of this AWS account
      Path: '/'
      Policies: 
        - PolicyName: AllowAdministratorAccess
          PolicyDocument:
            Version: "2012-10-17"
            Statement:
              - Effect: "Allow"
                Action: "*"
                Resource: "*"
      Tags: 
        - Key: Name
          Value: !Ref FullAdministratorRoleName
        - Key: EnvName
          Value: All

  # AWS::IAM::Role - AWS CloudFormation
  # https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-iam-role.html
  AccountingRole:
    Type: AWS::IAM::Role
    Properties: 
      RoleName: !Ref AccountingRoleName
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: "Allow"
            Principal:
              AWS:
                - !Ref AdministratorAwsAccountId
            Action:
              - sts:AssumeRole
      Description:
        This role allows IAM users in the administrator account to invoke billing-related actions within the target AWS account.
      Path: '/'
      ManagedPolicyArns: 
        - arn:aws:iam::aws:policy/job-function/Billing
      Tags: 
        - Key: Name
          Value: !Ref AccountingRoleName
        - Key: EnvName
          Value: All

```

### masterアカウントにStackSetを作る
AWS CLIから以下のように実行してStackSetを作ります。
この時点では、masterアカウントにStackSetが作成されたのみで、子AWSアカウントには何も作成されていません。

create-stack-instances — AWS CLI 1.18.5 Command Reference
https://docs.aws.amazon.com/cli/latest/reference/cloudformation/create-stack-instances.html

```:aws&nbsp;--profile&nbsp;master&nbsp;cloudformation&nbsp;create-stack-set
$ aws --profile master cloudformation create-stack-set \
  --region us-east-1 \
  --stack-set-name basic-iam-roles \
  --template-body file://iam-roles-in-the-target-account.yaml \
  --parameters ParameterKey=AdministratorAwsAccountId,ParameterValue=123456789012 \
  --execution-role-name OrganizationAccountAccessRole \
  --capabilities CAPABILITY_NAMED_IAM

{
    "StackSetId": "basic-iam-roles:********-****-****-****-************"
}
```

なお今回、StackSetの作成先として、 us-east-1 (North Virginia)を明示的に指定しています。
これはAWSアカウント／AWSリソース管理のポリシー上の問題となりますが、GlobalなAWSリソースを作成するためのCloudFormation Stack/StackSetは、us-east-1に集約する方が管理しやすい、との判断です。
実際にGlobalなAWSリソース用のStack/StackSetをどのリージョンに作るかは、各組織のポリシーと判断に任されると思います。

実行後、成功すると上記のようにStackSetIdが出力されます。
作成されたStackSetを確認します。

describe-stack-set — AWS CLI 1.18.5 Command Reference
https://docs.aws.amazon.com/cli/latest/reference/cloudformation/describe-stack-set.html

```:aws&nbsp;--profile&nbsp;master&nbsp;&nbsp;cloudformation&nbsp;describe-stack-set
$ aws --profile master cloudformation describe-stack-set \
  --region us-east-1 \
  --stack-set-name basic-iam-roles
### ※出力結果省略
```

上記StackSetに紐づくStack Instanceを確認します。まだ何も存在していないことが分かります。

```:aws&nbsp;--profile&nbsp;master&nbsp;&nbsp;cloudformation&nbsp;list-stack-instances
$ aws --profile master cloudformation list-stack-instances \
  --region us-east-1 \
  --stack-set-name basic-iam-roles
{
    "Summaries": []
}
```


## 子AWSアカウントにStack Instanceを作り、必要なAWSリソースを作成する
### masterアカウント上でStack Instance作成操作を実行する
masterアカウント上で、cloudformation create-stack-instancesを実行します。
指定するパラメータは、masterアカウントのものなのか、子AWSアカウントのものなのか混乱しやすいので注意が必要です。

- region: masterアカウント側の指定。どこのRegionのStackSetが操作対象となるかを指定する。今回はus-east-1で作成している。
- stack-set-name: masterアカウント側の指定。上記までに作成したStackSetの名前。
- accounts: 子AWSアカウントID。複数の子AWSアカウントIDを指定することもできる。
- regions: 子AWSアカウント側の指定。Stack InstanceをどこのRegionに作成するかを指定する。今回はus-east-1を指定する。

create-stack-instances — AWS CLI 1.18.5 Command Reference
https://docs.aws.amazon.com/cli/latest/reference/cloudformation/create-stack-instances.html

```
$ aws --profile master cloudformation create-stack-instances \
  --region us-east-1 \
  --stack-set-name basic-iam-roles \
  --accounts 987654321098 \
  --regions us-east-1

### OperationIdが出力される

{
    "OperationId": "abcd1234-aaaa-bbbb-cccc-ddddeeeeffff"
}
```

出力されたOperationIdをもとに、Stack Instanceの作成状況を確認することができます。--operation-idオプションに、先ほど出力された値を指定します。
Statusが「RUNNING」であれば、Stack Instance作成中のようです。

```:aws&nbsp;--profile&nbsp;master&nbsp;cloudformation&nbsp;describe-stack-set-operation 
$ aws --profile master cloudformation describe-stack-set-operation \
  --region us-east-1 \
  --stack-set-name basic-iam-roles \
  --operation-id abcd1234-aaaa-bbbb-cccc-ddddeeeeffff
{
    "StackSetOperation": {
        "Status": "RUNNING",
        "AdministrationRoleARN": "arn:aws:iam::123456789012:role/AWSCloudFormationStackSetAdministrationRole",
        "OperationPreferences": {
            "RegionOrder": []
        },
        "ExecutionRoleName": "OrganizationAccountAccessRole",
        "Action": "CREATE",
        "CreationTimestamp": "2020-02-24T13:42:53.125Z",
        "StackSetId": "basic-iam-roles:12345678-9999-aaaa-bbbb-ccccddddeeee",
        "OperationId": "abcd1234-aaaa-bbbb-cccc-ddddeeeeffff"
    }
}
```

Statusが「SUCCEEDED」になれば、Stack Instance作成完了です。

```:aws&nbsp;--profile&nbsp;master&nbsp;cloudformation&nbsp;describe-stack-set-operation 
$ aws --profile master cloudformation describe-stack-set-operation \
  --region us-east-1 \
  --stack-set-name basic-iam-roles \
  --operation-id abcd1234-aaaa-bbbb-cccc-ddddeeeeffff
{
    "StackSetOperation": {
        "Status": "SUCCEEDED",
        "AdministrationRoleARN": "arn:aws:iam::123456789012:role/AWSCloudFormationStackSetAdministrationRole",
        "OperationPreferences": {
            "RegionOrder": []
        },
        "ExecutionRoleName": "OrganizationAccountAccessRole",
        "Action": "CREATE",
        "CreationTimestamp": "2020-02-24T13:42:53.125Z",
        "StackSetId": "basic-iam-roles:12345678-9999-aaaa-bbbb-ccccddddeeee",
        "OperationId": "abcd1234-aaaa-bbbb-cccc-ddddeeeeffff"
    }
}
```

AWS Management ConsoleからもStackSetが作成されたことを確認できます。
![2020-02-24_22h48_24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/137777/ec6ce67d-cb66-580a-8e0a-4ef23a2a9aa5.png)


## 子AWSアカウントで、作成されたStack Instanceを確認する
### Stack Instanceの確認
子AWSアカウント上には、通常のCloudFormation Stackとして作成されます。
子AWSアカウント側でcloudformation list-stacksを実行することで確認できます。
作成されたStack Instanceは、「StackSet-[StackSet名]-[ランダムなUID]」という名称で作成されます。


```
$ aws --profile investigate-multi-accounts cloudformation list-stacks \
  --region us-east-1 \
  --query "StackSummaries[?starts_with(StackName, 'StackSet-basic-iam-roles-')]"

[
    {
        "StackId": "arn:aws:cloudformation:us-east-1:987654321098:stack/StackSet-basic-iam-roles-98765432-abcd-ef12-3456-7890abcd1234/abcd0123-9876-5432-10fe-1234abcd5678",
        "DriftInformation": {
            "StackDriftStatus": "NOT_CHECKED"
        },
        "TemplateDescription": "This template is intended to be used for CloudFormation StackSets. That enables you to create fundamental IAM roles into each target AWS accounts. Those are used for AssumeRole by IAM users in your master AWS account.\nPlease notice this template requires the \"CAPABILITY_NAMED_IAM\" capability to create a CloudFormation stack. In addition, since this template creates an IAM Role with fixed name, I recommend you to create a CFn stack in the N.Virginia(us-east-1) region.\nA sample command line with necessary parameters is below. $ aws cloudformation create-stack-set --region us-east-1 --stack-name <stack name> \\ --template-body file://<this file name> \\ --parameters ParameterKey=AdministratorAwsAccountId,ParameterValue=123456789012 \\ --capabilities CAPABILITY_NAMED_IAM \\\n",
        "CreationTime": "2020-02-24T13:43:00.500Z",
        "StackName": "StackSet-basic-iam-roles-98765432-abcd-ef12-3456-7890abcd1234",
        "StackStatus": "CREATE_COMPLETE"
    }
]
```

AWS Management Consoleからも下記のように確認できます。（下記は子AWSアカウントにSwitch Roleして確認しています。）
![2020-02-24_22h49_25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/137777/f4847f14-989f-c739-3178-5c0d88ba3175.png)


### 作成されたIAM Roleの確認
念のため、子AWSアカウント上に必要なAWSリソースが作成されていることを確認しておきます。
今回作成したのはIAM Roleが2つ、「AccountingRole」「FullAdministratorRole」となりますので、iam list-rolesで確認できます。

list-roles — AWS CLI 1.18.5 Command Reference
https://docs.aws.amazon.com/cli/latest/reference/iam/list-roles.html

```
$ aws --profile investigate-multi-accounts iam list-roles \
  --query "Roles[?contains(['AccountingRole', 'FullAdministratorRole'], RoleName)]"

[
    {
        "Description": "This role allows IAM users in the administrator account to invoke billing-related actions within the target AWS account.",
        "AssumeRolePolicyDocument": {
            "Version": "2012-10-17",
            "Statement": [
                {
                    "Action": "sts:AssumeRole",
                    "Effect": "Allow",
                    "Principal": {
                        "AWS": "arn:aws:iam::123456789012:root"
                    }
                }
            ]
        },
        "MaxSessionDuration": 3600,
        "RoleId": "AROA*****************",
        "CreateDate": "2020-02-24T13:43:05Z",
        "RoleName": "AccountingRole",
        "Path": "/",
        "Arn": "arn:aws:iam::987654321098:role/AccountingRole"
    },
    {
        "Description": "This role allows IAM users in the administrator account to perform AssumeRole to obtain the privileges of this AWS account",
        "AssumeRolePolicyDocument": {
            "Version": "2012-10-17",
            "Statement": [
                {
                    "Action": "sts:AssumeRole",
                    "Effect": "Allow",
                    "Principal": {
                        "AWS": "arn:aws:iam::123456789012:root"
                    }
                }
            ]
        },
        "MaxSessionDuration": 3600,
        "RoleId": "AROA*****************",
        "CreateDate": "2020-02-24T13:43:05Z",
        "RoleName": "FullAdministratorRole",
        "Path": "/",
        "Arn": "arn:aws:iam::987654321098:role/FullAdministratorRole"
    }
]
```

# おわりに
CloudFormation StackSetを利用して、子となるAWSアカウント上に任意のCloudFormation Stackを作成することができました。
AWS CLI実行時に指定するパラメータがどのAWSアカウントに対するものなかが分かりにくかったり、masterアカウント側の準備が少し面倒だったりしますが、上手く使えば複数AWSアカウントの管理作業を大幅に軽減することができます。