Pulumiを使ってCognitoユーザープールを作成してみた

2022.05.12

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

こんにちは!DA(データアナリティクス)事業本部 サービスソリューション部の大高です。

今回は、マルチクラウドに対応している Infrastructure as Code のツール「Pulumi」を使ってCognitoユーザープールを作成してみようと思います。

要件

作成するCognitoユーザープールとしては、以下のような要件を満たすものを作成したいと思います。

  • サインインには「Eメール」を利用してサインインできるものとする
  • パスワード要件はCognitoのデフォルト要件で良い
  • ユーザーアカウントの復旧(パスワード忘れ)を有効にし、復旧手段には「Eメール」を利用する
  • フロントエンドのアプリケーションクライアントが利用できるように、1つのクライアントを登録する
    • 更新トークンの有効期限は「1日」とする
    • 認証フローは以下をサポートする
      • ALLOW_REFRESH_TOKEN_AUTH (更新トークンベースの認証)
      • ALLOW_ADMIN_USER_PASSWORD_AUTH (認証のための管理 API のユーザー名パスワード認証)
      • ALLOW_USER_SRP_AUTH (SRP (セキュアリモートパスワード) プロトコルベースの認証)
  • ユーザーに通知する「Eメール」の内容は以下とする
    • 招待メッセージ
      • 件名:【仮パスワード通知】
      • 本文:ユーザー名は {username}、仮パスワードは {####} です。
    • 検証メッセージ
      • 件名:【検証コード通知】
      • 本文:検証コードは {####} です。

上記を満たすような要件のものを作成し、それ以外はデフォルトのままとしたいと思います。

作ってみる

では早速作っていきましょう。

プロジェクトの作成まで

「プロジェクトの作成(Create a New Project)」までは以下のエントリで紹介しているとおりに進めるので省略します。なおプロジェクト名は「cognito-userpool-using-pulumi」としました。

ファイルの構成について

今回は「Cognitoユーザープール」および「Cognitoユーザープールクライアント」を作成していくのですが、これらのリソースを1つのファイルに記述していくとコードが冗長になっていきます。

また、作成するリソースが増えることを考えるとファイルを分離しておいたほうが良いので以下のようなフォルダ構成にしていきます。

% tree -I node_modules
.
├── LICENSE
├── Pulumi.dev.yaml
├── Pulumi.yaml
├── README.md
├── index.ts
├── package-lock.json
├── package.json
├── resources
│   └── cognito
│       ├── user-pool-client.ts
│       └── user-pool.ts
└── tsconfig.json

まずはリソース配置用のフォルダと、各リソース用のファイルを作成します。

% mkdir -p resources/cognito
% touch resources/cognito/user-pool.ts
% touch resources/cognito/user-pool-client.ts

これで準備完了です。

ユーザープール作成用コード

準備ができたら、まずは user-pool.ts から作成していきます。

Cognito UserPoolについては以下のドキュメントを参考に作成します。

また、ここで満たすべき「要件」は以下になるので、こちらも意識して作成します。

  • サインインには「Eメール」を利用してサインインできるものとする
  • パスワード要件はCognitoのデフォルト要件で良い
  • ユーザーアカウントの復旧(パスワード忘れ)を有効にし、復旧手段には「Eメール」を利用する
  • ユーザーに通知する「Eメール」の内容は以下とする
    • 招待メッセージ
      • 件名:【仮パスワード通知】
      • 本文:ユーザー名は {username}、仮パスワードは {####} です。
    • 検証メッセージ
      • 件名:【検証コード通知】
      • 本文:検証コードは {####} です。

以上を踏まえたコードが以下になります。

resources/cognito/user-pool.ts

import * as aws from "@pulumi/aws";

export class UserPool {
  public name: string;
  private inviteSubject: string = "【仮パスワード通知】";
  private inviteMessage: string =
    "ユーザー名は {username}、仮パスワードは {####} です。";
  private verificationSubject: string = "【検証コード通知】";
  private verificationMessage: string = "検証コードは {####} です。";

  constructor(name: string) {
    this.name = name;
  }

  create(): aws.cognito.UserPool {
    const pool = new aws.cognito.UserPool(this.name, {
      accountRecoverySetting: {
        recoveryMechanisms: [
          {
            name: "verified_email",
            priority: 1,
          },
        ],
      },
      adminCreateUserConfig: {
        inviteMessageTemplate: {
          emailSubject: this.inviteSubject,
          emailMessage: this.inviteMessage,
          smsMessage: this.inviteMessage,
        },
      },
      autoVerifiedAttributes: ["email"],
      emailVerificationSubject: this.verificationSubject,
      emailVerificationMessage: this.verificationMessage,
      usernameAttributes: ["email"],
    });

    return pool;
  }
}

「パスワード要件はCognitoのデフォルト要件で良い」以外の要件については、それぞれ「aws.cognito.UserPool」のプロパティとして明示しています。

ユーザープールクライアント作成用コード

次に user-pool-client.ts を作成していきます。

Cognito UserPoolClientについては以下のドキュメントを参考に作成します。

また、UserPoolClientで満たすべき「要件」は以下になるので、こちらも意識して作成します。

  • フロントエンドのアプリケーションクライアントが利用できるように、1つのクライアントを登録する
    • 更新トークンの有効期限は「1日」とする
    • 認証フローは以下をサポートする
      • ALLOW_REFRESH_TOKEN_AUTH (更新トークンベースの認証)
      • ALLOW_ADMIN_USER_PASSWORD_AUTH (認証のための管理 API のユーザー名パスワード認証)
      • ALLOW_USER_SRP_AUTH (SRP (セキュアリモートパスワード) プロトコルベースの認証)

以上を踏まえたコードが以下になります。

resources/cognito/user-pool-client.ts

import * as aws from "@pulumi/aws";

export class UserPoolClient {
  public name: string;
  public pool: aws.cognito.UserPool;

  constructor(name: string, pool: aws.cognito.UserPool) {
    this.name = name;
    this.pool = pool;
  }

  create(): aws.cognito.UserPoolClient {
    const client = new aws.cognito.UserPoolClient(this.name, {
      userPoolId: this.pool.id,
      explicitAuthFlows: [
        "ALLOW_ADMIN_USER_PASSWORD_AUTH",
        "ALLOW_REFRESH_TOKEN_AUTH",
        "ALLOW_USER_SRP_AUTH",
      ],
      refreshTokenValidity: 1,
    });

    return client;
  }
}

こちらはかなりシンプルですね。要件については、それぞれ「aws.cognito.UserPoolClient」のプロパティとして明示しています。また、ユーザープールクライアントに紐付けるユーザープールが必要となるので、引数として与えるようにしてみました。

リソース作成用コード

最後に上記のリソースを作成するための大本のコードです。これはindex.tsに記載します。

index.ts

import { UserPool } from "./resources/cognito/user-pool";
import { UserPoolClient } from "./resources/cognito/user-pool-client";

const userPool = new UserPool("ootaka-user-pool");
const pool = userPool.create();

const userPoolClient = new UserPoolClient("ootaka-user-pool-client", pool);
const client = userPoolClient.create();

export const cognitoClientId = client.id;

先程作成した各リソース作成用のコードを呼び出してリソースを作成しています。また、それぞれのリソース名をootaka-user-poolootaka-user-pool-clientという引数として指定しました。

最後に、作成した「ユーザープールクライアントID」をクライアントアプリケーションで利用するのでexport const cognitoClientId = client.id;としてみました。

デプロイしてみる

以上で全ての準備が完了したので、実際にpulumi upコマンドでデプロイしてみたいと思います。

% pulumi up
Previewing update (dev)

View Live: https://app.pulumi.com/ootaka-daisuke/cognito-userpool-using-pulumi/dev/previews/b29c764b-e5e6-4e1c-8b54-a6f362d18336

     Type                           Name                               Plan       
 +   pulumi:pulumi:Stack            cognito-userpool-using-pulumi-dev  create     
 +   ├─ aws:cognito:UserPool        ootaka-user-pool                   create     
 +   └─ aws:cognito:UserPoolClient  ootaka-user-pool-client            create     
 
Resources:
    + 3 to create

Do you want to perform this update? yes
Updating (dev)

View Live: https://app.pulumi.com/ootaka-daisuke/cognito-userpool-using-pulumi/dev/updates/1

     Type                           Name                               Status      
 +   pulumi:pulumi:Stack            cognito-userpool-using-pulumi-dev  created     
 +   ├─ aws:cognito:UserPool        ootaka-user-pool                   created     
 +   └─ aws:cognito:UserPoolClient  ootaka-user-pool-client            created     
 
Outputs:
    cognitoClientId: "XXXXXXXXXXXXXXXXXXXXXXXXXX"

Resources:
    + 3 created

Duration: 6s

成功しました。cognitoClientIdも表示されていますね。また、実際にコンソール画面からも確認してみます。

想定どおりユーザープールが作成されており、各要件にも合っていそうです。

  • サインインには「Eメール」を利用してサインインできるものとする

  • パスワード要件はCognitoのデフォルト要件で良い

  • ユーザーアカウントの復旧(パスワード忘れ)を有効にし、復旧手段には「Eメール」を利用する

  • フロントエンドのアプリケーションクライアントが利用できるように、1つのクライアントを登録する
    • 更新トークンの有効期限は「1日」とする
    • 認証フローは以下をサポートする
      • ALLOW_REFRESH_TOKEN_AUTH (更新トークンベースの認証)
      • ALLOW_ADMIN_USER_PASSWORD_AUTH (認証のための管理 API のユーザー名パスワード認証)
      • ALLOW_USER_SRP_AUTH (SRP (セキュアリモートパスワード) プロトコルベースの認証)

  • ユーザーに通知する「Eメール」の内容は以下とする
    • 招待メッセージ
      • 件名:【仮パスワード通知】
      • 本文:ユーザー名は {username}、仮パスワードは {####} です。
    • 検証メッセージ
      • 件名:【検証コード通知】
      • 本文:検証コードは {####} です。


▲SMSメッセージの設定忘れが一箇所ありますが、今回SMSは利用しないので良しとしたいと思います。

まとめ

以上、Pulumiを使ってCognitoユーザープールを作成してみました。

上記の内容では、一回で作成出来たような流れになっていますが、実際には何度か試行錯誤を繰り返しています。具体的には「デフォルトの状態で作成されるものがどうなるのか?」が分からなかった為、「実際に作成してみてAWSのコンソール画面で確認する」→「設定を変えてみて再度確認する」ということを繰り返していました。

ドキュメントにどのような設定項目なのか記載はあるのですが、実際には何度か試行錯誤して構築していくのがPulumiを利用した構築になるのかなと思います。(これは他のIaCツールでも同じかなと思います)

一方で「構築、更新、破棄」の処理時間は、私が試した範囲ではとても早く(数秒で終わる)、試行錯誤を繰り返すのにとても助かりました。

どなたかのお役に立てば幸いです。それでは!