【AWS Organization】メンバーアカウントのルートユーザー認証情報が存在しているかを一覧する

どのアカウントにルートユーザー認証情報が存在しているか一覧を取得して、スプシやスクリプトで処理したくなるけど、マネコンだと取れないし、ググってもよくわかんな～い
という訳で考えてみた、という記事です

→　[本編はこちら](#リストするサンプルコード)

# 前提
AWS Organization マスターアカウントでマネジメントコンソールへログインし、[IAMコンソールの「アカウント設定」](https://us-east-1.console.aws.amazon.com/iam/home?region=ap-northeast-1#/account_settings)で「メンバーアカウントのための一元化されたルートアクセス」が有効であること。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653543/0fe8336b-7fff-4877-befd-a35d55574339.png)

あと、本記事に記載のAWS CLIコマンドは、AWS CLIバージョンが古いと出来ないようです。（AWS CLI 2.15.4 on Windowsだと出来なくて、2.24.13 on Windowsだと出来ることは確認できています）

# マネコンで見れる
[IAMコンソールの「ルートアクセス管理」](https://us-east-1.console.aws.amazon.com/iam/home?region=ap-northeast-1#/organizations/root-access-management)で、添付画像のような形で見ることができる。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653543/50ce5522-185e-4db8-aac7-073b78a9b53f.png)

アカウントごとに、ルートユーザーアクセスキーおよびコンソールパスワードの存在有無の一覧が欲しくても、いちいちクリックして転記しなければならないのか。

# AWS CLIでがんばる
以下のAWS CLIコマンド①～②をスクリプト等でメンバーアカウントごとにループ処理する；

①AWS Organization マスターアカウントでは、ルートユーザー認証情報の存在有無にかかわらずメンバーアカウントのルートユーザー認証情報を取得可能
```shell
$ aws sts assume-root --target-principal <member account id> --task-policy-arn "arn=arn:aws:iam::aws:policy/root-task/IAMAuditRootUserCredentials" --profile <cli profile name of master account>
{
    "Credentials": {
        "AccessKeyId": "*******",
        "SecretAccessKey": "*******",
        "SessionToken": "*******",
        "Expiration": "*******"
    }
}
```
※注意：メンバーアカウントの一時的認証情報が帰ってきているが、これをもって当該アカウントにルートユーザーアクセスキーが存在すると見なすことは出来ない
※注釈：ここで取れる認証情報はルートユーザーの一時的認証情報ではあるものの、`--task-policy-arn`で指定されたポリシーに記載のオペレーションしか出来ない。しかしながら、ルートユーザーにしか出来ない操作が出来る認証情報ではあるため、ルートユーザーアクセスキーと同じくらい厳重に管理すべき

②ルートユーザーの認証情報／MFAデバイス／コンソールパスワード、およびデジタル署名用証明書の存在を確認する

```shell
aws iam get-account-summary --profile <member account root user profile name by above temporary credential>
{
    "SummaryMap": {
        "GroupPolicySizeQuota": 5120,
        "InstanceProfilesQuota": 1000,
        "Policies": 10,
        "GroupsPerUserQuota": 10,
        "InstanceProfiles": 0,
        "AttachedPoliciesPerUserQuota": 10,
        "Users": 2,
        "PoliciesQuota": 1500,
        "Providers": 1,
        "AccountMFAEnabled": 1,
        "AccessKeysPerUserQuota": 2,
        "AssumeRolePolicySizeQuota": 2048,
        "PolicyVersionsInUseQuota": 10000,
        "GlobalEndpointTokenVersion": 1,
        "VersionsPerPolicyQuota": 5,
        "AttachedPoliciesPerGroupQuota": 10,
        "PolicySizeQuota": 6144,
        "Groups": 1,
        "AccountSigningCertificatesPresent": 0,
        "UsersQuota": 5000,
        "ServerCertificatesQuota": 20,
        "MFADevices": 3,
        "UserPolicySizeQuota": 2048,
        "PolicyVersionsInUse": 49,
        "ServerCertificates": 0,
        "Roles": 48,
        "RolesQuota": 1000,
        "SigningCertificatesPerUserQuota": 2,
        "MFADevicesInUse": 3,
        "RolePolicySizeQuota": 10240,
        "AttachedPoliciesPerRoleQuota": 10,
        "AccountAccessKeysPresent": 0,
        "AccountPasswordPresent": 1,
        "GroupsQuota": 300
    }
}
```

`AccountMFAEnabled`：1ならばルートユーザーのMFAが有効である
`AccountAccessKeysPresent`：1ならばルートユーザーのアクセスキーが存在している
`AccountPasswordPresent`：1ならばルートユーザーのコンソールサインイン用パスワードが存在している
`AccountSigningCertificatesPresent`：1ならばデジタル署名用証明書が存在している

※注意：ルートユーザーアクセスキーが無効化されていても、存在していれば`AccountAccessKeysPresent`は1になる

# リストするサンプルコード

```Python
# pip install boto3
# pip install -U boto3

import boto3

ORGANIZATION_MASTER_ACCOUNT_ID = '<enter your organization master account id>'

_session = boto3.Session(profile_name='organization master account')
_orgclient = _session.client('organizations')
_stsclient = _session.client('sts', region_name='us-east-1', endpoint_url="https://sts.us-east-1.amazonaws.com")

def list_organizations_accounts(NextToken=None):
    response = _orgclient.list_accounts(NextToken=NextToken) if NextToken else _orgclient.list_accounts()
    accounts =response['Accounts']
    if 'NextToken' in response:
        accounts.extend(list_organizations_accounts(response['NextToken']))
    return accounts

with open('member_account_rootuser_credential_summary.tsv', 'w') as f:
  f.write(f'accountId\taccountName\tAccountMFAEnabled\tAccountAccessKeysPresent\tAccountPasswordPresent\tAccountSigningCertificatesPresent\n')

for account in list_organizations_accounts():
    accountId = account['Id']
    accountName = account['Name']
    if accountId == ORGANIZATION_MASTER_ACCOUNT_ID:
       continue
    print(f'Account ID: {accountId}, Account Name: {accountName}')

    rootUserCredential = _stsclient.assume_root(
      TargetPrincipal=accountId,
      TaskPolicyArn={
          'arn': 'arn:aws:iam::aws:policy/root-task/IAMAuditRootUserCredentials'
      },
      DurationSeconds=123
    )
    # {'Credentials': {'AccessKeyId': '***', 'SecretAccessKey': '***', 'SessionToken': '***'}}

    iam_client = boto3.Session(
      aws_access_key_id=rootUserCredential['Credentials']['AccessKeyId'],
      aws_secret_access_key=rootUserCredential['Credentials']['SecretAccessKey'],
      aws_session_token=rootUserCredential['Credentials']['SessionToken'],
      region_name='us-east-1'
    ).client('iam')

    account_summary = iam_client.get_account_summary()
    summaryMap = account_summary['SummaryMap']

    AccountMFAEnabled = summaryMap['AccountMFAEnabled']
    AccountAccessKeysPresent = summaryMap['AccountAccessKeysPresent']
    AccountPasswordPresent = summaryMap['AccountPasswordPresent']
    AccountSigningCertificatesPresent = summaryMap['AccountSigningCertificatesPresent']

    print(f'AccountMFAEnabled: {AccountMFAEnabled}, AccountAccessKeysPresent: {AccountAccessKeysPresent}, AccountPasswordPresent: {AccountPasswordPresent}, AccountSigningCertificatesPresent: {AccountSigningCertificatesPresent}')

    with open('member_account_rootuser_credential_summary.tsv', 'a') as f:
      f.write(f'{accountId}\t{accountName}\t{AccountMFAEnabled}\t{AccountAccessKeysPresent}\t{AccountPasswordPresent}\t{AccountSigningCertificatesPresent}\n')

```

### boto3でやる時の注意点

2025年2月27日現在、STSのグローバルエンドポイントではAssumeRoleオペレーションに対応していないので、APIからエラーが返ってくる。

なので、以下のようにboto3 clientオブジェクトを生成する際にリージョンとエンドポイントURLを指定する必要がある。

```python
_stsclient = _session.client('sts', region_name='us-east-1', endpoint_url="https://sts.us-east-1.amazonaws.com")
```

# 結果
以下のような、アカウントIDごとにアカウント名と、ルートユーザー認証情報およびコンソールサインインパスワードの存在有無、ルートユーザーMFA、デジタル署名用証明書の有無がのったTSVファイルが得られる。

```text:member_account_rootuser_credential_summary.tsv
accountId	accountName	AccountMFAEnabled	AccountAccessKeysPresent	AccountPasswordPresent	AccountSigningCertificatesPresent
****	****	0	0	1	0
****	****	0	0	1	0
****	****	0	0	1	0
****	****	0	0	0	0
```

# 
ちなみに わたしは ねこちゃん 大好きマンです