複数アカウントへのスイッチロールを1つの許可セットで制御する

1つのログインアカウントから複数の作業対象アカウントへスイッチロールを許可するための許可セットについて、ベストプラクティスを模索する。

<!-- ================================================= -->
# AWS Identity CenterのAPIの問題
<!-- ================================================= -->
Identity Centerの許可セットのAPIは、**ec2:DescribeInstances**のAPIのように一回のAPI実行で許可セットの属性情報が取得できない。具体的には、許可セットのARN一覧を取得(**sso:ListPermissionSets**)した後、許可セット毎に許可セットの属性情報を別のAPIで取得(**sso:DescribePermissionSet**)している。

これは、AWS管理コンソールの**IAM Identity Center** > 許可セットの画面で、許可セットの数だけAPIの呼び出しがブラウザの裏側で行われていることを示す。
そのため、許可セットの数が30-40程度であればさほど問題は発生しないが、300を超えたあたりから急激に応答が遅くなるし、目的の許可セットを許可セット名で絞り込み出来ないのもこのAPIの仕様のためと推測される。
(APIの仕様上**sso:ListPermissionSets**では許可セットの名称が分からないため、結局全ての許可セットの情報を1件1件**sso:DescribePermissionSet**で取得することになる)

参考:[sso:ListPermissionSets API](https://docs.aws.amazon.com/ja_jp/singlesignon/latest/APIReference/API_ListPermissionSets.html)

```json:ListPermissionSetsのResponse
{
   "NextToken": "string",
   "PermissionSets": [ "string" ]
}
```

参考:[sso:DescribePermissionSet API](https://docs.aws.amazon.com/ja_jp/singlesignon/latest/APIReference/API_DescribePermissionSet.html)

```json:DescribePermissionSetのResponse
{
   "PermissionSet": { 
      "CreatedDate": number,
      "Description": "string",
      "Name": "string",
      "PermissionSetArn": "string",
      "RelayState": "string",
      "SessionDuration": "string"
   }
}
```

<!-- ================================================= -->
# 各AWSアカウントの管理者が異なる場合
<!-- ================================================= -->
ログインアカウントから各アカウントへスイッチロールすることを考える。
以下のように、各アカウント上には管理するチームおよびユーザーが異なるが、役割自体はチーム間で同一の考え方を用い、各アカウントには同名のIAMロールが存在するものとする(つまり、ユーザーはチームの兼任はしない)

アカウントA(管理チームA)
|No | IAMロール     | 担当するユーザー  |
|--:|:--------------|:-----------------|
|1  |Administrator  |担当A1            |
|2  |PowerUser      |担当A2            |
|3  |Readonly       |担当A3            |

アカウントB(管理チームB)
|No | IAMロール     | 担当するユーザー  |
|--:|:--------------|:-----------------|
|1  |Administrator  |担当B1            |
|2  |PowerUser      |担当B2            |
|3  |Readonly       |担当B3            |

アカウントC(管理チームC)
|No | IAMロール     | 担当するユーザー  |
|--:|:--------------|:-----------------|
|1  |Administrator  |担当C1            |
|2  |PowerUser      |担当C2            |
|3  |Readonly       |担当C3            |

このとき、担当A1-A3はアカウントAにのみスイッチロール可能とし、アカウントB,Cにはスイッチロール不可、アカウントB,Cについても同様としたい。
とすると、そのままその要件を満たす許可セットは以下のようになる。

|No | 許可セット       | 担当するユーザー |
|--:|:----------------|:-----------------|
|1  |A_Administrator  |担当A1            |
|2  |A_PowerUser      |担当A2            |
|3  |A_Readonly       |担当A3            |
|4  |B_Administrator  |担当B1            |
|5  |B_PowerUser      |担当B2            |
|6  |B_Readonly       |担当B3            |
|7  |C_Administrator  |担当C1            |
|8  |C_PowerUser      |担当C2            |
|9  |C_Readonly       |担当C3            |

すると今後アカウントが追加された場合、許可セットも追加されることになるため汎用性がなく、また前述したように許可セットの数が多くなるとAWS管理コンソール上での操作が困難になるので推奨されない。

<!-- ================================================= -->
# 解決策
<!-- ================================================= -->
これを解決するには、許可セット側のポリシーではなくスイッチロール先の**IAM**ロールの信頼ポリシーで制御する。
手順としては以下となる。

1. 各アカウント共通となる許可セットを作成する。
 上記の例であれば、Administrator、PowerUser、Readonlyの3つを作成し、**sts:AssumeRole**の許可ポリシーを記載する。この許可ポリシーはIAMロールのARNを**Resource**に記載することになるが、このときARNのアカウントに該当する部分を"*"のアスタリスクにしておく。
1. この許可セットについてログインアカウントにプロビジョニングする。
 適当に選定したユーザー1人に割り当てを行わないとログインアカウント上にプロビジョニングできないので、テスト的な1人を選定してプロビジョニングする。
1. 一度プロビジョニングすると、ログインアカウント内にIdentity Centerにて自動作成された**IAM**ロールが存在する。ロール名は"AWSReservedSSO_<許可セット名>_<ランダム十六進数16桁>"の形になっている。
1. 上記"AWSReservedSSO_*"の**IAM**ロールのロールIDを取得する。
 ロールIDの取得にはAWS CLIのコマンドを利用する。
1. 各アカウント側にスイッチロール対象となる**IAM**ロールを作成する。
 各**IAM**ロールの権限は各アカウント内で決定すれば良い(他のアカウントと細部が異なっていても問題はない)が、信頼ポリシーについてCondition句にスイッチロール元のロールIDと、使用を許可したいユーザーを識別する情報を設定する(下記参照)

```json:許可セットの許可ポリシー(下記はAdministratorの役割のIAMロール名をCommon_Administratorとしている)
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "sts:AssumeRole"
            ],
            "Resource": [
                "arn:aws:iam::*:role/Common_Administrator"
            ]
        }
    ]
}
```

```json:アカウントAのCommon_Administratorロールの信頼ポリシー
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::<ログインアカウント>:root"
      },
      "Action": "sts:AssumeRole",
      "Condition": {
        "StringEquals": {
          "aws:userid": [
            "<RoleId>:<UserName>"
          ]
        }
      }
    }
  ]
}
```

```shell:ロールIDを取得するAWS CLI
# aws iam get-role --role-name ロール名 | jq -r .Role.RoleId
```

信頼ポリシーの**RoleId**にはAWS CLIコマンドで取得したロールID、**UserName**にはIdentity Center上のユーザー名を指定する。
こうすることで、アカウントA上の**IAM**ロールはアカウントAの管理チームのユーザー、アカウントB上の**IAM**ロールはアカウントBの管理チームのユーザーのみが信頼ポリシーによってスイッチロールすることが可能となる。
また、許可セットのスイッチロールの許可ポリシーでスイッチロール先のロールARNでアカウント部分をアスタリスクにしておくことで、今後アカウントが追加されても許可セットの許可ポリシーを修正する必要がなくなるという利点がある。

<!-- ================================================= -->
# 留意事項
<!-- ================================================= -->
Identity Center上のユーザー名であるが、IDプールが**Active Directory**の場合、デフォルトでは**Active Directory**上の**userprincipalname**になっているため、**username@example.com**形式であることに注意する必要がある。