AWS SSOにAPIが追加されてCLIやCloudFormationで操作できるようになったよ

## はじめに
2020/9/10 ついに AWS Single Sign-On に [AWS SSO API](https://docs.aws.amazon.com/singlesignon/latest/APIReference/index.html) (sso-admin) が追加され、
AWS CLI/SDK や CloudFormation による操作もサポートされました。 

:rocket: **AWS Single Sign-On adds account assignment APIs and AWS CloudFormation support to automate multi-account access management**
https://aws.amazon.com/jp/about-aws/whats-new/2020/09/aws-single-sign-on-adds-account-assignment-apis-and-aws-cloudformation-support-to-automate-multi-account-access-management/

これまで 権限セットや AWS アカウントへのユーザー/グループの割り当てといった
AWS SSO の管理操作は API が提供されておらず、コンソールで手動設定するしかありませんでした。
今回のアップデートにより、アカウント割り当て設定の自動化や IaC への道がひらけました！！

## CloudFormation のサポート
2020年9月時点で CloiudFormation でサポートされているリソースは以下の2つです

* [`AWS::SSO::PermissionSet`](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-sso-permissionset.html) 
AWS SSO インスタンスに設定する権限を指定します。

* [`AWS::SSO::Assignment`](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-sso-assignment.html)
指定された権限セットを使用して、AWS アカウントへのアクセスを割り当てます。

### 注意点
CloudFormation で AWS SSOリソースを作成するには、InstanceArn, Identiy-Store-Id, 
UserId, GroupId などのさまざまな リソース ID が必要です。
これらの ID は 一部を除いて AWS SSO コンソールに表示されないため、API を介して取得する必要があります。

ただし PermissionSetArn や UserId/GroupId は各詳細ページの URL にも含まれていて
ps-xxxxxxxxxxxxxxxx といったリソースID や GUID を確認することもできます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/125105/536d2631-9bd2-5fc1-a447-1e89479026fa.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/125105/adf6e6eb-c46c-b9da-89d7-3be331d9e0bc.png)

2020/10/1 追記
AWS SSO コンソールのユーザー詳細画面で User ID が確認できるようになっていました。
(コンソールの言語設定を英語で確認しています）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/125105/183db44c-91a3-5935-3917-6ac3f5cfa919.png)
　

以降では AWS CLI で 各リソース ID を取得する方法も合わせて紹介します。
AWS SSO API (sso-admin) を使用するには、Version 1.18.136 または 2.0.48 以降が必要です。

### 権限セットの指定例
AWS 管理ポリシーを使用したシンプルな権限セットを作成する例です。

```yaml
PermissionSet:
  Type: AWS::SSO::PermissionSet
  Properties:
    InstanceArn: 'arn:aws:sso:::instance/ssoins-xxxxxxxxxxxxxxxx'
    Name: 'AdministratorAccess'
    ManagedPolicies:
    - 'arn:aws:iam::aws:policy/AdministratorAccess'
```

`InstanceArn` には SSO インスタンスの ARN を指定します。
sso-admin の list-instances から取得します。

```shell-session
$ aws sso-admin list-instances
{
    "Instances": [
        {
            "InstanceArn": "arn:aws:sso:::instance/ssoins-xxxxxxxxxxxxxxxx",
            "IdentityStoreId": "d-xxxxxxxxxx"
        }
    ]
}
```

### アカウント割り当ての指定例
以下のように AWS アカウント、権限セット、プリンシパルを指定します。

```yaml
Assignment: 
  Type: AWS::SSO::Assignment
  Properties: 
    InstanceArn: 'arn:aws:sso:::instance/ssoins-xxxxxxxxxxxxxxxx'
    PermissionSetArn: 'arn:aws:sso:::permissionSet/ssoins-xxxxxxxxxxxxxxxx/ps-xxxxxxxxxxxxxxxx'
    TargetId: '123456789012'
    TargetType: AWS_ACCOUNT
    PrincipalId: 'f81d4fae-7dec-11d0-a765-00a0c91e6bf6'
    PrincipalType: 'GROUP'
```

PermissionSetArn は sso-admin の describe-permission-set で確認します。

