PythonでIAMユーザーのリストを取得し、差分チェックを取得するためのスクリプト

## はじめに
- 自己学習の一環として、ShellScript以外で簡単なコードを作成しながら、自身の理解を深めるために、解説をする記事です

## スクリプトの内容
- IAMユーザーを読み込んで、別で準備したリストと比較し、差分を抽出してくれます

## 環境
- MacOS Monterey(12.6)
- Python 3.11

## 事前準備
- このコードを実行するにあって、以下のポリシーが最低限必要となります
- 自分のアカウントに付与していない人は、あらかじめ付与してください
- [iam:ListUsers ドキュメント](https://docs.aws.amazon.com/IAM/latest/APIReference/API_ListUsers.html)

``` json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "IAMUserPermissions",
      "Effect": "Allow",
      "Action": "iam:ListUsers",
      "Resource": "*"
    }
  ]
}
```

## コード
```python
import boto3
import os

def main():
    session = boto3.Session(profile_name='xxxxx')

    iam = session.client('iam')

    if not os.path.exists('user.list'):
        print("user.list is not found.")
        return

    with open('user.list', 'r') as f:
        expected_users = [line.strip() for line in f]

    aws_users = iam.list_users()
    aws_user_names = [user['UserName'] for user in aws_users['Users']]
    extra_users = set(aws_user_names) - set(expected_users)

    with open('deleted_user.list', 'w') as f:
        for user in extra_users:
            f.write(user + '\n')

if __name__ == "__main__":
    main()
```

## 解説
#### 1. boto3とは？
```python
import boto3
```
- PythonからAWSを操作するために必要なモジュールです
- [boto3 ドキュメント](https://boto3.amazonaws.com/v1/documentation/api/latest/index.html)

#### 2. Sessionオブジェクト
```python
session = boto3.Session(profile_name='xxxxx')
```
- Sessionオブジェクトを作成して、接続するためのクライアントを作成する前段
- profile_name引数には、「~/.aws/credentials」に記載した、特定のプロファイルを指定（xxxx箇所）
- [boto3 session](https://boto3.amazonaws.com/v1/documentation/api/latest/guide/session.html#default-session)

#### 3. クライアントの作成
```python
iam = session.client('iam')
```
- IAMサービスへのクライアントを作成し、これを使用して今回のIAMユーザーのリストを取得
- [boto3 custom-session](https://boto3.amazonaws.com/v1/documentation/api/latest/guide/session.html#custom-session)

#### 4. os.path.existsによるファイルの存在確認
```python
if not os.path.exists('user.list'):
```
- user.listファイルが、存在するかを確認
- [os.path.exists](https://docs.python.org/ja/3/library/os.path.html#os.path.exists)

#### 5. open関数とwith文
```python
with open('user.list', 'r') as f:
```
- user.listファイルを読み込みモード ('r') で開く
- ファイルオブジェクトは、変数fに代入します
- with文は、ファイル操作が終了した後に自動的にファイルを閉じてくれます

#### 6. stripメソッドとリスト内包表記
```python
expected_users = [line.strip() for line in f]
```
- 変数fから値をもとにstripメソッドを使用して、文字列の先頭と末尾にある空白文字（スペース、タブ、改行など）を削除し、その結果はexpected_usersに代入
- [line.strip() for line in f]は、リスト内包表記と呼ばれる
    - [式 for 任意の変数名 in f]
- [stripメソッド](https://docs.python.org/ja/3/library/stdtypes.html#str.strip)
- [リスト内包表記](https://docs.python.org/ja/3/tutorial/datastructures.html#list-comprehensions)

#### 6-1. expected_usersの中身
```python
user.list
user1
user2
user3
...

expected_usersの
['user1', 'user2', 'user3', ...]
```

#### 7. list_users関数
```python
aws_users = iam.list_users()
```
- list_users関数を使用して、ユーザーの一覧を取得する

#### 8. aws_users['Users']とuser['UserName']の意味
```python
aws_user_names = [user['UserName'] for user in aws_users['Users']]
```
- aws_users['Users']は、Usersキーの値でユーザー情報を参照
- その後、user['UserName']でUserNameキーの値を抽出し、今回のリストを作成
- aws_usersは、iam.list_users()の呼び出しによって得られたレスポンスの辞書で、中身は以下の構造となる

#### 8-1. ユーザー情報の構造のサンプル
```python
{
  'Users': [
    {
      'Path': 'string',
      'UserName': 'string',
      'UserId': 'string',
      'Arn': 'string',
      'CreateDate': datetime(2020, 1, 1),
      ...
    },
    ...
  ],
  ...
}
```

#### 9. Pythonの集合（set）
```python
extra_users = set(aws_user_names) - set(expected_users)
```
- Pythonの集合（set）を使用して、IAMのユーザーリストからexpected_usersリストを引き算します
- これにより、IAMに存在し、expected_usersリストに存在しないユーザーの集合をが残る
- [Pythonの集合（set）](https://docs.python.org/ja/3/tutorial/datastructures.html#sets)

## 最後
- 30行にも満たないコードでしたが、記事にしようとすると数時間かかるので疲れましたが、、ShellScriptよりも、短いコードになり勉強になりました
- Pythonの集合（set）が、比較処理を簡単にしてくれたり、リスト内包表記やboto3のことも今回ちゃんと向き合えたのでよかったです