AWS IAMで未使用者のアクセスキーを無効する

# 背景
AWS認定資格の学習中に、「未使用期間が90日以上経過したIAMユーザーのユーザーアクセスキーを無効する作業を自動化するには？」という問題がありました。
AWSの設定のみで実現できないルーチンワーク的なセキュリティ処理をLambda関数などで作成するという解答はAWS認定試験の問題集などに散見されるのですが、具体的にどうやるのかの情報があまりヒットしないので、自分で作って確認してみることにしました。

# 目次
<!-- タイトルとアンカー名を編集 -->
1. [選択肢](#Chapter1)
1. [実現方法](#Chapter2)
1. [参考文献](#reference)

# 選択肢
1. 管理コンソールのIAMサービスからIAMユーザーの利用状況をダウンロードし、90日以上経過したユーザーをピックアップし、無効化する
手動操作、および目視確認のため、自動化には程遠い
　　→却下
2. AWS CLIで以下のコマンドを実行する
```
    $ aws iam generate-credential-report
	$ aws iam get-credential-report
    $ aws iam update-access-key
```
取得した認証情報はbase64でエンコードされたテキストファイルで取得できるが、そのファイルをテキスト抽出し、更にCLIコマンドを実施するのは結構労力を要する。
そもそもアクセスキー、シークレットキーの管理が必要でセキュリティ的に不向きに思われる。
　→却下

３．　AWS SDKでのGenerateCredentialReportでレポート作成後、GetCredentialReportでダウンロードし、UpdateAccesskeyを使用して無効化する
　→こちらが正解

# 実現方法
## 実装環境
1. AWS CloudShell上にpython3.8をインストールする
2. boto3などライブラリをインストール
3. 実装する

## 実装したコード
※参照する列が間違ってたので修正しました。
CSVで出力される列の「access_key_1_last_rotated」で取得される日付が90日以上経過している場合にアクセスキーを無効にする処理を実施しています。
GenerateCredentialReport/GetCredentialReport/UpdateAccesskeyの利用例の一例ですが、ご使用は自己責任でお願いします。
``` inactive_unused_access_key_.py
import sys
import time
import boto3
from datetime import timedelta
from datetime import datetime as dt
from botocore.exceptions import ClientError

iam = boto3.resource('iam')

def generate_credential_report():
    try:
        response = iam.meta.client.generate_credential_report()
    except ClientError:
        print("Couldn't generate a credentials report for your account.")
        raise
    else:
        return response

def get_credential_report():
    try:
        response = iam.meta.client.get_credential_report()
        print(response['Content'])
    except ClientError:
        print("Couldn't get credentials report.")
        raise
    else:
        return response['Content']

def suspend_user(username):
	update_res = []
	check_access_keys = iam.meta.client.list_access_keys(UserName=username)
	for key_lists in check_access_keys['AccessKeyMetadata']:
	    print()
	    res = iam.meta.client.update_access_key(UserName=username, AccessKeyId=key_lists['AccessKeyId'], Status='Inactive')
	    update_res.append(res)
	return update_res

def inactive_unused_access_key():
    print("Let's generate a credentials report...")
    report_state = None
    while report_state != 'COMPLETE':
        cred_report_response = generate_credential_report()
        old_report_state = report_state
        report_state = cred_report_response['State']
        if report_state != old_report_state:
            print(report_state, sep='')
        else:
            print('.', sep='')
        sys.stdout.flush()
        time.sleep(1)

    USER = 0
    ACCESS_KEY_1_LAST_ROTATED = 9
    cred_report = get_credential_report()
    col_count = ACCESS_KEY_1_LAST_ROTATED + 1
    print(f"Got credentials report. Showing only the first {col_count} columns.")
    cred_lines = [line.split(',')[:col_count] for line in cred_report.decode('utf-8').split('\n')]


    col_width = max([len(item) for line in cred_lines for item in line]) + 2
    today = dt.today()

    for line in cred_report.decode('utf-8').split('\n'):
        if line.split(',')[ACCESS_KEY_1_LAST_ROTATED] == "access_key_1_last_rotated" or line.split(',')[ACCESS_KEY_1_LAST_ROTATED] == "N/A" :
            continue
        else:
            date = line.split(',')[ACCESS_KEY_1_LAST_ROTATED]
            lastloginday = dt.strptime(date[0:9], '%Y-%m-%d')

            if  lastloginday < (today - timedelta(days=90)):
                print(line.split(',')[USER] + "is not used. Update access key to inactive. ")
                suspend_user(line.split(',')[USER])

if __name__ == '__main__':
    inactive_unused_access_key()
```

# 参考文献
- [IAMユーザーの認証情報レポート取得](https://docs.aws.amazon.com/ja_jp/IAM/latest/UserGuide/id_credentials_getting-report.html)
- [サンプルコード（GenerateCredentialReport/GetCredentialReport）](https://github.com/awsdocs/aws-doc-sdk-examples/blob/main/python/example_code/iam/iam_basics/account_wrapper.py)
- [サンプルコード（UpdateAccessKey）](https://gist.github.com/andymotta/cb64ebd71c4703726501fe9a3776ce3d)
-