Notionの利用情報をSlackに定期送信したいんだ！ - AWS CDK, Amazon EventBridge, AWS Lambdaでつくるバッチ処理アプリケーション - 

今回初めてQiitaで技術記事を書いてみます！！
皆様、お手柔らかに頂けますと幸いです。


# はじめに


本記事は `プロもくチャット Adevent Calendar 2023` の 10 日目です！！

https://qiita.com/advent-calendar/2023/puromoku


# この記事のゴール
- ローカル環境にて `AWS CLI` コマンドを実行できるようになります
- ローカル環境にて `AWS CDK` コマンドを実行できるようになります
- `AWS CDK` コマンドによって AWS 環境に各種リソースを作成できるようになります
- `AWS SDK` を用いて AWS のリソースを操作できるようになります
- Notion の特定データベースに対して実行したクエリ結果を Slack に投稿できるようになります


# 全体像
最終的なシステムの全貌は以下の通りです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3332694/de575207-97d0-43d5-32cb-3ab72f176cbf.png)


# 前提条件
- 利用端末 : `MacBook Air` `Apple M1` `macOS Sonoma 14.1.1`
- 使用言語 : `TypeScript`
- 実行環境 : `Node.js v20.10.0`
- エディタ  : `Visual Studio Code`
- AWS アカウントを作成済み
- `Docker Desktop` インストール済み
- Notion でデータベースを作成済み
- Slack でチャンネルを作成済み
- 作成した lambda 関数のテストは AWS コンソール画面で実施


# やることサマリ
1. Slack アプリケーションの作成
2. Notion Integration の作成
3. (おまけ) IAM ユーザーの作成
4. `AWS CLI` のインストール・設定
5. `AWS CDK` のインストール・設定
6. `AWS CDK` コマンドで新規プロジェクトを作成
7. 各種 `npm` パッケージのインストール
8. 各種認証情報を `AWS Systems Manager Parameter Store` に格納
9. AWS リソースを定義する CDK ファイルを作成
10. `AWS Lambda` で実行する TypeScript ファイルを実装
11. `AWS CDK` コマンドで AWS リソースをデプロイ
12. 利用料金等補足事項


# 手順詳細


