LoginSignup
17
22

More than 5 years have passed since last update.

【Slack de AWS CLI】SlackのChannelからAWS CLIを実行できるようにしてみました!

Posted at

はじめに

 去年Amazon API Gatewayを触り始めてから、Slack、API Gateway、Lambda、EC2 Run commandを使って、EC2に対してSlackからシェルコマンドを送るようにやってみたことがあります。その後、SlackのChannelにAWS CLIを送ったら、Lambdaが実行してくれるといいなと思っていました。
 LambdaでAWS CLIを実行する方法が既に知っていましたが、ずっとやれていなくて(最近仕事中に全然DeepDiveできなくて)、今回せっかく三連休ですので、SlackのChannelにAWS CLIを送って、Lambdaが実行するようにしてみました!
 Qiita上には、既にSlackからAWS CLIを実行する投稿がありましたが、個人的もう少し簡単に実現したいので、自分のやり方でやりました。
 今回は自分なりのやり方を皆さんに共有します。LambdaのコードはGitHubに上げておきました。

概要

利用するサービス

  1. Slack + Slash Command
  2. Amazon API Gateway
  3. Lambda( python + AWS CLI )

全体像

 何をどのようにやったかを簡単に説明しますと、

 Slash CommandでSlackのChannelにAWS CLIのコマンド(message)を送ったら、
 そのAWS CLIのコマンドがAPI Gateway経由でLambdaに送られ、
 Lambdaが送信されたAWS CLIのコマンドを実行して、実行結果をSlackに返す

 ということです。
 全体像は図に示す通りです。
 (なぜ、2つのLambdaを利用している理由はあとで説明します。)
スクリーンショット 0028-07-16 19.11.50.png

何が嬉しいのか

 そもそも何でSlackのChannelからAWS CLIを実行できるようにしたいのかと言いますと、正直に言って、特に明確な目的がありません.......が、こんな嬉しいことがあると思っています。

  1. ローカル環境でAWS CLIを叩かなくてもいい
    • Credencial情報をローカル環境に置かなくてもいいので、漏れる心配もない
  2. 複数メンバーがいる場合、AWS CLI実行環境を統一できる(かもしれません)
  3. PCであろうがスマホーであろうがタブレットであろうが、Slackがインストールされていれば、AWS CLIを使えます。まさに、Multi Device対応(凄くない?!)

おさえておきたいこと

 下記4点を是非皆さんにおさえておきたいです。

  • ① Slash CommandsのTimeout時間は3000ms
     一連の処理(特にLambda上のコマンド実行)が少し時間がかかりますので、Slash CommandsがTimeoutしないように、Front Lambdaで一旦Slackからの情報(AWS CLIのコマンドを含め)を受け取り、Slackに返信すると同時に、受け取った情報をExecution Lambdaに渡すようにしています。これが、2つのLambdaを利用している理由です。

  • ② Slackから送信される情報
    以下の情報がSlackからAPI Gatewayに送られますが、注意点2点あります.

    1. textの内容にspaceが入っている場合,space+ に置き換えられます
    2. response_urlエンコードされますので、Lambdaでデコードする必要があります
token=3mVXdAC5EFNbsXYGNk0sah3M
team_id=T0001
team_domain=example
channel_id=C2147483705
channel_name=test
user_id=U2147483697
user_name=Steve
command=/weather
text=94070
response_url=https://hooks.slack.com/commands/1234/5678
  • ③ API Gatewayでデータマッピング
    Slackから送られてきた情報(文字列)をJSONにマッピングする必要があります。VTLという言語を使っているそうです。
    マップピング用のテンプレートは以下になります。
## &区切りでPOSTされてきたデータを分割する
#set($httpPost = $input.path('$').split("&"))

## 出力されるJSONデータの生成
{
#foreach( $keyValue in $httpPost )
 ## "key=value"というデータをkeyとvalueに分ける
 #set($data = $keyValue.split("="))
 ## JSONのフォーマットに整形する(まだ走査するデータがあれば","を挿入する)
 "$data[0]" : "$data[1]"#if( $foreach.hasNext ),#end
#end
}
  • Lambda上のAWS CLI環境
    デフォルトのLambdaの環境では、AWS CLIが入っていないので、AWS CLIをパッケージ化して、Lambdaにアップロードする必要があります。
    やり方は【AWS Lambda Python で AWS CLI を実行する方法】にてご確認ください。

設定手順

 さって、さっそくSlackからAWS CLIを実行できるように設定していきましょう!特に難しいことはやっていませんので、ご安心ください!

Lambda

 今回権限まわりをちゃんと調査していないので、LambdaのRoleにPowerUserAccessをつけてあります。(自分はIAMにまだ弱いです)

Front Lambda

 詳細な作成手順は省きますが、1点だけ説明します。
 Front Lambdaの役割は受付(情報を受け取って、Execution Lambdaに渡す)だけですので、利用するRoleは最小権限で良いと思っています。
 Front Lambdaのコードは下記の通りです。

lambda_function.py
# -*- coding: utf-8 -*-
from __future__ import print_function
import boto3
import json

print('Loading function')

