IAM ユーザーのパスワード有効期限が迫っていることを通知する仕組みを、 EventBridge + Lambda 関数 + SES を使って実装してみた

2024.05.02

はじめに

テクニカルサポートの 片方 です。
ついにブログ執筆数が 200 本となりました。前回の 199 本目のブログ に引き続き Lambda 関数を利用した内容を紹介します。
こちらは、私の所属するテクニカルサポートチームへ「〇〇〇 を実現するサービスや機能はないか」と実際にお客様より頂いたお問い合わせを参考 (ヒント) にしてカスタムソリューションを作成してみました。

本ブログでは、EventBridge + Lambda 関数 + SES を使って、IAM ユーザーのパスワード有効期限が迫っていることを通知する仕組みを実装してみました。

構成と説明

EventBridge で Lambda 関数 を定期的に呼び出します。Lambda 関数内で IAM ユーザーのパスワード有効期限が迫っている場合に、該当の IAM ユーザーに対し SES を利用して通知メールを送信します。
なお、対象 IAM ユーザーのメールアドレス取得方法は、タグを利用します。

タグの例)

  • キー: Email
  • 値: abcdefg1234567@gmail.com

以下はイメージです。

やってみた

SES

SES の設定については省かせていただきます。以下の様に設定されていることを確認してください。 ID は送信元 Email アドレスとして使用するので控えておいてください。

  • ID: aabbccdd123456789@gmail.com ※控えておく
  • ID タイプ: E メールアドレス
  • ID ステータス: 検証済み

ロール

Lambda 関数にアタッチするロールを作成します。信頼関係は以下です。

{
    "Version": "2008-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "Service": "lambda.amazonaws.com"
            },
            "Action": "sts:AssumeRole"
        }
    ]
}

ロール名: LambdaFunctionCheckPasswordExpiryRole

アタッチするポリシー例

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "iam:ListUsers",
                "iam:ListUserTags",
                "iam:GetUser"
            ],
            "Resource": "*"
        },
        {
            "Effect": "Allow",
            "Action": [
                "ses:SendEmail",
                "ses:SendRawEmail"
            ],
            "Resource": "*"
        },
        {
            "Effect": "Allow",
            "Action": [
                "logs:CreateLogGroup",
                "logs:CreateLogStream",
                "logs:PutLogEvents"
            ],
            "Resource": "*"
        }
    ]
}

Lambda 関数

ランタイムは Python 3.12 を使用しました。
アタッチする実行ロールは、先ほど作成した "LambdaFunctionCheckPasswordExpiryRole" を選択します。

関数名: LambdaFunctionCheckPasswordExpiry

IAM ユーザーのパスワード有効期限が迫っている場合に、該当のユーザーに通知メールを送信するための Lambda 関数です。
簡単に説明すると... パスワードが設定されている場合は、以下の手順を実行します。

  • パスワードが最後に変更された日時を取得し、それを UTC からローカルタイムに変換します。
  • 現在の日付を取得します。
  • パスワードの有効期限日を計算します。
  • 通知を送信するべき日付(パスワードの有効期限の ●● 日前)を計算します。サンプルコードでは "timedelta(days=7)" として 7 日前を通知対象としています。ご要件に合わせて数字(日数)を変更してください。具体的には、パスワードの有効期限日 "expiration_date" から ●● 日引いた日付を通知の送信対象日として設定します。
  • 現在の日付が通知日以降かつ有効期限日以前である場合、そのユーザーを通知対象としてメール送信します。
サンプルコード
import boto3
from datetime import datetime, timedelta

# Initialize AWS clients
iam = boto3.client('iam')
ses = boto3.client('ses')

# パスワードの有効期間を取得する関数
def get_password_policy():
    response = iam.get_account_password_policy()
    max_password_age = response['PasswordPolicy']['MaxPasswordAge']
    return max_password_age

# 通知対象のIAMユーザーを取得する関数
def target_user():
    global expires
    global new_users
    expires = []
    new_users = []

    # パスワードの有効期間を取得
    max_password_age = get_password_policy()

    # IAMユーザーの情報を取得
    users = iam.list_users()['Users']
    for user in users:
        username = user['UserName']

        # パスワード情報を取得
        response = iam.get_user(UserName=username)
        password_last_changed = response['User'].get('PasswordLastUsed')

        # パスワードが設定されており、かつ有効期間内であれば通知対象とする
        if password_last_changed:
            password_last_changed = password_last_changed.replace(tzinfo=None)
            today = datetime.today()
            expiration_date = password_last_changed + timedelta(days=max_password_age)
            notification_date = expiration_date - timedelta(days=7)

            if today >= notification_date and today <= expiration_date:
                new_users.append(username)
                expires.append(expiration_date)

