おぎろぐはてブロ

なんだかんだエンジニアになって10年以上

Oktaのアプリケーション割り当て失敗をAmazon EventBridgeでSlackに通知する

Oktaのアプリケーションの割り当て処理が失敗したときにSlack通知を行い気付きやすくしました。 Okta logをAmazon EventBridgeにストリーミングし、EventBridgeから直接Slackにメッセージを送ります。

課題

Oktaでアプリケーションをアサインした後、OktaからSaaSに対してライセンス割り当ての処理が行われますが、SaaS側の問題で失敗している時があります。 アサインして即時エラーが出るわけではないのですぐに気づけないことがあります。 タスクには一覧表示されるけど常に見ているわけもなく。

失敗する理由としては、SaaS側でライセンスが不足している場合だったり、Microsoftの仕様でTeamsの試用版をセルフで開始している場合などがあります。

割り当て失敗のログ

失敗した時には、イベントログに記録されます。以下の条件で検索できます。

eventType eq "application.provision.user.push_profile" and outcome.result eq "FAILURE"

システムログ

イベントログをトリガーに処理を行いたい

できれば、ノーコードで処理したいですよね。 Okta Workflowトリガーあるかなと思ったけど無く、イベントフックという、イベントが発生した時に外部のURLに通知する機能もあるのですが、これも割り当て失敗イベントに対応していません。(Event Typesのうち event-hook-eligible と書いてあるものが対象)

ということで、イベントログを外部に連携するログストリーミングを利用します。

ログストリーミング

help.okta.com

ログストリーミングは外部にログを連携する仕組みですが、対応しているサービスが少なく、現状以下の2つのみです。

  • Amazon EventBridge
  • Splunk Cloud

Splunk Cloudは使っていなかったのでのEventBridgeを選択します。

Amazon EventBridgeへの接続

docs.aws.amazon.com

Amazon EventBridgeは、イベントを使用してアプリケーションコンポーネントを接続できるサーバーレスサービスです。これにより、スケーラブルなイベント駆動型アプリケーションを簡単に構築できます。イベント駆動型アーキテクチャとは、イベントの発信と応答によって連携する、ゆるやかに結合されたソフトウェアシステムを構築するスタイルです。イベント駆動型アーキテクチャは、俊敏性を高め、信頼性が高くスケーラブルなアプリケーションを構築するのに役立ちます。

→ 要するに、イベントのストリームを受け取って、それをフィルタして必要なイベントを抽出して、何か処理をしたりすることができるサービス。

AWS自体のイベント (例えばインスタンスの起動停止とか) は当然ながら、パートナーイベントソースというのがあって、他のSaaSのイベントを受け取って処理をすることができる。

他SaaSのイベントをトリガにAWSで何か処理をするとか、単にログを保存するといったことに便利。

Amazon EventBridgeのパートナーイベントソース

Okta側で送信先のAWSアカウントの設定をして、EventBridge側で受け取りの処理をすると接続完了

OktaのEventBridge構成

イベントの受け取り

連携したデータはイベントバスというものに流れてきて、ここに対してルールを設定して、欲しいイベントを受け取ることができる。

流れてくるデータはこんな感じで

{
  "version": "0",
  "id": "...",
  "detail-type": "SystemLog",
  "source": "aws.partner/okta.com/tenant/okta",
  "account": "...",
  "time": "2024-12-16T03:38:37Z",
  "region": "ap-northeast-1",
  "resources": [],
  "detail": {
    "actor": {
      "id": "...",
      "type": "User",
      "alternateId": "actor@example.jp",
      "displayName": "Actor",
      "detailEntry": null
    },
    ...
    "displayMessage": "Push user's profile to external application",
    "eventType": "application.provision.user.push_profile",
    "outcome": {
      "result": "FAILURE",
      "reason": null
    },
    "published": "2024-12-16T03:38:37.672Z",
    "securityContext": {
      "asNumber": null,
      "asOrg": null,
      "isp": null,
      "domain": null,
      "isProxy": null
    },
    "severity": "ERROR",
    "legacyEventType": "app.user_management.push_profile_failure",
    ...
    "uuid": "...",
    "version": "0",
    "request": {
      "ipChain": []
    },
    "target": [
      {
        "id": "...",
        "type": "AppUser",
        "alternateId": "mail@example.jp",
        "displayName": "Name",
        "detailEntry": null
      },
      {
        "id": "...",
        "type": "User",
        "alternateId": "mail@example.jp",
        "displayName": "Name",
        "detailEntry": null
      },
      {
        "id": "...",
        "type": "AppInstance",
        "alternateId": "Microsoft",
        "displayName": "Microsoft Office 365",
        "detailEntry": null
      }
    ]
  }
}

イベントパターンというもので対象を抽出する

{
  "source": [{
    "prefix": "aws.partner/okta.com"
  }],
  "detail": {
    "eventType": ["application.provision.user.push_profile"],
    "outcome": {
      "result": ["FAILURE"]
    }
  }
}

Slackへの送信

EventBridgeはAWSサービスの呼び出しの他、外部APIを呼び出すことができるので、直接Slack APIを呼び出すことにします。 API接続でパートナーテンプレートというのがありSlackもパートナーとして存在するのですが、ドキュメントがまともになくどう接続するかさっぱりわからなかったので、使うのは諦めました。

docs.aws.amazon.com

APIを呼び出すときのpayloadを作るのに入力トランスフォーマーというものを使って加工します。

変数を入力パスというので定義する。JSONから欲しいデータをとってくる

{
  "actor": "$.detail.actor.alternateId",
  "app": "$.detail.target[2].displayName",
  "app_id": "$.detail.target[2].id",
  "app_user": "$.detail.target[0].alternateId",
  "user": "$.detail.target[1].displayName",
  "user_id": "$.detail.target[1].id"
}

入力テンプレートをこんな感じで定義しました。Slackに送るのでチャンネル指定して、雑にblockを定義する感じです。

{
  "channel": "[channel id]",
  "blocks": [{
      "type": "section",
      "text": {
        "type": "mrkdwn",
        "text": "<!subteam^[group_id]> <actor> が実施したアプリケーションの割り当てが失敗しました。 <https://tenant-admin.okta.com/admin/tasks|Task> を確認して解消してください。\n\n- User: <https://tenant.okta.com/admin/user/profile/view/<user_id>|<user>>\n- App: <https://tenant-admin.okta.com/admin/app/app_name/instance/<app_id>|<app>>"
      }
  }]
  }

送信先を作成して、エンドポイントをSlackの chat.postMessage に、メソッドをPOST、認証としてAuthorizationヘッダにSlackアプリを作成して、トークンを取得して Bearer <bot token> と指定する

API送信先の設定

できた

こんな感じで通知されます Actor (アプリケーション割り当てを実施した人) をメンションしています。

Slackメッセージ