はじめに
こんにちは、クラウド事業部の清水(雄)です。
AWSの運用効率化にあたってChatOpsツールを開発しました。
「AWSコンソールを触ることなく」「チャットツールで運用作業を簡略化する」をコンセプトとしたツールです。
今回はSlack APIを用いて開発したのですが
Slack API は3秒以内にレスポンスを返さないと失敗扱いになります。
そこに至るまでの四苦八苦と攻略法について記載させて頂きます。
※ツール紹介の記事はこちら↓
techblog.ap-com.co.jp
※ツールダウンロードリンクはこちら↓
www.ap-com.co.jp
目次
何故気づいた?
当初こんな構成で作ってみて
「lambdaは15分以内であれば処理が可能だし、そもそもIAMユーザ作成ってすぐ終わるっしょ!」って思って実行した所、、
玉砕!
でも実はAWS側(IAMユーザ作成)の処理は成功していたんです。
理由
Slackの公式(翻訳)にはこんな記載が、、、
元のリクエストが送信されてから 3000 ミリ秒以内に Slack によって受信される必要があります。
受信しないと、operation_timeout エラーがユーザーに表示されます。
リクエストのペイロードを検証できなかった場合、アプリは代わりにエラーを返し、リクエストを無視する必要があります。
つまり「3秒以内にStatus code 200を返さないといけません。」
※該当ページははこちら↓
api.slack.com
ChatGPTが教えてくれた解決策
ChatGPTに聞くとこんな回答が
「たかがIAMユーザ作成で3秒以内に返せないの」とも思ったのですが
よく考えてみると
1. Slackを開いているクライアント側のNWの速度が遅かった場合
2. AWS CLIもレスポンスが遅い時があったり、、
など3秒以内に返せない理由はいくつか考えられた為
今回はChatGPTが提示した「3. 非同期処理」を実装することにしました。
攻略法:非同期処理の実装
こんな構成で作り直しました。
それぞれのfunctionはこんな感じ(Python3)
※それぞれ簡略版です。これだけでは動かないので試したい場合はダウンロードを!
www.ap-com.co.jp
↓function1
※正式版にはSlackAPIのクレデンシャルや引数チェックが含まれています。
import json import boto3 from urllib.parse import parse_qs import datetime import os def lambda_handler(event, context): # 別のLambda関数に渡すパラメータを作成する params = parse_qs(event['body']) text = params['text'][0] payload = {'text': params['text'][0]} #Slackレスポンスする内容の定義 words = [e for e in text.split(' ')] user_name = words[0] group_name = words[1] password = words[2] # 別のLambda関数を非同期で呼び出す client = boto3.client('lambda') function_name = 'slack-iam-user-create-excute-handler' invocation_type = 'Event' response = client.invoke( FunctionName=function_name, InvocationType=invocation_type, Payload=json.dumps(payload) ) return { "statusCode": 200, "headers": { "Content-Type": "application/json" }, "body": json.dumps(payload) }
↓function2 ※正式版にはポリシーアタッチやグループ追加、クレデンシャル発行も含まれてます。
import boto3 def create_iam_user(user_name): """ IAMユーザーを作成する """ client = boto3.client('iam') response = client.create_user( UserName=user_name ) return response def lambda_handler(event, context): text = event['text'] words = [e for e in text.split(' ')] user_name = words[0] password = words[2] sts_client = boto3.client('sts') account_id = sts_client.get_caller_identity()['Account'] policy_arn = f"arn:aws:iam::{account_id}:policy/Customer_IAM_MFA_Policy" # IAMユーザーの作成 create_user_response = create_iam_user(user_name) print("IAMユーザーが作成されました:", create_user_response) return { 'statusCode': 200, 'body': 'IAMユーザーが作成されました' }
関数の呼び出しでresponseに必要な値を代入してあげるだけでいけました。
別関数に値を渡す際はdict形式で渡さないといけないようです。
おわりに
非同期処理といえばSQSって思ってたのですが
案外functionからfunctionをキックするだけでよかったでのシンプルな構成となりました。
引数を別functionへ渡す処理を初めて書いたので勉強になりました!
3秒問題いつかAPI側でゆるくなるといいなぁ
お知らせ
APCはAWSセレクトティアサービスパートナー認定を受けております。
その中で私達クラウド事業部はAWSなどのクラウド技術を活用したSI/SESのご支援をしております。
https://www.ap-com.co.jp/service/utilize-aws/
また、一緒に働いていただける仲間も募集中です!
今年もまだまだ組織規模拡大中なので、ご興味持っていただけましたらぜひお声がけください。