```shell-session
$ aws sso-admin list-permission-sets \
> --instance-arn arn:aws:sso:::instance/ssoins-xxxxxxxxxxxxxxxx 
{
    "PermissionSets": [
        "arn:aws:sso:::permissionSet/ssoins-xxxxxxxxxxxxxxxx/ps-xxxxxxxxxxxxxxxx",
        "arn:aws:sso:::permissionSet/ssoins-xxxxxxxxxxxxxxxx/ps-yyyyyyyyyyyyyyyy",
        "arn:aws:sso:::permissionSet/ssoins-xxxxxxxxxxxxxxxx/ps-zzzzzzzzzzzzzzzz"
    ]
}

$ aws sso-admin describe-permission-set \
> --instance-arn arn:aws:sso:::instance/ssoins-xxxxxxxxxxxxxxx \
> --permission-set-arn arn:aws:sso:::permissionSet/ssoins-xxxxxxxxxxxxxxx/ps-xxxxxxxxxxxxxxx
{
    "PermissionSet": {
        "Name": "AdministratorAccess",
        "PermissionSetArn": "arn:aws:sso:::permissionSet/ssoins-xxxxxxxxxxxxxxx/ps-xxxxxxxxxxxxxxx",
        "CreatedDate": "2020-09-09T19:01:06.758000+09:00",
        "SessionDuration": "PT1H"
    }
}
```

CloudFormation で権限セットを作成しているのであれば
組み込み関数の Fn::GetAtt で ARN を参照するのが一番簡単です。

```yaml
    PermissionSetArn: !GetAtt LogicalId.PermissionSetArn
```

TargetType は `AWS_ACCOUNT` のみが指定可能で
TargetId には割り当てを行う AWS アカウントIDを指定します。

```yaml
    TargetId: '123456789012'
    TargetType: AWS_ACCOUNT
```

PrincipalType には `USER` または `GROUP` を指定できます。
PrincipalId には割り当てを行うユーザー/グループの `GUID` を指定する必要があります。

```yaml
    PrincipalId: 'f81d4fae-7dec-11d0-a765-00a0c91e6bf6'
    PrincipalType: 'GROUP'
```

