0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

AWS Step Functionsを使用してAWS Health イベントをNotionのデータベースに記録してみた

0
Posted at

AWS Health イベントを何かしらのデータベースで管理されている方は多いと思います。
今回はイベントが発生したらNotionのデータベースに登録する仕組みをAWS Step Functionsで実装してみました。

やること

以下の構成図の通りですが、AWS Health イベントをAmazon EventBridgeで検知してStep Functions経由でNotionのデータベースに登録を行います。
NotionのAPIキーはSSM Parameter Storeに登録します。
stepfunctions.drawio.png

設定

Notionにデータベースを作る

以下のようなデータベースを一つ作成してみました。
image.png

内容は以下のようになっています。

名前 ステータス 担当者 通知日付 期日
タイトル ステータス ユーザー 日付 日付

Notion API設定

コネクトの作成を行ってください。
image.png

コネクト作成後、アクセストークンをコピーした後に、右上の「・・・」から接続をクリックして作成したコネクトを選択します。
image.png

アクセストークンを使用して以下のcurlコマンドでデータベースの取得とデータの登録をしてみます。
ユーザーIDは適当なページを一つ作成してそこにメンションをつけてメッセージを取得すれば確認ができます。(ほかにスマートなやり方がある気がしますが...)

# データベース取得
curl --request GET \
  --url https://api.notion.com/v1/databases/データベースID \
  --header 'Authorization: Bearer トークン' \
  --header 'Notion-Version: 2026-03-11'

# データベースにデータ追加
curl -X POST "https://api.notion.com/v1/pages" \
  -H "Authorization: Bearer トークン" \
  -H "Notion-Version: 2026-03-11" \
  -H "Content-Type: application/json" \
  --data '{
    "parent": {
      "type": "data_source_id",
      "data_source_id": "'"データソースID (上記のcurlのレスポンスに含まれています)"'"
    },
    "properties": {
      "名前": {
        "title": [
          {
            "type": "text",
            "text": {
              "content": "APIから登録したタスク"
            }
          }
        ]
      },
      "ステータス": {
        "status": {
          "name": "未着手"
        }
      },
      "通知日付": {
        "date": {
          "start": "2026-06-26"
        }
      },
      "期日": {
        "date": {
          "start": "2026-06-27"
        }
      }
    }
  }'

# ページ内にメッセージを登録する
curl -X PATCH "https://api.notion.com/v1/blocks/ページID (上記コマンドのレスポンスに含まれています)/children" \
  -H "Authorization: Bearer トークン" \
  -H "Notion-Version: 2026-03-11" \
  -H "Content-Type: application/json" \
  --data '{
    "children": [
      {
        "object": "block",
        "type": "paragraph",
        "paragraph": {
          "rich_text": [
            {
              "type": "text",
              "text": {
                "content": "テストメッセージ"
              }
            }
          ]
        }
      }
    ]
  }'

# ユーザーID確認 (適当なページで自分にメンションをした後に以下のコマンドを実行してレスポンスに含まれるmentionからIDが確認できます)
curl --request GET \
    --url "https://api.notion.com/v1/blocks/ページID/children" \
    --header 'Authorization: Bearer トークン' \
    --header 'Notion-Version: 2026-03-11'

上記の実行に成功すると以下のようにデータベースに登録されていることが確認できます。

image.png

image.png

AWSリソース作成

API経由での登録ができたらAWSリソースの作成を行っていきます。
今回はkiro-cliを使用してTerraformで作成しました。
Terraformのコードは以下に配置しています。

中身は以下のようになっており、必要なものだけを最低限作成しています。

.
├── README.md
├── eventbridge.tf
├── iam.tf
├── outputs.tf
├── ssm.tf
├── step_functions.tf
├── terraform.tfvars
├── variables.tf
└── versions.tf

eventbridge.tfではEventBridgeルールを作成しており、AWSHealthイベントをすべてキャッチするように以下のルールを設定しています。
ちなみにイベントスキーマのサンプルは以下のドキュメントに記載されています。

{
  "detail-type": ["AWS Health Event"],
  "source": ["aws.health"]
}

ssm.tfではデータソースIDやNotionのトークンなどを登録するパラメータストアを作成しています。
トークンだけSecureStringにしました。

locals {
  ssm_prefix = trimprefix(var.prefix, "aws-")
}

resource "aws_ssm_parameter" "notion_api_token" {
  name        = "/${local.ssm_prefix}/notion-api-token"
  description = "Notion API integration token"
  type        = "SecureString"
  value       = var.notion_api_token
}

