「SaaS管理SaaS」でAWS IAMを棚卸しするために数十個のAWSアカウントに一撃でIAM Roleを作ってみた

# 前置き

この記事は[株式会社ビットキー 情シス Advent Calendar 2023](https://qiita.com/advent-calendar/2023/bitkey-corp-eng) の5日目の投稿です。

はじめまして。株式会社ビットキーの日浦です。
当社の情シスは少数精鋭なため、同じ人が何回も投稿する場合もあります。頑張ります。

ビットキーでは業務シーンで多くのSaaSを利用しており、最近は管理コストの削減や管理精度の向上のために最近流行っている「SaaS管理SaaS」の検討を行っています。

期待する効果としては
- **SaaS上のアカウントの可視化・棚卸し**
    - 一箇所で見えることは大事。
- **SaaSだけじゃなくてIaaSやPaaSも対象にしたい**
    - 要するにAWSやGCPのIAMですね。結論、大体のSaaSで対応可能

どのツールも低コストでアカウントの管理に関わる問題を解消できそうで、その有効性を実感しています。

しかし、私が試しに触らせていただいたところ、弊社の環境ではどのツールでも共通して**AWS IAMの管理のための連携時にプラスαの作業が必要だった**ため、備忘も兼ねて、また他の情シスの人の助けになればと思いここに書いておきます。

# SaaS管理SaaSとは

本題の前に「SaaS管理SaaS」という名前が一般的な呼称かどうかは置いといて、私が観測した機能とメリットを記載してみます

- **SaaS上のアカウントが可視化できる**
    - 多くのSaaSを一箇所で一覧で見れることは重要です（もうスプシにCSVを貼り付けなくて良い！）
    - なんなら「SaaS管理SaaS」上から連携先SaaSのアカウントの削除ができるものもあります
- **SaaS上のアカウントのコストが可視化できる**
    - 「1ライセンスいくら」で契約しているSaaSの金額まで見えるので、無駄なアカウントを削除するモチベーションになります
    - ぶっちゃけ「1ライセンス¥500/月」とかだと削除作業を後回しにしたくなるが、退職時のOpsと紐づくので漏れがなくなる
- **SaaS上に「従業員マスタ」を持てる**
    - 1従業員あたり月どれぐらいSaaSの費用が発生しているかがわかる
    - 新しいSaaSの必要性を経営陣に説明するとき、この観点求められますよね？

上記が代表的な機能ですが、ベンダーによっては下記のような機能もあります

- **デバイス管理機能**
    - 従業員の持っている端末を管理できる
        - 無形資産（SaaSのライセンス）と有形資産（端末）を「従業員」に紐づけて同じプラットフォームで管理できるのはアツい
    - MDMと連携していたりする。
- **セキュリティチェック機能**
    - SaaSを導入したい！となったとき困るやつですね。外部で保証してくれるなら安心
- **シャドーITの検知**
    - Chromeなどブラウザ拡張機能で収集してくるものが一般的です
    - Google Workspaceの監査ログを取り込んでGoogle LoginしたSaaSを引っ張ってくるものもあります
- **決済情報からシャドーITを検知**
    - クレジットカードの明細上だとなんのSaaSかわからないことも多いため、経理と一緒に紐解く作業をしている情シスも多いのではないでしょうか

# 「SaaS管理SaaS」を使えばAWSの管理上何が嬉しいのか

本題です。「SaaS管理SaaS」でAWSを連携すると何が嬉しいのか。

SaaSごとに若干の差はあれど、大体の製品で共通している機能は下記になります

1. **AWSアカウント上に作成されているIAMユーザーやIAM Roleの可視化**
2. **最終利用日も見れたりする**
3. **SaaS管理SaaS上でIAMユーザーやRoleを「管理責任を負う従業員」に紐づけることができる**
4. **SaaS管理SaaS上で他のSaaSのアカウントと並べて「企業内で保有しているアカウントの一覧」として可視化できる**
5. **SaaS管理SaaS上から、AWS上のIAMユーザーやIAM Roleを削除できる**

AWS管理の文脈だけだと

- AWSの管理コンソールを直接見たら済む話
- そもそも各AWSアカウント上のリソースをTerraformで管理してたら不要なのでは

と言った激烈なマサカリが飛んできそうではありますが、

- AWSは開発組織が管理してるから情シスは管理に携わっていない
    - 当然「開発組織がちゃんとやっていること」は信じているけど、統制上観測しておきたいとかありそう
- AWSアカウント数が多くて複数のアカウントを横断して運用するのがしんどい
    - ここをエンジニアリングで解決している事例は無数にあるし、当社もわりと頑張ってはいる

といった課題がある場合は有効かもしれません。

## PoCの中で見えた課題

ビットキーではAWSについてはAWS Organizationsの機能を有効化しており、各環境へのログインはIDaaSであるOktaからSSOする運用になっています。

「SaaS管理SaaS」などの外部のシステムをAWSと連携する方法は、下記の2種類が代表的かと思います。

- AWS上でIAM ユーザーを作成し、 `アクセスキーID` と `シークレットアクセスキー` をSaaS側に登録する
- AWS上でIAM Roleを作成し、 `信頼されたエンティティ` と `外部ID` を利用してSaaS側にARNを登録する

弊社ではAWSアカウント数を数十個利用していたため、**すべてのAWSアカウント上にIAMユーザー or IAM Roleを作成する必要**がありました。

「めんどくせー」と思って社内のSREに相談したところ、

> AWS OrganizationとCFn StackSetsでアカウントまとめてプロビジョニングできますよ
> 

と教えてもらったので試しにやってみました。

## **AWS CloudFormation StackSets と AWS Organizations**

まず、**AWS CloudFormation StackSets**は複数のAWSアカウント、AWSリージョンにスタック（AWSリソース）の作成・更新・削除ができる仕組みです。

通常であれば、リソースを作成する親となるアカウントと、デプロイ対象となるアカウントそれぞれにロールを作成する必要があり、要するに今回解決したい問題と全く同じことが起こるのですが、**AWS Organizations を有効化していると、Organizationsの親アカウントから子（メンバー）アカウントに対してはわざわざロールを作成する必要なく、デプロイができます。**

これにより、「SaaS管理SaaS」に連携するために必要なIAMユーザー（or IAM Role）を一撃ですべてのAWSアカウントに作成できることになります。

https://docs.aws.amazon.com/ja_jp/organizations/latest/userguide/services-that-can-integrate-cloudformation.html

## やってみた

`SaaS_IAM_Role` というRoleを各AWSアカウントに作成する流れを記載します。

今回作成されるリソースは下記です

- AWS Role名：`SaaS_IAM_Role`
- アタッチするポリシー名：`SaaS_IAM_Polocy`
- ポリシーの内容： `iam:ListUsers` のみ
- `信頼されたエンティティ` と `外部ID` も同一のもので作成する

### 1. 管理アカウントでOrganizationsのCloudFormation StackSetsを有効化します

管理アカウント（Organizationsの親アカウント）にログインし
AWS Organizations > サービス > CloudFormation StackSets を開き
「信頼されたアクセスを有効にする」をクリックします

![スクリーンショット 2023-12-02 22.22.25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545278/7310cd74-dc48-9e3e-64bf-dc8e48c59d40.png)

### 2. StackSetを作成します

管理アカウントでCloudformation > StackSetsを開き、「StackSetの作成」をクリックします。

今回は下記のYAMLをGUIから直接アップロードすることとします。

```yaml
AWSTemplateFormatVersion: '2010-09-09'
Description: Sample Role&Polcy for CloudFormation StackSets.

Parameters:
  # 「信頼されたエンティティ」が必要な場合は利用する
  PrincipalAwsAccountId:
    Type: Number
    Default: 012345678901

  # 「外部ID」が必要な場合は利用する
  ExternalId:
    Type: String
    Default: xxxxxxxxxxxxxxxxxxxx

Resources:
  SaaSIAMRole:
    Type: AWS::IAM::Role
    Properties: 
      RoleName: SaaS_IAM_Role
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: "Allow"
            Principal:
              AWS: !Sub arn:aws:iam::${PrincipalAwsAccountId}:root
            Action: "sts:AssumeRole"
            Condition:
              StringEquals:
                'sts:ExternalId': !Sub ${ExternalId}
      Policies: 
        - PolicyName: AllowAllActions
          PolicyDocument:
            Version: "2012-10-17"
            Statement:
              - Effect: "Allow"
                Action: # 連携に必要な権限を記載
                  - "iam:ListUsers"
                Resource: "*"
Outputs:
  Arn:
    Value: !GetAtt SaaSIAMRole.Arn
```
![スクリーンショット 2023-12-02 22.42.37.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545278/511e249c-045d-f5af-02d9-fd3fde1aa634.png)

![スクリーンショット 2023-12-02 22.51.23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545278/dcb6a9a8-7dd5-4600-57f4-1d578f1405fa.png)

![スクリーンショット 2023-12-02 22.52.13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545278/3aea0699-d89c-5f3b-1738-e8b02363cfb3.png)

![スクリーンショット 2023-12-02 22.56.27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545278/1cf3e0f9-83e1-6be6-0f2c-f9bf98a72b67.png)

AWS OU IDにはAWS Organizationsの画面で確認できる、親アカウントのOU-IDを入力してください

![スクリーンショット 2023-12-02 22.58.10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545278/b7c38864-95d5-91a3-f5eb-9a19079b62d1.png)

「自動デプロイ」を有効化することで、今後OrganizationsにAWSアカウントが追加で作成された際も勝手に今回作成するIAM Roleを作成してくれるようになります。

![スクリーンショット 2023-12-02 22.59.40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545278/48b77572-c27f-afca-ca12-27a309d24418.png)


設定内容を確認し、「送信」をクリックします。

「スタックインスタンス」ですべてのアカウントのステータスが**SUCCEEDED**になるのを見届けたら完了です。

SaaS管理SaaSに登録する際、ARNを求められるかと思いますが、今回各アカウント上に作成されるAWS Roleは

`arn:aws:iam::${ACCOUNT_NUMBER}:role/SaaS_IAM_Role` 

となっているため、アカウントナンバーを適宜変換することで各AWSの管理画面を開く必要なく登録ができるはずです。

# 最後に

AWS CloudFormation StackSetsとAWS Organizationsを利用することで、複数のAWS環境に一撃でIAM Roleを作る方法を試してみました。

当然ですが、アップロードするYAMLの内容を書き換えることで、異なるポリシーのIAM Roleを作成したり、全く別のリソースを全アカウント・全リージョンに一撃で作成できることになります。

本記事を書く際、改めて仕様のキャッチアップのためにドキュメントを読み直したり、スクリーンショットの撮影のために再度手順を試すこととなり、アウトプットの機会を得ることの重要性を実感しています。

以下、完全に余談です

### AWS OU IDをhogehogeのままで StackSetの作成を実行するとエラーになります（1敗）

実行前にエラーになってくれてもいいやん
![スクリーンショット 2023-12-02 22.56.27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545278/c809f65c-0c41-8968-554d-7512ad51d859.png)


### StackSetの説明で全角文字を入力すると文字化けします（1敗）

そういえば普段はDiscriptionは英語で書くからこれは踏んだことなかった。

![スクリーンショット 2023-12-02 22.51.23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545278/9be166d6-d82e-4bb2-7db5-2fda4bd6f04f.png)

![スクリーンショット 2023-12-02 23.21.35.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545278/5fe5a721-ff60-c928-e841-99d92bcf2215.png)


### ChatGPT先生曰く、「012345678901」は架空のAWSアカウントを指すので公開されるサンプルコードで利用しても良い。ただしCloudFormation StackSetでは実行後にエラーになる（1敗）

クレジットカードのサンプル番号的なやつがAWSにもあるんですね（本当か？）
![スクリーンショット 2023-12-02 22.47.07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545278/2a8a044f-d469-5008-0968-ee84be41d139.png)

エラー内容
```
ResourceLogicalId:SaaSIAMRole, ResourceType:AWS::IAM::Role, ResourceStatusReason:Resource handler returned message: "Invalid principal in policy: "AWS":"arn:aws:iam::012345678901:root" (Service: Iam, Status Code: 400, （略）.
```