CloudTrailログに対する処理をGroovyでサクっと書く

こんばんは。性の六時間ですが皆様如何お過ごしでしょうか。
クリスマスイブからクリスマス当日にかけてのエンジニアのAWS操作もCloudTrailがあれば大変簡単にハンドルできますね。

そもそも分析ツールも大変多く、自前でログの処理をするまでも無い感は多少ありつつも、先月リリースされている「AWS CloudTrail Processing Library」を使うと
CloudTrailで捕捉できたAWSの操作イベントに対する処理をらくちんに書くことができます。
[Announcing the AWS CloudTrail Processing Library](https://java.awsblog.com/post/Tx12VURHBNFLXMR/Announcing-the-AWS-CloudTrail-Processing-Library)(リリース時のアナウンス)

# 簡単にいうと
```groovy:simpleprocessing.groovy
@Grab(group='com.amazonaws', module='aws-cloudtrail-processing-library', version='1.0.0')
import com.amazonaws.services.cloudtrail.processinglibrary.AWSCloudTrailProcessingExecutor;

executor = new AWSCloudTrailProcessingExecutor.Builder(

        { events ->
            events.each {
                println "${it.eventData.userIdentity.userName?: 'root'} 「${it.eventData.eventName} なう」(${it.eventData.eventTime})"
            }
        },
         "/ctp.properties").
        build()

addShutdownHook {
        executor.stop()
}

executor.start()
```
こんな感じのgroovyコードを書くだけで
```
ishibashi 「ConsoleLogin なう」(Wed Dec 24 22:59:08 JST 2014)
ishibashi 「DescribeInstances なう」(Wed Dec 24 23:00:03 JST 2014)
ishibashi 「DescribeLoadBalancers なう」(Wed Dec 24 23:00:04 JST 2014)
ishibashi 「DeregisterInstancesFromLoadBalancer なう」(Wed Dec 24 23:00:06 JST 2014)
```
AWS上の各イベントを処理できます。楽ちん。

# CloudTrailとは
>AWS CloudTrail は、アカウントの AWS API の呼び出しを記録し、ログファイルを送信するウェブサービスです。記録される情報には、API 呼び出し元の ID、API 呼び出し元のソース IP アドレス、リクエストのパラメータ、および AWS サービスから返された応答の要素が含まれます。
CloudTrail を使用すると、アカウントの AWS API の呼び出し履歴を取得できます。履歴には、AWS マネジメントコンソール、AWS SDK、コマンドラインツール、高レベルの AWS サービス（AWS CloudFormation など）を使用した API の呼び出しが含まれます。CloudTrail で生成される AWS API の呼び出し履歴を利用して、セキュリティの分析、リソース変更の追跡、およびコンプライアンスの監査を行うことができます。

[AWS CloudTrail](http://aws.amazon.com/jp/cloudtrail/) 公式より

# Setup
1. CloudTrailを有効にする
![Screenshot 12:24:14 11:26 PM.png](https://qiita-image-store.s3.amazonaws.com/0/10659/5db4356a-54a1-c97b-0415-0462343b08a1.png)
まずはCloudTrailが有効になってないと話にならないので有効にします。
ログをS3に飛ばした際にSNS通知を行うかのオプションがあるので、Yesにして、トピックの名前をつけます。

2. SQSキューを作成して、上記SNSを購読させる
SQSキューを作成し、上記1.で設定したSNSトピックのサブスクライバとして設定することでこいつがプログラム用の処理キューになります。

3. 上記SQSキュー及び認証情報を設定したプロパティファイルを用意する
[ライブラリに付属しているサンプル](https://github.com/aws/aws-cloudtrail-processing-library/blob/master/src/sample/awscloudtrailprocessinglibrary.properties)の中身を書き換えるのが良いでしょう。AccessKey/SecretKey/SQSのUrl/S3（ログを落としてくる元）およびSQSのリージョンを設定します。

4. 実行！
上記ファイルと先頭のgroovyを同じ場所に置いて実行すると、上記の出力が楽しめます。

# Customize
使ってるとDescribeXXXとかGetYYYみたいなやつは頻度が高く、フィルタしちゃってもいいかなという気になってきます。そんな時はEventFilterを使います。

```groovy:filteringapplied.groovy
@Grab(group='com.amazonaws', module='aws-cloudtrail-processing-library', version='1.0.0')
import com.amazonaws.services.cloudtrail.processinglibrary.AWSCloudTrailProcessingExecutor;

executor = new AWSCloudTrailProcessingExecutor.Builder(
        { events ->
            events.each {
                println "${it.eventData.userIdentity.userName?: 'root'} 「${it.eventData.eventName} なう」(${it.eventData.eventTime})"
            }
        }, "/ctp.properties").
        withEventFilter(
            { event ->
                name =  event.eventData.eventName
                return !name.startsWith("Describe") && 
                       !name.startsWith("Get")
            }
         ).
        build()

addShutdownHook {
        executor.stop()
}
executor.start()
```

実際はprintlnしても意味が無いのでHipChat/Slackに飛ばすなどして迅速に検知したいですね。

```posttohipchat.groovy
@Grab(group='com.amazonaws', module='aws-cloudtrail-processing-library', version='1.0.0')
import com.amazonaws.services.cloudtrail.processinglibrary.AWSCloudTrailProcessingExecutor;

@Grab(group='org.codehaus.groovy.modules.http-builder', module='http-builder', version='0.7.1')
import groovyx.net.http.*
import static groovyx.net.http.ContentType.URLENC

http = new HTTPBuilder( 'https://api.hipchat.com/v1/rooms/message?format=json&auth_token=XXXXXXXXXX')

executor = new AWSCloudTrailProcessingExecutor.Builder(
        { events ->
            events.each {
                message = "${it.eventData.userIdentity.userName?: 'root'} 「${it.eventData.eventName} なう」(${it.eventData.eventTime})"
                http.post( body: [ room_id:'123456', from: 'CloudTrail', message: message, message_format:'text'],
                           requestContentType: URLENC ){resp->}
            }
        }, "/ctp.properties").
        withEventFilter(
            { event ->
                name =  event.eventData.eventName
                return !name.startsWith("Describe") && 
                       !name.startsWith("Get")
            }
         ).
        build()

addShutdownHook {
        executor.stop()
}

executor.start()
```

実に楽ちんであります。

# おわりに
上記フィールドだけでは勿論足りず、例えばリージョンとかアクセス元IPとか様々なデータが取れるので[CloudTrailEventData](https://github.com/aws/aws-cloudtrail-processing-library/blob/master/src/main/java/com/amazonaws/services/cloudtrail/processinglibrary/model/CloudTrailEventData.java)や[UserIdentity](https://github.com/aws/aws-cloudtrail-processing-library/blob/master/src/main/java/com/amazonaws/services/cloudtrail/processinglibrary/model/internal/UserIdentity.java)あたりをざっと見ておくと楽しいです。
https://github.com/aws/aws-cloudtrail-processing-library はJavaで書かれたライブラリですが、
* 少なからず依存関係があるけどGroovyファイルだけ渡せばだれでも実行できる（Grapeが良い）
* EventProcessorやEventFilterをクロージャで書いちゃう方が見た目スッキリ
という理由からGroovyを選んでいます。

この仕組って、要するにS3 Event Notificationsを用いたログ処理のCloudTrail特化版だと思うので、イベントリーダ（行処理及びJSON処理部）も含めて弄りやすければわりと使いやすいのだと思います。
ファイル追加が通知されるSNSトピックにSQSキューを引っ掛けておく。キューから取り出した情報をもとにS3からファイルを落としてくる。処理しながら進捗をレポートする。処理が終わったらファイルを消してキューからタスクも消す。あたりは常に共通してそうなのでこのライブラリで処理できるのがCloudTrailログだけなのは勿体無い。次はこいつをGunosyログ向けに改造してみようと思います。

# メリー・クリスマス！
とってつけた感満載ですが、皆様良いホリデーシーズンをお過ごしください。

明日はオーラス、[tyshgc](http://qiita.com/tyshgc)の「他所でもクリスマスになんかかくのでここでなんかかく」です！お楽しみに！