resource "aws_ssm_parameter" "notion_datasource_id" {
  name        = "/${local.ssm_prefix}/notion-datasource-id"
  description = "Notion datasource ID for health events"
  type        = "String"
  value       = var.notion_datasource_id
}

resource "aws_ssm_parameter" "notion_user_id" {
  name        = "/${local.ssm_prefix}/notion-user-id"
  description = "Notion user ID to mention in page content"
  type        = "String"
  value       = var.notion_user_id
}

step_functions.tfではStepFunctionsを作成しており、ステップとしては4つになります。
1つ目と2つ目がSSMパラメータストアからNotion API実行に必要なID (データソースID、ユーザーID) をとってきています。
3つ目が、Notionのページを作成しており、ここでNotionの各プロパティを入力しています。
4つ目が、3つ目で作成したページ内にAWS HealthイベントのJSONを登録する部分です。
3~4についてはEventBridgeのEventBridge Connectionを使用してNotionのAPIを実行しています。

# EventBridge Connection for Notion API auth (API Key: Bearer token)
resource "aws_cloudwatch_event_connection" "notion" {
  name               = "${var.prefix}-notion-connection"
  authorization_type = "API_KEY"

  auth_parameters {
    api_key {
      key   = "Authorization"
      value = "Bearer ${var.notion_api_token}"
    }
  }
}

resource "aws_cloudwatch_log_group" "sfn" {
  name              = "/aws/states/${var.prefix}"
  retention_in_days = 14
}

resource "aws_sfn_state_machine" "health_to_notion" {
  name     = "${var.prefix}-state-machine"
  role_arn = aws_iam_role.sfn.arn

  definition = jsonencode({
    Comment       = "Record AWS Health events to Notion datasource"
    QueryLanguage = "JSONata"
    StartAt       = "GetNotionDataSourceId"
    States = {
      GetNotionDataSourceId = {
        Type     = "Task"
        Resource = "arn:aws:states:::aws-sdk:ssm:getParameter"
        Arguments = {
          Name           = aws_ssm_parameter.notion_datasource_id.name
          WithDecryption = false
        }
        Output = {
          "data_source_id" = "{% $states.result.Parameter.Value %}"
          "event"       = "{% $states.input %}"
        }
        Next = "GetNotionUserId"
      }
      GetNotionUserId = {
        Type     = "Task"
        Resource = "arn:aws:states:::aws-sdk:ssm:getParameter"
        Arguments = {
          Name           = aws_ssm_parameter.notion_user_id.name
          WithDecryption = false
        }
        Output = {
          "data_source_id" = "{% $states.input.data_source_id %}"
          "user_id"     = "{% $states.result.Parameter.Value %}"
          "event"       = "{% $states.input.event %}"
        }
        Next = "CreateNotionPage"
      }
      CreateNotionPage = {
        Type     = "Task"
        Resource = "arn:aws:states:::http:invoke"
        Arguments = {
          ApiEndpoint = "https://api.notion.com/v1/pages"
          Method      = "POST"
          Authentication = {
            ConnectionArn = aws_cloudwatch_event_connection.notion.arn
          }
          Headers = {
            "Content-Type"   = "application/json"
            "Notion-Version" = "2026-03-11"
          }
          RequestBody = {
            parent = {
              type           = "data_source_id"
              "data_source_id" = "{% $states.input.data_source_id %}"
            }
            properties = {
              "名前" = {
                title = [{
                  type = "text"
                  text = {
                    "content" = "{% '[' & $states.input.event.detail.service & '] ' & $states.input.event.detail.eventTypeCode %}"
                  }
                }]
              }
              "通知日付" = {
                date = {
                  "start" = "{% $states.input.event.time %}"
                }
              }
              "ステータス" = {
                status = {
                  name = "未着手"
                }
              }
              "担当者" = {
                people = "{% $parse($states.input.user_id) %}"
              }
            }
          }
        }
        Output = {
          "page_id"    = "{% $states.result.ResponseBody.id %}"
          "data_source_id" = "{% $states.input.data_source_id %}"
          "user_id"    = "{% $states.input.user_id %}"
          "event"      = "{% $states.input.event %}"
        }
        Next = "AddNotionPageContent"
        Retry = [{
          ErrorEquals     = [
            "States.HTTPErrorStatusCode500",
            "States.HTTPErrorStatusCode502",
            "States.HTTPErrorStatusCode503",
            "States.HTTPErrorStatusCode504",
            "States.HTTPErrorStatusCode429",
          ]
          IntervalSeconds = 5
          MaxAttempts     = 3
          BackoffRate     = 2
          JitterStrategy  = "FULL"
        }]
      }
      AddNotionPageContent = {
        Type     = "Task"
        Resource = "arn:aws:states:::http:invoke"
        Arguments = {
          ApiEndpoint = "{% 'https://api.notion.com/v1/blocks/' & $states.input.page_id & '/children' %}"
          Method      = "PATCH"
          Authentication = {
            ConnectionArn = aws_cloudwatch_event_connection.notion.arn
          }
          Headers = {
            "Content-Type"   = "application/json"
            "Notion-Version" = "2026-03-11"
          }
          RequestBody = {
            children = [{
              object = "block"
              type   = "code"
              code = {
                language  = "json"
                rich_text = [{
                  type = "text"
                  text = {
                    "content" = "{% $string($states.input.event, true) %}"
                  }
                }]
              }
            }]
          }
        }
        End = true
        Retry = [{
          ErrorEquals     = [
            "States.HTTPErrorStatusCode500",
            "States.HTTPErrorStatusCode502",
            "States.HTTPErrorStatusCode503",
            "States.HTTPErrorStatusCode504",
            "States.HTTPErrorStatusCode429",
          ]
          IntervalSeconds = 5
          MaxAttempts     = 3
          BackoffRate     = 2
          JitterStrategy  = "FULL"
        }]
      }
    }
  })

  logging_configuration {
    log_destination        = "${aws_cloudwatch_log_group.sfn.arn}:*"
    include_execution_data = true
    level                  = "ERROR"
  }
}

