この記事はシスコの有志による Cisco Systems Japan Advent Calendar 2022の一つとして投稿しています。
2022年版: https://qiita.com/advent-calendar/2022/cisco
以下、昨年までのものです。
2021年版(1枚目): https://qiita.com/advent-calendar/2021/cisco
2021年版(2枚目): https://qiita.com/advent-calendar/2021/cisco2
2020年版(1枚目): https://qiita.com/advent-calendar/2020/cisco
2020年版(2枚目): https://qiita.com/advent-calendar/2020/cisco2
2019年版: https://qiita.com/advent-calendar/2019/cisco
2018年版: https://qiita.com/advent-calendar/2018/cisco
2017年版: https://qiita.com/advent-calendar/2017/cisco
やったこと
ワークフローを開始すると、承認を求めるメッセージがWebex Messagingに届き、Wait For Event
のブロックでワークフローがいったん停止します。承認することでワークフローが再開し、後続する処理が実行されます。
モチベーション
既存のサンプルだとSecureXのWebインターフェイス上で承認するものがあります。
承認者にいちいちSecureXにログインしてもらうのも何なんでチャットで承認できる方法を考えてみました。
ワークフローの全体像
- Create Approval Request
- HTTP Request
- Wait For Event
- Completed
以下、SecureXのワークフローだけでなくWebexクラウドや外部Webサービスとの連携も加えて、順をおって解説します。
1. Create Approval Request
ワークフローを開始するとSecureX上に承認リクエストが作成されます。リクエスト毎にランダムなTask IDが生成されます。各サービス間で連携する際にこのTask IDを引き回すことで、どのリクエストへの承認もしくは拒否がなされたのかの判断できるようにします。
2と3で行われる処理の全体像
① HTTPリクエストが呼ばれるとWebex API経由でWebex MessagingクライアントにAdaptive Cards形式のメッセージが送信されます。
② 承認者がWebex Messagingクライアント上で「承認する」もしくは「拒否する」のボタンを押すと、WebexクラウドからWebhookが呼ばれます。
③ 承認者によるアクションの詳細を得るためにWebhookの中のIDをみてWebex API経由でアクションの詳細を取得します。
④ SecureXのワークフローが承認待ちで停止状態なので、Wait For Event
に紐づいているSecureX Webhookを呼びだし、ワークフローを再開させます。
⑤ 最後に、承認者をメンションして承認したのかもしくは拒否したのかのメッセージをスレッドに残します。
Webexの前準備
Webex MessagingのAPIを通じて、チャットベースの承認リクエストを送ったり、承認者から承認もしくは拒否のアクションを得ます。
そのためWebex MessagingのBotとWebhookを作成しておきます。
WebhookはBotのWebex Tokenで作成します。
Webex MessagingにおけるBotとWebhookの作成には以下の記事をご参考にしてください。
Webhookのパラメータは以下のとおりです。
name: Webhook for attachment actions(任意)
targetUrl: https://{my_app}.{my_subdomain}.workers.dev
resourace: attachmentActions
event: created
2. HTTP Request
主な設定項目は以下のとおりです。
Target
Target
Webex Teams
HTTP Request
Relative Url
/v1/messages
Method
POST
Request Body
roomId
はWebexのルームIDで変数で渡してあげます。
どういったリクエストへの承認なのか分かるようにCreate Approval Request
で生成されたTask IDをdata
に埋め込んでおきます。
{
"roomId": "<webex-roomId>",
"text": "Give me your approval",
"attachments": [
{
"contentType": "application/vnd.microsoft.card.adaptive",
"content": {
"type": "AdaptiveCard",
"$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
"version": "1.2",
"body": [
{
"type": "Image",
"url": "https://4.bp.blogspot.com/-gKPdnJWscyI/VCIkF3Po4DI/AAAAAAAAmjo/fAKkTMyf8hM/s150/monster01.png",
"horizontalAlignment": "Center",
"width": "150px",
"height": "150px"
},
{
"type": "Container",
"style": "emphasis",
"items": [
{
"type": "TextBlock",
"wrap": true,
"text": "承認欲求モンスターが承認を求めています",
"spacing": "None",
"size": "Large",
"weight": "Bolder",
"color": "Accent",
"isSubtle": false
}
]
},
{
"type": "ActionSet",
"actions": [
{
"type": "Action.Submit",
"title": "承認する",
"id": "1",
"data": {
"taskId": "<task-id>",
"action": "Approved"
}
},
{
"type": "Action.Submit",
"title": "拒否する",
"id": "2",
"data": {
"taskId": "<task-id>",
"action": "Rejected"
}
}
]
}
]
}
}
]
}
Headers
Content Type
Application JSON
Custom headers
HEADER: Authorization
VALUE: Bearer <webex-bot-token>
Webex MessagingでAdaptive Cards形式のメッセージを送ったり、カードへのアクションを取得する方法については、以下のサイトを参考にしてください。
[Webex公式] Buttons and Cards
https://developer.webex.com/docs/api/guides/cards
WebexからのWebhookを受けるWebサービスの作成
Adaptive Cards形式のメッセージにボタンを押した際にWebhookが呼ばれます。
そのWebhookを受けるのに今回はCloudflare Workersを使いました。
インターネット上にあってWebex Cloudから到達性があれば、AWS Lambdaとかで用意しても構いません。
Cloudflare Workersの使い方は以下の投稿を参照ください。
Cloudflare Workersで動作させるアプリケーションの雛型を作成します。
$ wrangler generate securex-approval
$ cd securex-approval
Webhookが呼ばれたときの処理をindex.js
に編集します。
Webexクラウドから呼び出されるWebhookのリクエストBodyからIDを取得して、Webex API経由でアクションの詳細を取得します。
Task IDと承認もしくは拒否が分かりますので、以下のようにその内容を埋めて、今度はSecureXのWebhookを呼び出します。
{
"taskId": "021RPLBIJIKPO1mfxQsiRySzeViIdnstv6p",
"action":"Approved"
}
最後に、承認者をメンションして承認したのかもしくは拒否したのかをスレッドに残します。
addEventListener('fetch', event => {
event.respondWith(handleRequest(event.request))
})
/**
* Respond with hello worker text
* @param {Request} request
*/
async function handleRequest(request) {
const body = await request.json();
const resp = await fetch(`https://webexapis.com/v1/attachment/actions/${body.data.id}`, {
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${WEBEX_TOKEN}`
}
})
const data = await resp.json()
console.log(data)
// call securex webhook
await fetch(SECUREX_WEBHOOK, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ taskId: data.inputs.taskId, action: data.inputs.action }),
})
await fetch('https://webexapis.com/v1/messages/', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${WEBEX_TOKEN}`
},
body: JSON.stringify({ parentId: data.messageId ,roomId: data.roomId, markdown: `<@personId:${body.data.personId}> You ${data.inputs.action}!` }),
})
return new Response('{}', {
headers: { 'content-type': 'application/json' },
})
}
Webex Bot Tokenをシークレット変数として登録します。
$ wrangler secret put WEBEX_TOKEN
SecureX Webhookをシークレット変数として登録します。
$ wrangler secret put SECUREX_WEBHOOK
アプリケーションを公開します。
$ wrangler publish
SecureX Webhookの作成
SecureXオーケストレーションのトップ画面に戻り、左ナビのEvents & Webhooks
からWebhook
タブに遷移して、New Webhook
ボタンをクリックします。
下のようなWebhookの作成画面がでてきたら、Display Name
を入力して、右下のSubmit
ボタンをクリックします
3. Wait For Event
主な設定項目は以下のとおりです。
Event Info
Event Type
直前に作成したWebhookを指定
Conditions
Cloudflare Workers上のWebサービスからのSecureXのWebhookへのリクエストBodyにTask IDと承認(Approved)もしくは拒否(Rejected)の情報が含まれるので、Task IDと承認(Approved)の文字列が正規表現マッチすることを条件にします。
4. Completed
本来であれば、承認後になんらかの処理を実行するはずですが、今回の例では何もせずにワークフローを終了させてます。
課題
面倒くさいので 過度に複雑な実装にするより分かりやすさを優先して以下の点は妥協しました。
-
Create Approal Taskを使ったため、SecureX上にもタスクができてしまいます。仮にSecureX上で承認してもワークフローは再開しません。
-
承認リクエストがタイムアウトした後もチャットから承認できてしまいます。この場合もワークフローは再開しません。
-
いったん拒否した後に承認してもワークフローが再開します。
おしまい