LambdaでEC2インスタンスの日次スナップショットを取得する

# 目的
EBSの日次スナップショットを取得し、７世代だけ保持するという要件が発生したため

# 構成
今回はカスタムメトリクス取得時と同様、以下のような構成にしました。
[カスタムメトリクスをLambdaとCloudWatch Eventsを使って取得する](http://qiita.com/d-yamanaka/items/9ecac06901c739a40872)

![image](https://qiita-image-store.s3.amazonaws.com/0/40136/5bff691f-0fcb-8fd3-7ac8-16682527f003.png)

1.CloudWatch Eventsにて1日に1回スナップショット取得用Lambdaを呼び出す
2.LambdaにてEBSのスナップショットを取得する
3.取得した結果スナップショット数が上限値を超えたら一番古いスナップショットを削除する


# IAMロールの設定
Lambda用にロールを作成し、以下ポリシーを紐づけます。

```json
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "Stmt1479031595000",
            "Effect": "Allow",
            "Action": [
                "ec2:CreateSnapshot",
                "ec2:CreateTags",
                "ec2:DeleteSnapshot",
                "ec2:DescribeSnapshots"
            ],
            "Resource": [
                "*"
            ]
        }
    ]
}
```

# Lambdaの設定

## 設定
設定値は以下の通りです。

|    項目    |    設定値    |
|:-----------|:-----------|
|Runtime|Node.js 4.3|
|Handler|index.handler|
|Role|作成したLambda用ロール|
|Memory|128 MB|
|Timeout|20 sec|

## コード
コードは以下の通りです。
※volumeIdを取得したいEBSのIDに変更してください。
※tagNameは取得したスナップショットのNameタグ名です。

```js
'use strict';

const AWS = require('aws-sdk');
const ec2 = new AWS.EC2();

exports.handler = (event, context, callback) => {

    const generator  = (function *() {

        try {
            // スナップショットを取得する対象のEBSボリュームIDに変更します。
            const volumeId    = 'vol-xxxxxxxx';
            // 任意のタグ名
            const tagName     = 'xxxx-snapshot';
            // バックアップ保持世代数
            const retention   = 7;

            // スナップショットを作成
            const snapshotId = yield createSnapshot(volumeId, generator);
            
            // タグ付け
            yield createTags(snapshotId, tagName, generator);

            // スナップショットのリストを取得
            const snapshotsList = yield describeSnapshots(tagName, generator);

            // snapshotの数が保持世代数を超えたら削除
            if(snapshotsList.length > retention ) {
                yield deleteSnapshot(snapshotsList[snapshotsList.length-1].SnapshotId, generator);
            }

            callback(null, 'success');

        } catch (e) {
            callback(e.message);
        }
    })();

    /* 処理開始 */
    generator.next();

};

// スナップショットを作成
function createSnapshot(volumeId, generator) {

    const params = {
        VolumeId: volumeId
    };

    ec2.createSnapshot(params, function(err, data) {
        if (err) {
            console.log(err, err.stack);
            generator.throw(new Error('create snapshot error : ' + params.VolumeId));
            return;
       }
       console.log('successful create snapshot : ' + params.VolumeId);
       generator.next(data.SnapshotId);
    });
}

// タグ付け
function createTags(snapshotId, tagName, generator) {
    
    const params = {
        Resources: [
            snapshotId
        ],
        Tags: [
            {
                Key  : "Name",
                Value: tagName
            }
        ]
    };
    
    ec2.createTags(params, function(err, data) {
        if (err) {
            console.log(err, err.stack);
            generator.throw(new Error('create tags error : ' + params.Resources[0]));
            return;
       }
       console.log('successful create tags : ' + params.Resources[0]);
       generator.next();
    });
}

// スナップショットのリストを取得
function describeSnapshots(tagName, generator) {

    const params = {
        Filters: [
            {
                Name: 'tag-key',
                Values: [
                    'Name'
                ]
            },
            {
                Name: 'tag-value',
                Values: [
                    tagName
                ]
            }
            
        ]
    };

    ec2.describeSnapshots(params, function(err, data) {
        if (err) {
            console.log(err, err.stack);
            generator.throw(new Error('describe snapshots error'));
            return;
       }
       console.log('successful describe snapshot');

       const snapshotsList = data.Snapshots;
       // 降順にならびかえ
       snapshotsList.sort(function(a,b){
           let createDateA = new Date(a.StartTime);
           let createDateB = new Date(b.StartTime);

           if(createDateA.getTime() > createDateB.getTime()) {
               return -1;
           }
           if(createDateA.getTime() < createDateB.getTime()) {
               return 1;
           }
           return 0;
       });
       generator.next(snapshotsList);
    });
}

// snapshotを削除する
function deleteSnapshot(snapshotId, generator) {

    const params = {
        SnapshotId: snapshotId
    };

    ec2.deleteSnapshot(params, function(err, data) {
        if (err) {
            console.log(err, err.stack);
            generator.throw(new Error('delete snapshot error : ' + params.SnapshotId));
            return;
       }
       console.log('successful delete snapshot : ' + params.SnapshotId);
       generator.next();
    });
}
```

# CloudWatch Eventsの設定
日次でLambdaを実行するためのイベントを作成します。

[CloudWatch] - [ルール] - [ルールの作成]から新しくルールを作成します。

## スケジュール
毎日夜中の3時（日本時間）にsnapshotを取得したい場合は、Cron式を以下のようにします。

```bash
cron(0 18 * * ? *)
```

参考：[ルールのスケジュール式の構文](http://docs.aws.amazon.com/ja_jp/AmazonCloudWatch/latest/events/ScheduledEvents.html)

## ターゲット
ターゲットには作成したLambdaファンクションを指定してください。


以上の設定ができれば、CloudWatch Eventsの設定は完了です。

# 以上
動作確認をしたところ、毎日スナップショットが作成され、きっちり７世代だけ残ることが確認できました。