terraform.tfvarsは以下のようにしてファイル保存してください。
notion_user_idは複数人登録できるようにリスト構造にしています。

notion_api_token = "トークン"
notion_datasource_id = "データソースID"
notion_user_id = "[{\"id\":\"ユーザーID\"}]"

Terraformコードの準備ができたら以下のコマンドでデプロイしてください。

terraform init
terraform apply

テスト

AWSリソースの作成が完了したら、Step Functionsの画面からサンプルイベントでテスト実行してみます。
AWSサポートの有料プランを契約している場合、Healthイベントのサンプル実行を依頼することができるのですが、今回は手短に確認したかったので、とりあえずStep Functionsの画面から実行しています。

サンプルイベントはAWSのドキュメントに記載されているJSONを使ってみます。

{
    "version": "0",
    "id": "7bf73129-1428-4cd3-a780-95db273d1602",
    "detail-type": "AWS Health Event",
    "source": "aws.health",
    "account": "123456789012",
    "time": "2023-01-27T09:01:22Z",
    "region": "af-south-1",
    "resources": [],
    "detail": {
        "eventArn": "arn:aws:health:af-south-1::event/EC2/AWS_EC2_OPERATIONAL_ISSUE/AWS_EC2_OPERATIONAL_ISSUE_7f35c8ae-af1f-54e6-a526-d0179ed6d68f",
        "service": "EC2",
        "eventTypeCode": "AWS_EC2_OPERATIONAL_ISSUE",
        "eventTypeCategory": "issue",
        "eventScopeCode": "PUBLIC",
        "communicationId": "01b0993207d81a09dcd552ebd1e633e36cf1f09a-1",
        "startTime": "Fri, 27 Jan 2023 06:02:51 GMT",
        "endTime": "Fri, 27 Jan 2023 09:01:22 GMT",
        "lastUpdatedTime": "Fri, 27 Jan 2023 09:01:22 GMT",
        "statusCode": "open",
        "eventRegion": "af-south-1",
        "eventDescription": [{
            "language": "en_US",
            "latestDescription": "Current severity level: Operating normally\n\n[RESOLVED] \n\n [03:15 PM PST] We continue see recovery \n\nThe following AWS services were previously impacted but are now operating normally: APPSYNC, BACKUP, EVENTS."
        }],
        "affectedEntities": [],
        "page": "1",
        "totalPages": "1",
        "backupEvent": "false",
        "affectedAccount": "123456789012",
        "personas": ["OPERATIONS"]
    }
}

該当のステートマシンから実行を開始をクリックしてください。
スクリーンショット 2026-06-27 152349.png

クリックしたら「入力 - オプション」に上記のJSONをペーストして実行を開始をクリックしてください。
スクリーンショット 2026-06-27 152648.png

実行が正常に完了すると以下のようにNotionのデータベースに登録されていることが確認できます。
image.png
image.png

さいごに

最初はLambdaで作ろうと思っていたのですが、とりあえずNotionに登録するだけであればStep FunctionsだけでもAPI実行できるのでLambdaいらないのでは?というところから始めてみました。
実際メッセージの細かい編集などが無い今回のようなものであればStep Functionsのみで十分かなといった印象です。
Lambdaだと自由度は高いですが、定期的なランタイム更新などが発生するので通知するだけの目的で使うのはちょっと面倒なのかなと思いました。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?