SlackにアップロードしたファイルをS3に保存させる①

Lambda

今回は2本立て

フェイス・ソリューション・テクノロジーズ株式会社 IS 本部 OS ユニットの masです。
今回はSlackとAWSの両方で操作する部分が多く、そこそこのボリュームになるので、以下の2部構成で手順を説明していこうと思います。

  • サービスの構成の説明~Lambdaの作成まで
  • API Gatewayの作成~テストまで

それではまず今回の全体的な概要から説明していきます。

概要

今回はSlackのチャンネルにファイルをアップロードしたら、そのファイルがAWSのS3にファイルが保存されるようにします。
やり方としてはLambdaとAPI Gatewayを連携して、Slackからのevent情報をLambdaに送ります。
そして、event情報を受け取ったLambdaがSlackからファイルを取得しS3へ保存する処理を行います。

概要は以上です。
続きまして、各サービスの繋がりを理解してもらうために簡単に構成を説明していきます。

サービスの構成

繋がりをイメージしやすいようにサービスの構成を下記に表しました。

ちなみになんとなくで想像すると、SlackにアップロードしたファイルがそのままダイレクトにS3に送信されて保存されるようなイメージをしてしまいそうですが、
実際の過程では、まずSlackにファイルが保存されて、その後にLambdaがSlackからファイルを取得してS3に保存するという流れになります。

上記のイメージを簡単に頭に入れていただけたらサクサクと手順をこなしていきましょう。

作業手順

S3の操作

S3bucketを作成する

S3のサービス画面に入り、バケットを作成をクリック

バケット名に任意の名前を入力しバケットを作成する。

Lambdaの操作

Lambdaの作成

AWSのコンソール画面で左上の検索画面に「lambda」と打ち込むと検索結果にLambdaが表示されるのでそれをクリック

Lambdaのサービス画面の右上あたり、関数の作成をクリックする。

関数名は任意の関数名を入力する。

ランタイムはPython3.9を選択

右下(ちょいスクロールしないと見えてないかも)の関数の作成をクリック

タイムアウトの設定を変更する

初期設定のままだとタイムアウトで処理が止まる可能性があるので変更します。
「設定タブ」の一般設定から編集をクリック
基本設定内のタイムアウトの値を15分0秒に設定して保存します。

LambdaのロールにS3にアクセスする為のポリシーを付与する

Lambdaの設定からアクセス権限を開き、実行ロールのロール名をクリック

移動したページで許可ポリシーのポリシー名をクリック

さらに移動先のページでポリシーの編集をクリック

さらにアクセス許可を追加するボタンをクリックし表示された設定画面を画像のように設定しポリシーの確認をクリック
サービス : S3
アクション : リスト
リソース : すべてのリソース

移動先の画面で変更の保存をクリック

コードの作成

コードのところに下記を貼り付ける。
下の方にあるbucketNameの値は先ほど作成したバケット名を入力してください。

import os
import json
import boto3
import urllib.request

def lambda_handler(event, context):
    # TODO implement
    
    #もしeventの中身がbodyの場合
    if event.get('body'):
        event=event['body']
    
    request_type=event['type']
    
    #eventのtypeがurl_verificationの場合はchallengeを返す
    if request_type=='url_verification':
        challenge=return_challenge_to_Slack(event)
        return challenge
    
    ins_file_from_slack=file_from_slack(event)
    
    print(f'token is {ins_file_from_slack.slackAccessToken}')
    print(f'id is {ins_file_from_slack.id}')
    
    ins_file_from_slack.uploadfile_to_S3()
    

class file_from_slack():
    def __init__(self,e):
        self.slackAccessToken=self.getSlackAccessToken()
        self.id=self.getFileID(e)
        self.json_file=self.getJsonFileDataFromSlack()
        self.url_private=self.getFilePrivateUrl()
        self.file_name=self.getFileName()

 
    def getSlackAccessToken(self):
        token=os.environ['SLACK_ACCESS_TOKEN']
        
        return token
    
    def getJsonFileDataFromSlack(self):
        url = "https://slack.com/api/files.info?file=" + self.id + "&pretty=1"
        
        req = urllib.request.Request(url)
        req.add_header('Authorization','Bearer ' + self.slackAccessToken)
        
        with urllib.request.urlopen(req) as res:
            body = res.read()
        
        print(f'body is {body}')
        
        fileRawData=body
        
        json_filesRawData=json.loads(fileRawData)

        
        json_file=json_filesRawData['file']
        
        return json_file

    def getFileName(self):
        
        return self.json_file['name']
    
    def getFilePrivateUrl(self):
        url_private=self.json_file['url_private']
        
        print(f'url_private is {url_private}')
        
        return url_private

    def getFileID(self,e):
        id = e['event']['file']['id']
        
        return id

    def uploadfile_to_S3(self):
        
        #/tmpにファイルをダウンロードしてくる
        req = urllib.request.Request(self.url_private)
        req.add_header('Authorization','Bearer ' + self.slackAccessToken)
        
        with urllib.request.urlopen(req) as res:
            body = res.read()
        
        #ファイルをs3にアップロードする
        with open('/tmp/test.txt','wb') as wf:
            
            wf.write(body)
            
            bucketName = '作成したバケット名を手動でいれてください'
            
            #clientを取得
            s3 = boto3.resource('s3')
            
            #bucketを取得
            bucket = s3.Bucket(bucketName)
            
        #bucketにファイルをアップロード
        bucket.upload_file('/tmp/test.txt', self.file_name)

#slackへchallengeを返す
def return_challenge_to_Slack(event):

    #apigatewayで受ける場合
    challenge=event['challenge']
    
    return challenge
コード部分の解説

作成したLambdaがSlackからイベントを受け取るためには、まずAPIを作成して、Slackの方に作成したAPIのURLを登録しないといけません。
そしてURLの登録をする際に、SlackからLambdaへリクエストデータが飛んできます。
そのデータの中に含まれているchallengeというidを抽出して、Slackに返してやらないと無効なURL扱いになり、URLを登録させてもらえません。
その為、今回はreturn_challenge_to_Slackという関数を使ってSlackへchallengeを返却するようにしています。
ちなみに今回こちらで作成している関数は簡易なものの為、本来のセキュリティ要件は満たせていません。
実際に本番で使用される際には、Slackのドキュメントを参考に要件に則ったうえで作成いただければと思います。

今回はここまで

お疲れ様でした。
とりあえず今回はLambdaの作成まで進めてきました。
完成までの主な残り作業はAPI Gatewayの設定とSlackでのアプリ作成の2つの工程です。
S3へのファイルアップロードは様々な自動化の鍵になると思うので、引き続いて次回の記事もお読みいただき、是非とも活用していただければと思います。

それでは次回もよろしくお願いします。

プロフィール
この記事を書いた人
mas

IS本部オペレーションサービスユニットでサーバ監視および映像監視担当。
趣味はカラオケだけれど、コロナの為に自粛中。
AWSの資格はクラウドプラクティショナーを取得済み。
ソリューションアーキテクトアソシエイト取得に向けて勉強中。

masをフォローする
フェイスでは一緒に働く仲間を募集しています

フェイス・ソリューション・テクノロジーズ株式会社では、一緒に働いてくれる仲間を募集しています。
いろいろな案件があるので、いろいろなことに挑戦できる会社です。
「面白いこと」に積極的なので、あなたの「面白そうなことだからやってみたい」を形にできるチャンスがあります!

Lambda
フェイススタッフブログ

コメント

タイトルとURLをコピーしました