AWS – Cognito 認証済みユーザを一撃作成したい

cognito-create-users AWS

この記事を読むのに必要な時間は 約10分 です。

この記事では、以下の内容をまとめています👇️
詳しく知りたい・質問等がありましたら、ぜひコメント欄へお願いします。

Amazon Cognito のユーザプールに 認証済みユーザをまとめて作成する方法
+ Admin 権限を有した CodeEditor からboto3 を利用して一撃作成

(背景)GenU ユーザ認証での課題

皆さまは GenU 使っていますでしょうか?
Generative AI Use Cases: AWSさまのありがたいページ
AWS上で、生成AIのユースケースをさくっと構築して、利用可能な超便利な OSS リポジトリです!!

Generative AI Use Cases JP


私の職場でも、社内向けの RAGチャット(いわゆる文書検索アシスタント) として活用しており、ありがたいことに「うちの部署でも使いたい!」というお声をいただいております。
ところが…

社内ユーザ
社内ユーザ

めっちゃ便利ですね!
100人くらい新規ユーザを登録したいんだけど

しんじ
しんじ

良いですね!
セルフサインアップ有効化してあるので、ユーザ登録してね♫

社内ユーザ
社内ユーザ

なんだかエラーで登録できないんだが…!?

という自体に遭遇しました。
よく調べてみると、 ユーザ登録するときに「Cognito からEメールを送信」するのですが、「1日に送信可能なメールは50通まで」という制限がありました…!?
GenU では、ユーザ認証に Amazon Cognito を利用しています。
Amazon SES を利用すれば、50通/日という制限を気にしなくて良いのですが、定常的に発生するイベントではなく、スパイク的な事象なので、そのためだけに SES を利用するのもナンセンスです。

ということで、

しんじ
しんじ

たまにしかないことなら、 管理者権限で一撃作成できるようにしておこう!

って感じで、boto3 使って認証済みユーザを作成できるようにしてみました。

💡Cognito の制限については、こちらのページでも詳しく解説されています
 これから Cognito でユーザ認証をいじる方は、ぜひ参考にしてください〜
 👉️ 知らなかったCognitoの制限 – NCDCエンジニアブログ[Zenn]

Webアプリケーションやモバイルアプリケーションに対して、ユーザーのサインアップやサインインのための開発を容易にする機能を提供するマネージドサービスです。また、ユーザープールというユーザーディレクトリを作成し、ユーザー情報を管理することもできます。

Amazon Cognitoとは : 知らなかったCognitoの制限 – NCDCエンジニアブログ[Zenn]

boto3 で認証済みユーザを作成する

Cognito のセルフサインアップを利用してユーザ作成する場合は、以下の流れで作成されます。

  1. ユーザ名(アドレス)とパスワードを設定
  2. Cognito からアドレス確認用のメールが届く
  3. メールのリンクにアクセスすることでアドレスを有効化
    👉️ユーザ登録完了

一方で、boto3で Cognito のユーザ登録をする場合は、以下のように実装できます。
作成した ノートブック形式ファイルはこちら です。
新規作成するユーザ情報を確認しながら操作できるので、ノートブック形式ファイルの方がおすすめです。

import boto3
import pandas as pd
from tqdm import tqdm


def get_all_users(user_pool_id):
    """ Cognitoユーザプールからユーザリストを取得
    Args:
        user_pool_id (string): CognitoユーザプールID
    Returns:
        users (list): ユーザプール内の全てのユーザリスト
    """
    cognito_client = boto3.client('cognito-idp')
    users = []
    pagination_token = None

    while True:
        if pagination_token:
            response = cognito_client.list_users(
                UserPoolId=user_pool_id,
                PaginationToken=pagination_token
            )
        else:
            response = cognito_client.list_users(
                UserPoolId=user_pool_id
            )

        users.extend(response['Users'])

        if 'PaginationToken' in response:
            pagination_token = response['PaginationToken']
        else:
            break

    return users


def users_to_dataframe(users):
    """ Cognitoユーザプールから取得したユーザリストをデータフレームに変換
    Args:
        users (list): ユーザプール内の全てのユーザリスト
    Returns:
        (pd.DataFrame): Cognitoのユーザリスト
    """
    user_data = []
    for user in users:
        user_dict = {
            'Username': user['Username'],
            'UserStatus': user['UserStatus'],
            'UserCreateDate': user['UserCreateDate'].strftime('%Y-%m-%d %H:%M:%S'),
            'UserLastModifiedDate': user['UserLastModifiedDate'].strftime('%Y-%m-%d %H:%M:%S'),
        }

        # Add user attributes
        for attr in user['Attributes']:
            user_dict[attr['Name']] = attr['Value']

        user_data.append(user_dict)

    return pd.DataFrame(user_data)


