Lambda関数の処理を整理してみた【アクセスキーのローテーション自動通知】

### はじめに
アクセスキーとシークレットアクセスキーが生成されて90日経つとTrusted Advisorから通知が来ます。アクセスキー、シークレットアクセスキーは有効期限は無く事前に知る術がありません。そのため一度作成するとローテーションされることなくそのままの状態になります。

自動で通知する仕組みを検証をしたときに詰まったことや使用したサービスやコードの理解があいまいなので整理することにしました。

### そもそもアクセスキーとは
一言でいうとマイナンバーカードのようなものでしょうか。
アクセスキーがカードに記載された名前でシークレットアクセスキーが流出してはいけない個人番号のようなものです。

アクセスキーは認証情報としての役割を持っています。アクセスキーIDとシークレットアクセスキーの二つで構成されています。
これを使ってAWS CLIやAWS APIにプログラムでリクエストに署名することができ、認証する際に使用されます。

詳しくはこちらに記載されています！

https://docs.aws.amazon.com/ja_jp/IAM/latest/UserGuide/id_credentials_access-keys.html


SDK、AWSCLIにログインしてコマンドを実行したりS3バケットにファイルをアップロードするために使われたりします。

### アクセスキーを作成する方法

アクセスキーはIAMユーザーのコンソール画面からセキュリティ認証情報をタブをクリックしユースケースを選択する。
「上記のレコメンデーションを理解し、アクセスキーを作成します。」にチェックを入れて「次へ」をクリック
「アクセスキーを作成」をクリックすることで作成できる。シークレットアクセスキーはCSVファイルでダウンロードできる。（ただし1回しか表示されないので忘れず！）

https://dev.classmethod.jp/articles/iam_user_create_access_keys_2023/

### 通知自動化の概要
今回のこの仕組みを作るのに参考にした記事はこちらです。

https://dev.classmethod.jp/articles/iam-passwordexpiry-eventbridge-lambda-ses-solutions/

かんたんに説明すると対象のIAMユーザーにタグをつけてキーにはEmail、値には送信するメールアドレスを設定します。

①EventBridgeが毎日決まった時間にLambdaを実行します。
②Lambdaでの処理はIAMユーザーの情報を読み取りアクセスキーが生成された日から90日間経過し、かつその90日よりも7日前の日付に該当するIAMユーザーを通知の対象とします。そしてタグ付けされたメールアドレスに送信します。
③タグ付けで送信対象か対象外を決めることができます。

今回参考にした技術記事はIAMユーザーのパスワード有効期限が迫っていることを通知する仕組みですがこれをアクセスキー、シークレットアクセスキーの設定にしています。
※1 タグ付けする設定はスキップします。
※2 実際のコードは記載していません。

### IAMでの設定
IAMロールでは記事にある内容をそのまま使用します。
ちなみにここいらでIAMポリシーの復習

#### Statement
ロールに適用される許可や信頼を詳細に指定します。
1つ以上のStatementブロックを持つことができます。

#### Effect
"Allow"
これは、このポリシーが指定されたアクションを許可することを示します。

#### Principal
Principalは、ロールを引き受けることができるエンティティ（例えばAWSサービス、ユーザー、または別のアカウント）を定義します。

#### Service
ここでは"lambda.amazonaws.com"が指定されています。
このロールを AWS Lambda サービスが引き受けることを許可することを意味します。

#### Action": "sts:AssumeRole"
「特定のIAMロールを引き受ける権限」を与える設定です。

## 追加するIAMポリシー
記事のIAMポリシーに加えて追加するポリシーはこちらです。

```
iam:ListUsers
# IAMユーザーの一覧を取得します。
iam:ListAccessKeys
# IAMユーザーに関連付けられたアクセスキーのリストを取得する権限
iam:GetAccessKeyLastUsed
# アクセスキーが最後に使用された月と日の情報を取得する権限
iam:ListUserTags
# IAMユーザーに関連付けられたタグの一覧を取得します。
iam:GetUser
# 指定したIAMユーザーの詳細情報を取得します。
ses:SendEmail
# メールを送信する権限
ses:SendRawEmail
# MIME形式の詳細なメール（添付ファイルなどを含む）を送信する権限
logs:CreateLogGroup
# 新しいロググループを作成する権限
logs:CreateLogStream
# 既存のロググループ内に新しいログストリームを作成する権限
logs:PutLogEvents
# ログイベントをログストリームに書き込む権限
```

## Lambda関数
参考にした記事に書いてあることはほとんどスキップしており、疑問点の部分だけを解説しています。
モジュールインストール後の関数が下記になります。

### 通知対象のIAMユーザーを取得する関数
```
def target_user():
    global expires
    global new_users
    expires = []
    new_users = []
    
# globalはこの関数全体でアクセス可能な変数や関数のことをいいます。
　 関数内部でこれらの変数をグローバルスコープの変数として扱うことを宣言しています。
 　Pythonでは、関数内で変数を参照または更新する際、デフォルトではローカルスコープ（関数内部専用）として扱われます。

```
## これから登場する変数
たくさんの変数が出てくるので整理