def lambda_handler(event, context):
    print("Received event: " + json.dumps(event, indent=2))
    #messageが "+"で連結されたため,"+"をスペースに戻す
    event['text'] = event['text'].replace('+',' ')

    clientLambda = boto3.client("lambda")
    clientLambda.invoke(
        #invokeするLambda Name
        FunctionName="Execution-Lambda",
        InvocationType="Event",
        Payload=json.dumps(event)
    )
    return event # Echo back the first key value

Execution Lambda

 【AWS Lambda Python で AWS CLI を実行する方法】を参考に、やって頂ければ、LambdaでAWS CLIを実行できるようになりますので、詳細な手順は割愛します。2点だけ説明します。

  1. lambda_function.pyは記事の内容と違っています。
  2. Execution LambdaがAWS CLIを実行しますので、適切な権限を与える必要があります。
    自分の場合は、検証ですので、PowerUserAccessを与えています。

 lambda_function.pyのコードは下記の通りです。

lambda_function.py
# -*- coding: utf-8 -*-
import commands
import json
import os
from cStringIO import StringIO
import re
import urllib
from urllib2 import Request, urlopen, URLError, HTTPError

print('Loading function')

def _(cmd):
    return commands.getoutput(cmd)

def lambda_handler(event, context):
    print("Received event: " + json.dumps(event, indent=2))

    #response_urlのデコード
    response_url = urllib.unquote(event['response_url']).encode('raw_unicode_escape').decode('utf-8')
    print(response_url)

    slack_message = {
        'channel': event['channel_name'],
        'response_type': 'ephemeral',
        'isDelayedResponse': 'true',
        'text': "response for: `aws " + event['text'] + "`\n" + _("./aws " + event['text'])
    }
    req = Request(response_url)
    req.add_header('Content-Type', 'application/json')
    try:
        response = urlopen(req, json.dumps(slack_message))
        response.read()
        print("Message posted to %s", slack_message['channel'])
    except HTTPError as e:
        print("Request failed: %d %s", e.code, e.reason)
    except URLError as e:
        print("Server connection failed: %s", e.reason)

API Gateway

  1. 新しいAPIを作成します。
    1.pic.jpg

  2. 今回はAPI Nameをslackにします。 適宜変更してください。
    2.pic.jpg

  3. APIを作成されたら、「Action」→「Create Method」をクリックして、メソッドを選択します。3.pic.jpg

  4. 今回はPOSTメソッドを使います。4.pic.jpg

  5. 「Lambda Function」をクリックして、Regionを選択します。
    スクリーンショット 0028-07-17 0.29.59.png

  6. 自分の場合は、Tokyo Regionのfront-lambdaを利用します。
    5.pic.jpg

  7. Permissionを聞かれますが、「OK」で良いです。
    6.pic.jpg

  8. 下記の画面が表示されますと、「Integration Request」をクリックします。
    7.pic.jpg

  9. 「Body Mapping Templates」にて、mapping templateを追加します。Request body passthroughはAWS推奨の項目にします。
    8.pic.jpg

  10. Content-Typeにapplication/x-www-form-urlencodedを追加します。
    9.pic.jpg

  11. templateを定義して、保存します。
    10.pic.jpg

  12. templateが保存されましたら、「Actions」の「Deploy API」を選択します。
    11.pic.jpg

  13. Stage nameは適宜でよいです。
    スクリーンショット 0028-07-17 0.50.54.png

  14. APIをデプロイされたことを確認し、Invoke URLをメモってください。後でSlackのSlack Commandsに設定します。12.pic_hd.jpg

  15. API Gatewayの準備が完了

SlackのSlash Commands

 Slack Commandsについては、SlackのDocumentation( Slack Commands )を確認してください。

  1. Slash Commandsにアクセスして、新しいSlash Commandを作成します。
    (例では、Slash Command /aws を作成します)13.pic.jpg

  2. URL欄にはAPI GatewayのInvoke URLを入力し、Method欄はPOSTにして、(残りの設定はお好みで)、「Save Integration」を押します。14.pic_hd.jpg

  3. SlackのSlash Commandsの設定は完了

利用イメージ

 SlackのChannelにAWS CLIを投げると、実行してくれます.
 以下は利用例です。

  • aws s3 ls
    21.pic_hd.jpg
     

  • aws ec2 describe-regions
    20.pic_hd.jpg

最後

 SlackのChannelからAWS CLIを実行できるようになりました。AWS CLIのコマンドを投げて、実行結果を返されるまで、少し時間がかかってしまい、多少ストレスが感じるかもしれませんが、Serverless且つMulti DeviceでAWS CLIを実行できることが凄く便利だと思っています。是非、使ってみてください!
 最後に1つ注意点だけ皆さんに伝えたいです、API Gatewayには認証とかできていないので、エンドポイント(Invoke URL)がバレてしまうと、だれでもAPIを叩ける状態になっています。ですので、少なくてもfront-lambdaに簡単な判断ロジックを入れいる必要があると思います。例えば、決められたSlackのChannel(team_id&channel_id)からのコマンドしか受け付けないようにすることです。
 最後の最後となりますが、まだ実運用されていないので、正直に言って、どこまで実用性があるかはまだ分かりませんが、少なくても、飲み会のネタにはビッタリではないでしょうか。
 今回はここまでにします。

17
22
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
17
22