AWS SSO API (sso-admin) の多くのオペレーションは、プリンシパルと呼ばれる
ユーザーとグループの識別子に依存しています。
ユーザー/グループの GUID を取得するには [AWS SSO Identity Store API](https://docs.aws.amazon.com/singlesignon/latest/IdentityStoreAPIReference/welcome.html) (identitystore) の
list-users, list-groups を使用します。
`--identity-store-id` には 先ほど sso-admin の list-instances で確認した
ID ストアの識別子 (`d-xxxxxxxxxx`) を指定する必要があります。

list-users では `UserName`、list-group では `DisplayName` をフィルターに指定することができます。

```shell-session
$ aws identitystore list-users \
> --identity-store-id d-xxxxxxxxxx \
> --filters AttributePath=UserName,AttributeValue="user@example.com"
{
    "Users": [
        {
            "UserName": "userXX@examle.com",
            "UserId": "f81d4faxge-7dec11d8-a765-3at5-80e4-00a0c91e6bf6"
        }
    ]
}

$ aws identitystore list-groups \
> --identity-store-id d-xxxxxxxxxx \
> --filters AttributePath=DisplayName,AttributeValue="テストグループ"
{
    "Groups": [
        {
            "GroupId": "f81d4faxge-789fcfa5-005c-4379-89ba-10a11e641c17"
            "DisplayName": "テストグループ"
        }
    ]
}
```

### テンプレート例
上記を踏まえて作成した、権限セットおよびそれを使用したアカウント割り当てを行う
CloudFormation のテンプレート例です。
ユーザーやグループの GUID やアカウント ID については複数の割り当てが発生するため
マッピングを使用して値を参照するようにしてみましたが、もっとよいやり方がありそうな気がします。

```yaml
AWSTemplateFormatVersion: "2010-09-09"
Description: Configure AWS SSO Account Assignment

Parameters: 
  pAwsSsoInsanceArn: 
    Type: String
    Default: 'arn:aws:sso:::instance/ssoins-xxxxxxxxxxxxxxx'
    Description: 'No change required.'

Mappings: 
  mUsers:
    User1:
      Id: 'f81d4faxge-7dec11d8-a765-3at5-80e4-00a0c91e6bf6'
  mGroups:
    Group1:
      Id: 'f81d4faxge-60b1e9ac-edda-4c9a-a4fd-5f26197afb79'
    Group2:
      Id: 'f81d4faxge-3a2f9d97-8e37-4e3d-9d46-3cf701a00296'
  mAccounts:
    Account1:
      Id: '111111111111'
    Account2:
      Id: '222222222222'
    Account3:
      Id: '333333333333'

Resources:
# Permission Set
  rPermissionSetAdministratorAccess:
    Type: AWS::SSO::PermissionSet
    Properties:
      InstanceArn: !Ref pAwsSsoInsanceArn
      Name: 'AdministratorAccess'
      ManagedPolicies:
        - 'arn:aws:iam::aws:policy/AdministratorAccess'

  rPermissionSetSecurityAudit:
    Type: AWS::SSO::PermissionSet
    Properties:
      InstanceArn: !Ref pAwsSsoInsanceArn
      Name: 'SecurityAudit'
      ManagedPolicies:
        - 'arn:aws:iam::aws:policy/SecurityAudit'

  rPermissionSetViewOnlyAccess:
    Type: AWS::SSO::PermissionSet
    Properties:
      InstanceArn: !Ref pAwsSsoInsanceArn
      Name: 'ViewOnlyAccess'
      ManagedPolicies:
        - 'arn:aws:iam::aws:policy/job-function/ViewOnlyAccess'

# Account Assignment
  rAccount1Group1AdministratorAccess:
    Type: AWS::SSO::Assignment
    Properties: 
      InstanceArn: !Ref pAwsSsoInsanceArn
      PermissionSetArn: !GetAtt rPermissionSetAdministratorAccess.PermissionSetArn
      PrincipalId: !FindInMap [ mGroups, Group1, Id ]
      PrincipalType: 'GROUP'
      TargetId: !FindInMap [ mAccounts, Account1, Id ]
      TargetType: 'AWS_ACCOUNT'

  rAccount2Group1ViewOnlyAccess:
    Type: AWS::SSO::Assignment
    Properties: 
      InstanceArn: !Ref pAwsSsoInsanceArn
      PermissionSetArn: !GetAtt rPermissionSetViewOnlyAccess.PermissionSetArn
      PrincipalId: !FindInMap [ mGroups, Group1, Id ]
      PrincipalType: 'GROUP'
      TargetId: !FindInMap [ mAccounts, Account2, Id ]
      TargetType: 'AWS_ACCOUNT'

  rAccount3User1AdministratorAccess:
    Type: AWS::SSO::Assignment
    Properties: 
      InstanceArn: !Ref pAwsSsoInsanceArn
      PermissionSetArn: !GetAtt rPermissionSetAdministratorAccess.PermissionSetArn
      PrincipalId: !FindInMap [ mUsers, User1, Id ]
      PrincipalType: 'USER'
      TargetId: !FindInMap [ mAccounts, Account3, Id ]
      TargetType: 'AWS_ACCOUNT'

  rAccount1Group2SecurityAudit:
    Type: AWS::SSO::Assignment
    Properties: 
      InstanceArn: !Ref pAwsSsoInsanceArn
      PermissionSetArn: !GetAtt rPermissionSetSecurityAudit.PermissionSetArn
      PrincipalId: !FindInMap [ mGroups, Group2, Id ]
      PrincipalType: 'GROUP'
      TargetId: !FindInMap [ mAccounts, Account1, Id ]
      TargetType: 'AWS_ACCOUNT'

```

## まとめ
* AWS SSO API (sso-admin) が追加され、管理作業の一部自動化や IaC が可能になった
* CloudFormation では権限セットの作成とアカウント割り当てをサポート
* プリンシパルの指定に必要なユーザー/グループの ID は IdentityStore API で取得する必要がある
    * アカウント割り当てを行う際に ID の検索が必要なため今のところ完全自動化は難易度が高そう
    * AWS SSO のコンソールではこれらの ID や ARN が表示されないため少々不便
* とはいえ、AWS SSO を API で管理できるのはとてもエキサイティング！
  
## 参考
**AWS CloudFormation User Guide - SSO resource type reference**
https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/AWS_SSO.html
**AWS CLI Command Reference - sso-admin**
https://awscli.amazonaws.com/v2/documentation/api/latest/reference/sso-admin/index.html
**AWS CLI Command Reference - identitystore**
https://awscli.amazonaws.com/v2/documentation/api/latest/reference/identitystore/index.html

以上です。
参考になれば幸いです。