暗号化したEBSボリュームを持つインスタンスの起動について(ハマったところ)

暗号化したEBSルートボリュームを持つインスタンスを、Lambdaを使って自動起動させようとした時にハマった箇所があったのであったのでまとめました。

# EBSルートボリュームの暗号化
以下の記事にありますように現在、EBSではインスタンスのルートボリュームの暗号化がサポートされています。
http://aws.typepad.com/aws_japan/2015/12/new-encrypted-ebs-boot-volumes.html
今回はこのルートボリューム暗号化を施したインスタンスの自動起動を行う際にハマった点について書きます。

# インスタンスの起動失敗
今回AWS Lambdaを用いてインスタンスの起動自動化を図ろうとしていました。
使用したスクリプトは[AMIバックアップを取るLambdaファンクション（python）](http://qiita.com/Hiroyama-Yutaka/items/9fab02438dc22c0b85ea)を元に作成した、以下のインスタンス起動バッチです。
ストップと合わせてGitHubにも置いてあります。
https://github.com/tatsuo48/Hello-world/tree/master/LambdaFunctions

```python:startinstance.py
#!/usr/bin/python
# -*- coding: utf-8 -*-

import json

import boto3

import pprint

TAG_KEY_AUTO_START = 'auto-start'


print('Loading function')

pp = pprint.PrettyPrinter(indent=4)

# 関数名 ： lambda_handler
def lambda_handler(event, context):
    print("Received event: " + json.dumps(event, indent=2))

    ec2_client   = boto3.client('ec2')

    ret = execute_start_instance(ec2_client)
    print 'Start instance success(%s).' % (ret)

    return 0
    raise Exception('Something went wrong')

# 関数名 ： execute_start_instance
# 戻り値 ： 実行結果
# 引数　 ： ec2_client
#       ： ec2_resource
# 機能　 ： インスタンスを起動する。
def execute_start_instance(ec2_client):
    response = ec2_client.describe_instances()

    result = None
    for ec2_group in response['Reservations']:
        for instance_info in ec2_group['Instances']:
            ret = is_target(instance_info)
            
            if (ret == False):
                continue
            instance_id = instance_info.get('InstanceId')
            response = ec2_client.start_instances(InstanceIds=[instance_id,])
            print response
            result = True
    return result

# 関数名 ： is_target
# 戻り値 ： 起動要否
# 引数　 ： instance_info <dict>
# 機能　 ： 起動要否を判定する
def is_target(instance_info):
    val = get_tag_value(
        instance_info,
        TAG_KEY_AUTO_START
    )

    if val is None:
        return False

    return True

# 関数名 ： get_tag_value
# 戻り値 ： タグ値（当該キーに合致するものがなければNone）
# 引数　 ： instance_info <dict>
#       ： key <str>
# 機能　 ： インスタンス情報から指定キーのタグ値を取得する
def get_tag_value(instance_info, key):
    tags = instance_info.get('Tags')
    if tags == None:
        return None

    for tag in tags:
        if not (key == tag['Key']):
            continue

        tag_value = tag['Value']
        if tag_value == "true":
            return True

    return None
```

しかし、何度Lambdaファンクションを実行しても、以下のような正しいレスポンスが返っているのにインスタンスは起動しませんでした。。。。
```
{u'StartingInstances': [{u'InstanceId': 'インスタンスID', u'CurrentState': {u'Code': 0, u'Name': 'pending'}, u'PreviousState': {u'Code': 80, u'Name': 'stopped'}}], 'ResponseMetadata': {'HTTPStatusCode': 200, 'RequestId': 'kkksfh084k-l9hh-8iy6-hshs4-ipsug88w9hwh'}}
```

# 原因:実行ロールの権限不足
色々調べましたが原因はとても単純なことでした。
KMS(Key Management Service)の実行許可が抜けていたのです。。
今回、私はデフォルトのキーではなく自分で暗号化キーを作成して使っていました。
その場合、該当の暗号化キーの「キーユーザ」もしくは実行ロールのポリシーにてキーの利用を許可する必要がありました。
暗号化キーの「キーユーザ」に設定する場合は以下のようにGUIで設定できます。
![暗号化キーのキーユーザ設定.png](https://qiita-image-store.s3.amazonaws.com/0/117895/e59c7ed4-4f41-405c-011e-90baef1410a3.png)


実行ロールのポリシーだったらこんな風に。

```JSON:実行ロールのポリシーに設定する場合
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "Stmt1236596007000",
            "Effect": "Allow",
            "Action": [
                "kms:*"
            ],
            "Resource": [
                "作成したキーのARN"
            ]
        }
    ]
}
```

# 終わりに
原因は至極当然で当たり前なんですが普段使わない機能だとそこまでたどり着くのにも苦労します。
これからも業務内外問わず触れていない機能に触れるようにしなければなーと思いました。