## 1. Slack アプリケーション の作成
下記記事「Slack アプリケーションを作成する」を参照してください。
[Notion で作成されたページを日時でサマリして Slack に投稿してみた](https://dev.classmethod.jp/articles/notion-report/ "Notion で作成されたページを日時でサマリしてSlack に投稿してみた")

::: note info
Slack アプリケーションを作成後、下記画像の黒塗り部分に表示されている文字列を控えます。
![スクリーンショット 2024-03-03 15.29.28.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3332694/c40e2cf0-9c76-4cff-36d6-54db49a1ffe7.png)
:::

*す、すみません。。サボりました。。*


## 2. Notion Integration の作成
下記記事「Notion Integration を作成する」「Notion Integration から Notion データベースへの接続を許可」を参照
[Notion で作成されたページを日時でサマリして Slack に投稿してみた](https://dev.classmethod.jp/articles/notion-report/ "Notion で作成されたページを日時でサマリしてSlack に投稿してみた")

::: note info
Notion Integration の Secrets とデータベースID を控えます。
:::

*す、すみません。。サボりました。。。*
*書く気がないわけではないのです..!*
*ホントにそのまま参考になったのです....!*


## 3. (おまけ) IAM ユーザーの作成


公式ドキュメントはこちら。

https://docs.aws.amazon.com/ja_jp/IAM/latest/UserGuide/id_users_create.html 

::: note info
「4. `AWS CLI` のインストール・設定」にてIAMユーザーが必要になります。
AWS アカウントはあるけど IAM ユーザーは持っていない、という方はぜひご一読ください。
:::

::: note warn
IAM におけるベストプラクティスは、IDプロバイダーとのフェデレーションを使用して一時的な認証情報によって AWS にアクセスする方法とのことです。（上記リンク参照）
今回は簡単のため、IAM ユーザーを作成する手段を採用します。
:::


**1. ルートアカウントにて `AWS IAM` にアクセス後、左ペイン「ユーザー」をクリックします。**
![スクリーンショット 2023-11-28 21.56.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3332694/9b4fa110-c27c-4a8d-147c-1f55286c6146.png)


**2. 「ユーザーの作成」をクリックします。**
![スクリーンショット 2023-11-28 22.00.12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3332694/d5b8dc29-2f31-7216-611d-f652ec562f98.png)


**3. 任意のユーザー名を入力します。**
![スクリーンショット 2023-11-28 22.04.05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3332694/f77ac4c7-d7ed-aed0-d767-0b08ff743640.png)


**4. 「AWS マネジメントコンソールへのユーザーアクセスを提供する」をクリックします。** 
`AWS Lambda` へのデプロイ完了後、マネジメントコンソールにてテストを実施するためです。
クリックすると、青枠の項目が表示されます。
![スクリーンショット 2023-11-28 22.05.43.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3332694/c524b838-ba94-f26e-226f-1220aef2f2a2.png)


**5. 「IAM ユーザーを作成します」を選択します。**
選択すると、ユーザーのログイン情報を設定する画面が表示されます。
適宜情報を入力します。


**6. 下記画像の右下にある「次へ」をクリックします。**
![スクリーンショット 2023-11-29 20.33.25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3332694/ae6adc41-5b39-9332-9388-c5c10a3e9abd.png)


**7. 「ポリシーを直接アタッチする」を選択します。**
![スクリーンショット 2023-11-29 20.38.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3332694/ccf84303-dc2d-4be0-a0a6-ce7edee189a7.png)

::: note warn
「許可のオプション」におけるベストプラクティスは、目的のポリシーをアタッチしたユーザーグループに対して今回作成したユーザーを追加することのようです。（画面に表示されている通り）
今回は簡単のため、ポリシーを直接アタッチする手段を採用します。
:::


**8. `AdministratorAccess` ポリシーを選択したのち、右下の「次へ」をクリックします。**
![スクリーンショット 2023-11-29 20.52.52.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3332694/6ce2371e-9c32-24d0-c56c-c0570e5050b0.png)

::: note warn
当然、管理者権限を付与することは最小権限の原則から逸脱した行為ですが、今回はトラブルシューティング簡素化（という言い訳）のため当該ポリシーを採用しました。
:::


**9. 確認画面で「ユーザーの作成」をクリックします。**
![スクリーンショット 2023-11-29 21.00.17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3332694/d886065b-8a11-26e9-89d9-ce7ec106edeb.png)


## 4. `AWS CLI` のインストール・設定


**1. 作成したユーザーの概要画面にて「アクセスキーを作成」をクリックします。**
![スクリーンショット 2023-11-29 21.08.16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3332694/26c50369-6a60-d554-27b5-7b758524adff.png)


**2. 「ユースケース」にて「コマンドラインインターフェイス(CLI)を選択します。**
![スクリーンショット 2023-11-29 21.22.53.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3332694/13a3e5bf-bd06-2dcb-0692-9e6dd594d2bf.png)

::: note warn
画面には、推奨される代替案が2つ表示されています。
ただ、本記事では言及しません。僕の勉強不足ゆえ。。
公式ドキュメントをご参照ください。
[IAM ユーザーのアクセスキーの管理](https://docs.aws.amazon.com/ja_jp/IAM/latest/UserGuide/id_credentials_access-keys.html)
:::


**3. 「上記のレコメンデーションを理解し、アクセスキーを作成します」にチェックをつけたのち、「次へ」をクリックします。**


**4. 適宜アクセスキーの説明を入力し、「アクセスキーの作成」をクリックします。**
![スクリーンショット 2023-11-29 21.31.08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3332694/4685c434-cf59-99c3-200c-ace8175742c0.png)


**5. 「アクセスキーの取得」画面に表示される *アクセスキー* と *シークレットキー* をコピーし保存します。**


**6. 下記公式ドキュメントから macOS `pkg` ファイルをダウンロードします。**
画像にあるリンクから該当ファイルをダウンロードできます。
今回は「GUI installer」を用いた方法を採用しました。
![スクリーンショット 2023-11-30 20.55.43.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3332694/1daa9677-8d18-e920-03d2-6b1bdd5f3e85.png)


**7. ダウンロードした `pkg` ファイルを実行します。**
画像のようなインストール画面が表示されると思います。
![スクリーンショット 2023-11-30 21.04.54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3332694/64d8d565-e8ec-209b-60ad-f899b3416348.png)


**8. インストーラーの指示に従い、`AWS CLI`　をインストールします。**
特別何かを変更する必要はないと思います。
*(少なくとも僕は今困っていません)*


**9. 下記コマンドを実行し、正常にインストールできたかどうか確認します**
```bash
# aws コマンドのインストール先が表示されること
$ which aws
/usr/local/bin/aws

# aws コマンドのバージョンが表示されること
$ aws --version
aws-cli/2.13.32 Python/3.11.6 Darwin/23.1.0 exe/x86_64 prompt/off
```


**10. 下記コマンドを実行し、*アクセスキー*・*シークレットキー*を設定します。**
```bash
aws configure
```

対話型で認証情報を聞かれるので、適宜入力します。
```bash
AWS Access Key ID [None]: "先ほど控えたアクセスキー"
# Enter
AWS Secret Access Key [None]: "先ほど控えたシークレットキー"
# Enter
Default region name [None]: "任意のリージョン" # (東京リージョンが無難ですかね？）
# Enter
Default output format [None]: json # 他の形式も選択できます。公式ドキュメントには json が指定されていました。
# Enter
```
公式ドキュメントはこちら。
[IAM ユーザー認証情報を使用して認証を行う](https://docs.aws.amazon.com/ja_jp/cli/latest/userguide/cli-authentication-user.html)

::: note warn
ベストプラクティスは、AWS IAM Identity Center などの ID プロバイダーとのフェデレーションを使用することのようです。(上記ドキュメント参照)
僕の勉強不足ゆえ、今回は IAM ユーザーの認証情報を利用します。
:::


**11. 下記コマンドを実行し、設定が正常に完了したことを確認します。**
```bash
cat ~/.aws/credentials
```

```
# 出力結果
[default]
aws_access_key_id = "先ほど入力したアクセスキー"
aws_secret_access_key = "先ほど入力したシークレットキー
```


## 5. `AWS CDK` のインストール・設定
公式ドキュメントはこちら

https://docs.aws.amazon.com/ja_jp/cdk/v2/guide/getting_started.html#getting_started_install

**1. 下記コマンドを実行し、`AWS CDK` ツールキットをグローバルにインストールします。**
```bash
npm i -g aws-cdk
```

**2. 下記コマンドを実行し、インストールが正常に完了したことを確認します。**
```bash
cdk --version
```
```
# cdk コマンドのバージョンが表示されること
$ 2.110.1 (build 0d37f0d)
```


## 6. `AWS CDK` コマンドで新規プロジェクトを作成


インストールした `AWS CDK` ツールキットを利用して新規プロジェクトを作成します。


**1. 新規プロジェクト用のディレクトリを作成します。**
```bash
$ pwd
/Users/userName/Desktop/Lambda

$ mkdir ./qiita
```


**2. 作成したディレクトリにて下記コマンドを実行します。**
```bash
cdk init --language typescript
```

コマンド実行後のディレクトリは次の通りです。
![スクリーンショット 2023-12-01 21.45.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3332694/910e42b0-e4b1-d14e-337c-e2d270a11159.png)

*ふぅ〜、これで初期設定が完了しました。*
*あとは煮るなり焼くなり好きにできます。*


## 7. 各種 `npm` パッケージのインストール


**1. 下記コマンドを実行し、今回必要な各種パッケージをインストールします。**
```bash
npm i @slack/web-api @notionhq/client @types/aws-lambda @aws-sdk/client-ssm
```

- @slack/web-api
Slack にデータを送信・Slack からデータをクエリするための SDK です。

https://slack.dev/node-slack-sdk/

- @notionhq/client
Notion にデータを送信・Notion からデータをクエリするための SDK です。

https://github.com/makenotion/notion-sdk-js

- @types/aws-lambda
`AWS Lambda` のための型定義を提供するパッケージです。

https://www.npmjs.com/package/@types/aws-lambda

- @aws-sdk/client-ssm
プログラムから `AWS Systems Manager Parameter Store` を操作するための SDK です。


## 8. 各種認証情報を `AWS Systems Manager Parameter Store` に格納


**1. 下記コマンドを実行し、各種認証情報を `AWS Systems Manager Parameter Store` に格納します。**
公式ドキュメントはこちら。

https://awscli.amazonaws.com/v2/documentation/api/latest/reference/ssm/index.html#cli-aws-ssm

https://docs.aws.amazon.com/ja_jp/systems-manager/latest/userguide/systems-manager-parameter-store.html#what-is-a-parameter

```bash
aws ssm put-parameter --name qiita-notionAuth --value "xxxxxxxxxx" --type "SecureString"
aws ssm put-parameter --name qiita-notionDbId --value "xxxxxxxxxx" --type "SecureString"
aws ssm put-parameter --name qiita-slackBotToken --value "xxxxxxxxxx" --type "SecureString"
aws ssm put-parameter --name qiita-channelName --value "xxxxxxxxxx" --type "String"
```

- --name オプション
パラメータの名称です。
適宜任意のものを設定します。

- --value オプション
パラメータ名に対応する各種認証情報です。

- --type オプション
設定する認証情報文字列の種別を指定します。
今回は下記2種類を利用しています。
    - String       : プレーンテキスト
    - SecureString : セキュアな方法で保存および参照する必要がある機密データ


## 9. AWS リソースを定義する CDK ファイルを作成


今回構築する AWS リソースを定義した CDK ファイルを作成します。
ファイルの全体は次の通りです。
```typescript:qiita-stack.ts
import * as cdk from "aws-cdk-lib";
import { Construct } from "constructs";

export class QiitaStack extends cdk.Stack {
  constructor(scope: Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    // IAM ロールの定義
    const executionLambdaRole = new cdk.aws_iam.Role(
      this,
      "executionLambdaRole",
      {
        roleName: "qiita-executionRole",
        assumedBy: new cdk.aws_iam.ServicePrincipal("lambda.amazonaws.com"),
        managedPolicies: [
          cdk.aws_iam.ManagedPolicy.fromAwsManagedPolicyName(
            "AmazonSSMReadOnlyAccess"
          ),
          cdk.aws_iam.ManagedPolicy.fromAwsManagedPolicyName(
            "CloudWatchLogsFullAccess"
          ),
        ],
      }
    );

    // Lambda 関数の定義
    const lambda = new cdk.aws_lambda_nodejs.NodejsFunction(
      this,
      "main-handler",
      {
        runtime: cdk.aws_lambda.Runtime.NODEJS_20_X,
        entry: "lambda/handler.ts",
        role: executionLambdaRole,
        environment: {
          NOTION_AUTH: "qiita-notionAuth",
          NOTION_DB_ID: "qiita-notionDBId",
          SLACK_BOT_TOKEN: "qiita-slackBotToken",
          SLACK_CHANNEL_NAME: "qiita-channelName",
        },
        bundling: {
          sourceMap: true,
        },
        timeout: cdk.Duration.seconds(30),
      }
    );

    // EventBridge スケジュールを定義
    new cdk.aws_events.Rule(this, "Schedule", {
      schedule: cdk.aws_events.Schedule.rate(cdk.Duration.minutes(1)),
      targets: [new cdk.aws_events_targets.LambdaFunction(lambda)],
    });
  }
}
```

コードにて定義しているリソース・各種説明は次の通りです。

- IAM ロール
    公式ドキュメントはこちら
    [class Role (construct)](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_iam.Role.html)
    ```typescript
    const executionLambdaRole = new cdk.aws_iam.Role(
      this,
      "executionLambdaRole",
      {
        roleName: "qiita-executionRole",
        // AWS Lambda を信頼ポリシーに設定します。
        assumedBy: new cdk.aws_iam.ServicePrincipal("lambda.amazonaws.com"),
        // ロールに含めるポリシーを指定します。
        // 今回は AWS から提供されている管理ポリシーを指定しました。
        managedPolicies: [
          // AWS SSM への読み取り権限
          cdk.aws_iam.ManagedPolicy.fromAwsManagedPolicyName(
            "AmazonSSMReadOnlyAccess"
          ),
          // AWS CloudWatch Logs へのフルアクセス権限
          cdk.aws_iam.ManagedPolicy.fromAwsManagedPolicyName(
            "CloudWatchLogsFullAccess"
          ),
        ],
      }
    );
    ```
    
- AWS Lambda
    公式ドキュメントはこちら
    [class NodejsFunction (construct)](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_lambda_nodejs.NodejsFunction.html#class-nodejsfunction-construct)
    ```typescript
        const lambda = new cdk.aws_lambda_nodejs.NodejsFunction(
      this,
      "main-handler",
      {
        // lambda 関数の実行環境を指定します。
        runtime: cdk.aws_lambda.Runtime.NODEJS_20_X,
        // lambda 関数のパスを指定します。
        // 今回は「qiita」ディレクトリ配下に「lambda」ディレクトリを作成しました。
        entry: "lambda/handler.ts",
        // 先ほど定義した IAM ロールです。
        role: executionLambdaRole,
        // lambda 関数に環境変数を設定します。
        // 以下に補足事項ありです。
        environment: {
          NOTION_AUTH: "qiita-notionAuth",
          NOTION_DB_ID: "qiita-notionDBId",
          SLACK_BOT_TOKEN: "qiita-slackBotToken",
          SLACK_CHANNEL_NAME: "qiita-channelName",
        },
        // lambda 関数の最大実行時間を設定します。
        // 今回は 30 秒を指定しました。（フィーリング）
        timeout: cdk.Duration.seconds(30),
      }
    );
    ```
    
    ::: note info
    lambda 関数の環境変数には `AWS Systems Manager Parameter Store` へ登録した認証情報に対応するパラメータ名称を指定しています。
    実際の認証情報は lambda 関数内で取得するようにしています。（後述）
:::

- AWS EventBridge
    公式ドキュメントはこちら
    [class Role (construct)](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_events.Rule.html#class-rule-construct)
    ```typescript
      new cdk.aws_events.Rule(this, "Schedule", {
        // 本イベントの発火ルールを指定します。
        // 今回は 1 分に一回実行するように指定しました。（迷惑🙄）
        schedule: cdk.aws_events.Schedule.rate(cdk.Duration.minutes(1)),
        // 当該イベントの発火時に実行される lambda 関数を指定します。
        targets: [new cdk.aws_events_targets.LambdaFunction(lambda)],
      });
    ```


## 10. `AWS Lambda` で実行する TypeScript ファイルを実装


実際に実行される処理を実装します。
ファイル全体は次の通りです。

*全部ベタ打ちで実装しているので、コードがかなり汚いです。。お手柔らかにお願いします。。*
*また、エラーハンドリング処理が雑いと思われます。*
*僕自身勉強中ゆえ、ご指摘等頂けますと幸いです。*

*12/08 更新：ちょっとだけリファクタリングしました*

各種公式ドキュメントはこちら。
- [GetParameterCommand (aws-sdk/client-ssm)](https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/client/ssm/command/GetParameterCommand/)
- [GetParametersCommand (aws-sdk/client-ssm)](https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/client/ssm/command/GetParametersCommand/)
- [Query a database (notion sdk)](https://developers.notion.com/reference/post-database-query)
- [Filter database entries (notion sdk)](https://developers.notion.com/reference/post-database-query-filter#status)
- [Web API (slack api)](https://slack.dev/node-slack-sdk/web-api)

```typescript:lambda/handler.ts
import {
  GetParameterCommand,
  GetParametersCommand,
  SSMClient,
} from "@aws-sdk/client-ssm";
import { Client } from "@notionhq/client";
import { WebClient } from "@slack/web-api";
import { Context, ScheduledEvent } from "aws-lambda";

const NOTION_AUTH_KEY = process.env["NOTION_AUTH"]!;
const NOTION_DB_ID_KEY = process.env["NOTION_DB_ID"]!;
const SLACK_BOT_TOKEN_KEY = process.env["SLACK_BOT_TOKEN"]!;
const SLACK_CHANNEL_NAME_KEY = process.env["SLACK_CHANNEL_NAME"]!;

// リージョンを指定して SSMClient をインスタンス化
const ssm = new SSMClient({ region: "ap-northeast-1" });

const getParametersFromSSM = async () => {
  // AWS SSM に対して認証情報（type: SecureString)を問い合わせ
  const secureStrResponse = await ssm.send(
    new GetParametersCommand({
      Names: [NOTION_AUTH_KEY, NOTION_DB_ID_KEY, SLACK_BOT_TOKEN_KEY],
      WithDecryption: true,
    })
  );

  // AWS SSM に対して slack のチャンネル名(type: String)を問い合わせ
  const strResponse = await ssm.send(
    new GetParameterCommand({
      Name: SLACK_CHANNEL_NAME_KEY,
      WithDecryption: false,
    })
  );

  return {
    notionAuth: secureStrResponse.Parameters?.find(
      (p) => p.Name === NOTION_AUTH_KEY
    )?.Value,

    notionDBId: secureStrResponse.Parameters?.find(
      (p) => p.Name === NOTION_DB_ID_KEY
    )?.Value,

    slackBotToken: secureStrResponse.Parameters?.find(
      (p) => p.Name === SLACK_BOT_TOKEN_KEY
    )?.Value,

    slackChannelName: strResponse.Parameter?.Value,
  };
};

export const handler = async (event: ScheduledEvent, context: Context) => {
  try {
    const { notionAuth, notionDBId, slackBotToken, slackChannelName } =
      await getParametersFromSSM();

    if (!notionAuth || !notionDBId || !slackBotToken || !slackChannelName) {
      throw new Error("必要な情報を全て取得できませんでした");
    }

    const notionClient = new Client({
      auth: notionAuth,
    });
    const slackClient = new WebClient(slackBotToken);

    // notionにクエリを実行します。
    // 今回はページプロパティが「期限：有」かつ「未実施」のものを検索するクエリを実行します。
    // 詳しい方法は上記公式ドキュメント参照です。
    const queryResult = await notionClient.databases.query({
      database_id: notionDBId,
      filter: {
        and: [
          {
            property: "期限",
            status: {
              equals: "有",
            },
          },
          {
            property: "done",
            checkbox: {
              equals: false,
            },
          },
        ],
      },
    });

    // クエリ結果からページ数を取得します。
    const pageCount = queryResult.results.length.toString();

    // 取得したページ数を slack に投稿します。
    // 今回はページ数のみ投稿しています。
    // 詳しい利用方法は上記公式ドキュメント参照です。
    await slackClient.chat.postMessage({
      text: pageCount,
      channel: slackChannelName!,
    });
  } catch (error: unknown) {
    if (error instanceof Error) {
      console.error("エラーが発生しました", error);
    }
  }
};

```


## 11. `AWS CDK` コマンドで AWS リソースをデプロイ


**1. Docker を起動します。**

**2. 「qiita」ディレクトリにて、下記コマンドを実行します。**
公式ドキュメントはこちら。
まだ一度も lambda 関数をデプロイしたことがない場合にのみ実行します。
[AWS 環境のブートストラップ](https://docs.aws.amazon.com/ja_jp/cdk/v2/guide/cli.html#cli-bootstrap)

```bash
cdk bootstrap
```

**3. 「qiita」ディレクトリにて、下記コマンドを実行します。**
公式ドキュメントはこちら。
以降、lambda 関数を修正した際は下記コマンドを実行し、修正を反映させます。
[スタックのデプロイ](https://docs.aws.amazon.com/ja_jp/cdk/v2/guide/cli.html#cli-deploy)

```bash
cdk deploy
```
ソースコード等を変更した際は、再度上記コマンドを実行します。


これで手順は終了です！！！！
うまくいけば、指定したチャンネルに 1 分間隔でメッセージが投稿されるはずです。（迷惑🙄)

*あ、テストの方法は、、書いてないです。。。*
*サボりました。。。*
*本当は `AWS SAM` を使ってローカルで lambda 関数をテストできるようにしたいので、それができたらまた記事にしようかな*


## 12. 利用料金等補足事項


AWS には無期限の無料利用枠というものがあり、この範囲内の利用なら課金されません。
詳細はこちら。
[AWS 無料利用枠 (常に無料)](https://aws.amazon.com/jp/free/?gclid=Cj0KCQiA67CrBhC1ARIsACKAa8Tn3JREPKQKpB3oaYRbF9B68F96ToXtbdrpFfROfE3HCaIzameTfWoaAhTgEALw_wcB&all-free-tier.sort-by=item.additionalFields.SortRank&all-free-tier.sort-order=asc&awsf.Free%20Tier%20Types=tier%23always-free&awsf.Free%20Tier%20Categories=*all&trk=ec4d2622-9132-4d67-996b-e37dbc3a40d7&sc_channel=ps&ef_id=Cj0KCQiA67CrBhC1ARIsACKAa8Tn3JREPKQKpB3oaYRbF9B68F96ToXtbdrpFfROfE3HCaIzameTfWoaAhTgEALw_wcB:G:s&s_kwcid=AL!4422!3!591672862988!p!!g!!aws%20%E3%82%AF%E3%83%A9%E3%82%A6%E3%83%89%E3%82%B5%E3%83%BC%E3%83%93%E3%82%B9)

僕は現在、「6時間に１回」・「1週間に2回」起動する lambda 関数をそれぞれ運用しておりますが、この枠内で収まっています。


# まとめ


長々とお付き合いいただきありがとうございました。
どうせ今後また同じことをやりたくなるだろうと思い、手順書作成の意味を込めて記事にしてみました。
もしこの記事が誰かの役に立てますと幸いです。