エンジニア

2016.02.07

はじめまして、ハンズラボにJOINした吉田です

皆様、はじめまして。
先日(1月中旬)ハンズラボにエンジニアとしてJOINした吉田です!
エンジニアブログ、頑張っていきます!

前職

2014年に新卒入社した会社でインフラエンジニアとして働いていました。
主にオンプレのWindowsServer(AD周り)の保守運用などの仕事に従事しつつ、AWSに恋をして夜な夜な一人でEC2と遊んでいました。
憧れのAWS環境でのお仕事なので頑張りつつ、認定資格5冠Tシャツ目指します。

1本目のエンジニアブログはLambdaに関して書いていこうと思います。

私はハンズラボに入社して初めてslackを使いました。
slackBOT、便利デスヨネ-。
便利なものは活用したい。

と、いうこで・・・

SNSの通知をLambdaでいい感じに整形してSlackに通知しよう!

やってみました。


1.SlackBot作成準備編

まずslack webhookなどで検索するとslackのAPI解説が出てきます。
https://api.slack.com/incoming-webhooks
skitch5
skitch3
skitch2
incoming webhook integration をクリックするとページにジャンプするのでキャプチャ画像に沿って選択をしていきます。

このページはいろいろ書いてあってここ読むとなんとなくslack botについてわかると思うので一読しましょう。

Webhook URLを取得できればslack側の準備は終了です。


2.Lambdaファンクションの作成

今回は現在勉強中のPythonを使ってLambdaのファンクションを定義していきます。

今回やりたいことはこちら!

Lambda構成図
CloudTrailのログを調査して、ROOTユーザーだったら
“えー、ROOTユーザー?ROOTが許されるのは小学生までだよねー!!!”
と、BOTが煽ってくる構成にします。
今回一番ハマったのはTrailのログです。
S3に圧縮して格納されています。これは困った・・・
最終的にはLambdaで一時領域にダウンロードし、解凍して読み取ることで解決しました。
Trail,SNS,S3,Lambda,DynamodbとけっこうAWSのサービスを活用しています。わくわくしますね。
なぜDynamodbを使用するか
自分がLambdaを使用するとき、処理の中心となるのはJSONから欲しい情報を抽出する処理です。
LambdaのサンプルでSNSはありますが、実際どんな感じでMessageが飛んでいるのかコードを書いているとイマイチわかりません(サンプルではHello SNS!!となっているため)。
なので自分はよく、SNSで飛んでくるJSONのMessageをそのまま一度Dynamoに出力しJSONを確認してからサンプルSNSを作成しています。


①SNSのメッセジをDynamoで確認する

Lambdaのコードサンプルはこちら
※前提としてDynamoへの書き込み権限のあるロール、JsonMessageという項目を持ったDynamoDBのテーブルを準備してあるものとします

from __future__ import print_function
import json
import boto3
from boto3.dynamodb.conditions import Key, Attr
DYNAMO_DB = boto3.resource('dynamodb')
DYNAMO_DB_TABLE_NAME = DYNAMO_DB.Table('**************')
print('Loading function')
def lambda_handler(event, context):
    message = event['Records'][0]['Sns']['Message']
    DYNAMO_DB_TABLE_NAME.put_item(
        Item={
            "JsonMessage":message,
        }
    )
    return message

さて、これで実際に飛んで来るSNSのMessageの中身が手に入りました。Dynamoのテーブルを確認してください。
こいつを元に作業をしていきます。


②ログが格納されているS3のバケットパスを取得する

Dynamoで取得したMessageの中身を確認しましょう

{\"s3Bucket\":\"************.*****\",\"s3ObjectKey\":[\"AWSLogs/************/CloudTrail/ap-northeast-1/2016/02/04/************_CloudTrail_ap-northeast-1_************_************.json.gz\"]}

余談ですが、ここで最初に絶望しました。このMessageでTrailで見れるやつ出してくれよ・・・
欲しかった格納先の情報がここからてにはいります。やったね!

③S3に格納されたデータをダウンロードします。

またまた余談ですが、Lambdaでも一部bashコマンド使えます。

import commands
def lambda_handler(event, context):
    check = commands.getoutput("touch /tmp/hoge")
    check = commands.getoutput("ls -la /tmp")
    print check

と定義したLambdaを実行してみると・・・

START RequestId: ********** Version: $LATEST
total 8
drwx------  2 sbx_user1060  486 4096 Feb  6 14:14 .
drwxr-xr-x 21 root         root 4096 Feb  6 10:17 ..
-rw-rw-r--  1 sbx_user1060  486    0 Feb  6 14:14 hoge
END RequestId: **********

このように帰ってきます。touchコマンド効いてますね。

つまり、圧縮ファイルだろうが /tmp 領域にダウンロードして解凍すれば使えます。

解凍さえ出来てしまえば後はJSONで欲しい値を探すだけです。


④SlackへのPOST

今回はslackwebという外部モジュールを使用します。
外部モジュール使用のためには、モジュールを含んでZIP化し、アップロードすれば大丈夫です。
⇒飛ばしません。ちゃんと書きますよ!

Macで作業(僕はMac使ってます)

cd ~
mkdir lambda_function
cd lambda_function
touch function.py
pip install slackweb -t ./
vim function.py
zip -r myfunc.zip ./

と、こんなノリで行きます。
コードとモジュールをまとめてZIP化してしまえばいいのです。
function.pyには下記コードを記載してください。

⑤完成

完成したものがこちら
Lambdaは日本語文字列を使うと怒られるためBase64でエンコードした文字列を渡しています。

import base64
import slackweb
import gzip
import json
import boto3
s3 = boto3.client('s3')
s3_bucket_name = '******'
SLACK_POST_URL = "https://hooks.slack.com/services/******"
def slack_post(post_massage):
    slack = slackweb.Slack(url=SLACK_POST_URL)
    slack.notify(text=post_massage)
def matching(target_str, source_str, text, text2):
    if source_str == target_str:
        slack_post(text)
    else:
        slack_post(text2)
def lambda_handler(event, context):
        snsmsg = event['Records'][0]['Sns']['Message']
        decode_json = json.loads(snsmsg)
        data = decode_json[u's3ObjectKey']
        obj_kye = data[0]
        s3.download_file(s3_bucket_name, obj_kye, '/tmp/log.gz')
        f = gzip.open('/tmp/log.gz', 'rb')
        content = f.read()
        f.close
        dec_cont_json = json.loads(content)
        user_type = dec_cont_json['Records'][0]['userIdentity']['type']
        dec_str = base64.b64decode('44GI44O844CBUk9PVOODpuODvOOCtuODvO+8n1JPT1TjgYzoqLHjgZXjgozjgovjga7jga/lsI/lrabnlJ/jgb7jgafjgaDjgojjga3jg7zvvIHvvIHvvIE=')
        matching('Root', user_type, dec_str, user_type)

slackを確認すると・・・
skitch
できました!
やった!!!たのしい!!!!!
プログラムもド素人で、右も左もわからず、本当に右左分からなくて同フロアで他の会社に入ろうとしたりとまだまだダメダメですが一歩づつ頑張っていきます。
宣伝
今月のJAWS-UG初心者支部でLTします!(初LT)


参考

http://docs.python.jp/2/library/gzip.html
https://github.com/satoshi03/slack-python-webhook

一覧に戻る