# 通知先のメールアドレスを取得する関数
def get_address():
    global email
    email = []
    for user in new_users:
        response = iam.list_user_tags(UserName=user)
        tags = response.get('Tags', [])
        for tag in tags:
            if tag['Key'] == 'Email':
                email.append(tag['Value'])

# メールを送信する関数
def send_mail():
    for i, dst_address in enumerate(email):
        username = new_users[i]
        expiration_date = expires[i].strftime("%Y-%m-%d")
        signin_url = f"https://{boto3.client('sts').get_caller_identity().get('Account')}.signin.aws.amazon.com/console"
        subject = "【重要】パスワードの有効期間が迫っています"
        body = f"{username} 様へ\n\nパスワードの有効期限が迫っています。現在設定されているパスワードは {expiration_date} に失効しますので、お早めに変更してください。\nサインインはこちらから:{signin_url}"
        ses.send_email(
            Destination={'ToAddresses': [dst_address]},
            Message={
                'Body': {'Text': {'Data': body}},
                'Subject': {'Data': subject}
            },
            Source="SES で設定した送信元アドレスをここに入力する"
        )

# Lambdaハンドラ関数
def lambda_handler(event, context):
    target_user()
    get_address()
    send_mail()

EventBridge

作成した Lambda 関数と同じリージョンで実装してください。

ルール名: LambdaFunctionCheckPasswordExpiryRule

イベントバスは default で、ルールタイプはスケジュールを選択します。

通常のレートで実行されるスケジュール (10 分ごとなど)。を選択します。
rate 式は、今回 1 日 に設定しました。こちらはお好みで設定してください。

ターゲットのセクションで、ターゲットタイプを AWS のサービスを選択し、ターゲットを選択では Lambda 関数 を選択します。
先ほど作成した関数名 "LambdaFunctionCheckSSMAgentStatus" を選択してルール作成を行えば終了です。

以上で実装は終了です。お疲れさまでした。

検証してみた

パスワードポリシーを以下の様にします。パスワードの有効期間を 5 日に設定したので作成した IAM ユーザーは通知対象です。

タグ付けもします。

結果、問題なくタグに記載の Email アドレスへ通知されました。

【重要】パスワードの有効期間が迫っています  

Test-User-a 様へ  
パスワードの有効期限が迫っています。現在設定されているパスワードは 2024-04-19 に失効しますので、お早めに変更してください。  
サインインはこちらから:https://123456789012.signin.aws.amazon.com/console

検証時に作成した IAM ユーザーの "パスワードが作成されてから経過した期間" は 4/14 であり検証日(テスト実施)も 4/14 です。そのためパスワードの有効期間を 8 日などに設定した場合は、通知対象の 7 日前に条件が合致しないため通知対象外で Email は送信されません。(検証済み)

注意点

タグに Email アドレス記載があることからも、個人情報の取り扱いには十分に注意してください。
特定の管理者・責任者以外は、IAM のマネジメントコンソール画面を参照不可にするといった対応をご検討ください。

{
    "Version": "2012-10-17",
    "Statement": {
        "Effect": "Deny",
        "Action": [
            "iam:Get*",
            "iam:List*",
            "iam:Generate*"
        ],
        "Resource": "*"
    }
}

まとめ

ご自身の環境に合わせて適宜修正の上ご利用ください。本ブログが誰かの参考となれば幸いです。

参考資料

アノテーション株式会社について

アノテーション株式会社は、クラスメソッド社のグループ企業として「オペレーション・エクセレンス」を担える企業を目指してチャレンジを続けています。「らしく働く、らしく生きる」のスローガンを掲げ、様々な背景をもつ多様なメンバーが自由度の高い働き方を通してお客様へサービスを提供し続けてきました。現在当社では一緒に会社を盛り上げていただけるメンバーを募集中です。少しでもご興味あれば、アノテーション株式会社WEBサイトをご覧ください。