def check_string_in_column(df, column_name, string_to_check):
    """
    Args:
        df(Dataframe): チェック対象のデータフレーム
        column_name(str): チェック対象の列名
        string_to_check(str): 検索する文字列
    Return:
        (list): データフレームの各行について、True|False のリスト
    """
    return df[column_name].str.contains(string_to_check, na=False)


def create_cognito_users(USER_POOL_ID, df):
    """ Cognito ユーザプールに新規ユーザをまとめて作成する
    Args:
        USER_POOL_ID (str): CognitoユーザプールID
        df (pd.DataFrame): 登録したいユーザと初期パスワードをまとめたデータフレーム
    """
    cognito_client = boto3.client('cognito-idp')

    # Cognito ユーザプールから最新のユーザリストを取得
    user_list = get_all_users(USER_POOL_ID)
    # print(f"Cognito ユーザプールに登録されている人数: {len(user_list)}")

    # ユーザリストをデータフレームに変換
    df_users = users_to_dataframe(user_list)

    # 一時的な仮パスワード
    PASS_TEMP = "Karipass#9020"

    # Admin権限でユーザを新規作成する
    for index, row in tqdm(df.iterrows(), total=len(df)):
        EMAIL = row["メールアドレス"]
        PASS = row["初期パスワード"]

        # Cognitoユーザプール内に重複が無いかをチェック
        if check_string_in_column(df_users, 'email', EMAIL).sum():
            print(f'Creatig user >>> {EMAIL} >>> pass')
            continue
        print(f'Creatig user >>> {EMAIL}')

        # 共通の仮パスワードを使って、とりあえずユーザ作成
        # ここで一時パスワードを設定すると時限付となってしまう
        # メール認証は `済` にしてしまう
        cognito_client.admin_create_user(
            UserPoolId=USER_POOL_ID,
            Username=EMAIL,
            TemporaryPassword=PASS_TEMP,
            UserAttributes=[
                {
                    'Name': 'email_verified',
                    'Value': 'true'
                },
                {
                    'Name': 'email',
                    'Value': EMAIL
                }
            ],
            MessageAction='SUPPRESS'
        )

        # 初期パスワードを確認済みとして設定
        cognito_client.admin_set_user_password(
            UserPoolId=USER_POOL_ID,
            Username=EMAIL,
            Password=PASS,
            Permanent=True
        )

    print("New user created in the Cognito user pool.")


def main():
    df = pd.read_excel('GenU_一括登録リスト(テスト).xlsx')
    df = df.dropna(subset='メールアドレス')
    df = df.filter(['メールアドレス', '初期パスワード'])    # 余計な情報があった場合は、フィルタリングする

    # Cognito ユーザプールID
    USER_POOL_ID = "XXXXXXXXXXXXXXXXXXXXXXX"

    # ユーザ名と初期パスワードをまとめたデータフレーム
    df = pd.read_csv("users.csv")

    create_cognito_users(USER_POOL_ID, df)

    print("Done.")


if __name__ == "__main__":
    main()

ポイントとしては、こんな感じです👇️

  1. boto3 の admin_create_user を利用してAdmin権限でユーザを新規作成するときに、
    • email_verified を trueにする
      💡 ここで メール認証は になります!
       (メール配信されずに済む)
    • emailには、(テキトーな)仮パスワードを設定
      (次のステップでちゃんとパスワードを設定します)
  2. boto3 の admin_set_user_password を利用して、Admin権限でパスワードを設定しちゃう

admin_create_user で仮パスワードを設定できるのですが、時限付きなので、一定時間を経過すると、Admin 権限(Cognito のコンソール画面等)で、再度設定が必要になってしまいます。
ユーザ側としては、かなりメンドクサイ事になるので、(セキュリティ的な課題はありそうですが、)すぐに GenU にアクセスできるようにしています。

あとは、作成したユーザ名&パスワードをお知らせすれば、すぐにGenUを使ってもらうことができます!!

生成AIは、さわってナンボ、ですから、ハードルは少ないほうが良いですね。
(今回は社内向けですが、社外向けだと、もっとちゃんと考える必要があります…!)

まとめ

しんじ
しんじ

新規ユーザリストから 100名分のユーザを一撃作成できました!

社内ユーザ
社内ユーザ

ありがとうございます〜
GenU で遊んでみますね〜

という感じで、100名規模でも、Cognito の制限を突破してユーザ作成することができました!
自分でイチからAIチャットボットを作ると、大変ですが、 GenU 等の OSS を活用するとサクッと社内向けチャットボットを作成できていいですよね。

次回も、生成AI関係でなにか書いてみようと思います〜

コメント

タイトルとURLをコピーしました