・user情報変数
・ユーザーネーム変数
・アクセスキー取得対象変数
・アクセスキー変数
・キー変数
・アクセスキーID変数
・アクセスキー作成日変数
・現在日時変数
・Trsted通知変数
・通知判定済みユーザーリスト変数
・ローテーション通知日変数
・メール送信対象リスト変数
・本文書き込みユーザ名変数
・本文書き込み通知日変数




## Lambda関数の処理の流れ
このあとのLambda関数の処理は下記のようになります。
実際のコードは割愛します。

### ①ユーザーの情報の各要素を代入
IAMのlist_usersメソッドを呼び出してusersリストにあるUsersから各要素（ユーザーネーム、arnなど）を取り出して変数に代入していきます。これをユーザー情報を格納した変数=「user情報変数」とします。

### ②ユーザネームの読み込み
次はfor文を使用します。さきほどの「user情報変数」からユーザーIDを入れて「ユーザーネーム変数」に代入します。ユーザーネームはprint fのフォーマットで出力していきます。

### ③アクセスキー情報の取得
アクセスキーの情報を取得する処理を行うため
先ほどのユーザーネーム変数からユーザネームを抜き出してアクセスキー取得対象変数に代入します。
list_access_keysメソッドを呼び出して['AccessKeyMetadata']のオブジェクトでアクセスキー取得対象変数に対して
一つずつユーザーネームに紐づいているアクセスキーの情報を取得してアクセスキー変数に代入します。

### ④ローテーション通知日の判定処理
アクセスキー変数の中のリストをキー変数にいれてfor文で回していきます。
　・キー変数にあるAccessKeyIdをアクセスキーID変数に代入します。
　・キー変数にある作成日時の情報を取得しreplaceメソッドを使って時間の情報だけをNoneにリプレイスします。
 　日にちだけの情報をアクセスキー作成日変数に代入します。
　・datetimeモジュールからutcnowメソッドを使用し現在日時変数に代入します。
　・アクセスキー作成日変数にtimedeltaオブジェクトを使って90日加算した日にちをTrsted通知変数に代入します。
　・Trusted通知変数からtimedeltaオブジェクトを使って10日減算した日にちをローテーション通知日変数に代入します。

### ⑤Lambda実行日が通知日なのかを判定する処理
 if文を使ってLambdaを実行した日が通知日なのか判定する処理を行います。これらの条件に合致するかを判定します。

・発砲した日付がローテーション通知日変数に格納された日にち以降であること
・発砲した日付がTrsted通知変数に格納された日にち以前であること

上記２つの条件に合致した場合に下記2つの処理がされます。

・通知判定済みユーザーリスト変数にユーザー名が追加されます。
・ローテーション通知日変数に実際の通知日が追加されます。

例外の処理は「except Exception as e:」を使用して決められたフォーマットの出力結果を出すようにします。

### ⑥メールアドレスを取得する関数を定義
通知判定済みユーザーリスト変数をメール送信対象確認変数に代入します。
list_user_tagsメソッドを使用してメール送信対象確認変数に格納されているIAMユーザーにタグを読み取ります。タグ付けしたユーザーをメール送信対象リスト変数に代入します。

こちらでも同じく例外の処理を「except Exception as e:」を使用します。

### ⑦メールを送信する関数を定義
下記3つの処理を行いメールを送信します。
　・enumerate()関数を使用してインデックスとメールアドレスをペアにしたリストを作成します。
　　ここでのメールアドレスはメール送信対象リスト変数から呼び出します。
　・インデックスのi番目のメール送信対象リスト変数からユーザー名を呼び出して本文書き込みユーザ名変数に代入します。
　・インデックスのi番目のローテーション通知日から%Y-%m-%d形式のフォーマットにした文字列を本文書き込み通知日変数に代入します。
 
 出力例イメージ
|i              |メール送信対象リスト変数  |ローテーション通知日変数|
|:--------------|:--------------|:--------------|
|0              |user1@example.com　|2024-12-20
|1              |user2@example.com  |2024-12-25
|2              |user3@example.com  |2024-12-25
|3       |user4@example.com  |2024-12-30|

これらを通知するメール本文に変数をいれて文章を作成し送信します。


### ⑧ハンドラ関数での処理
並べられている関数を順番に呼び出して実行します。
こちらでも同じく例外の処理を「except Exception as e:」を使用します。

## 参考にしたドキュメント
https://dev.classmethod.jp/articles/iam-passwordexpiry-eventbridge-lambda-ses-solutions/

https://docs.aws.amazon.com/ja_jp/IAM/latest/UserGuide/id_credentials_access-keys.html

https://and-engineer.com/articles/YKdczhQAACkAtksa

https://dev.classmethod.jp/articles/iam_user_create_access_keys_2023/

## 他にもこんな記事を書いています！
https://qiita.com/mmurm/items/01f7c891995a1ff22a5e

https://qiita.com/mmurm/items/26ae3671f5e07097ce26

https://qiita.com/mmurm/items/abf390def0ae6dbd1950

https://qiita.com/mmurm/items/8f421f00a23c8a4fda00