CloudWatch eventsを使ってEC2起動時にTagチェックをし必須TagがなければTerminateするLambdaサンプル

# 免責
本投稿は、個人の意見で、所属する企業や団体は関係ありません。
また掲載しているsampleプログラムの動作に関しても保障いたしませんので、参考程度にしてください。

# CloudWatch eventsでできること
リリース直後の(2016/1/15)で、以下の条件をevnet定義可能です。
詳細はAWS [SAブログ](http://aws.typepad.com/aws_japan/2016/01/new-cloudwatch-events-track-and-respond-to-changes-to-your-aws-resources.html)を参照下さい

* EC2のstatus変更
* スケジュール実行
* AWS API call
* AWS console sign-in
* Auto Scalling

API call数が多すぎて見切れません、、、CloudTrailで見れるものは対応できそうです。

# 本当投稿で実施すること
CloudWatch eventsを使ってLaunchしたInstaceのTagをチェックして、必須Tag-key情報がない場合にはInstanceをterminateする。
(本サンプルでは、TagのKeyとして"Cost"が設定していないinstanceがLaunchすると即座にTerminateされます)
Cloudwatch eventsは、今までPolling型でTargetのstatusを監視し、変化が合ったらプログラムを実行するようなケースに対して、Event発生を発火条件にできるサービスとなります。


# 作業

## Step1 AWS Lambda functionの作成
今回はPythonでAWS Lambdaを作ってみます。
Roleは lambda_basic_executionとします。（step3で使います)
大まかなプログラムの流れは、

* Step2の設定eventsから instance-id, instance statusを取得
* instance-id を条件にdescribe_instancesの実行
* responseからTag情報を取得し、必須Tag-Keyが設定されているかのチェック
* 必須Tag情報がなければ instanceを terminate
Lambda用Sample python scriptを貼っておきます。
#Pythonほとんど書いたことないので、綺麗なプログラムではないのと、エラー処理系は不足していると思います。とりあえず動けばという程度のものです。

update@20160126
 Tagを空(NULL)にするとLambdaがエラーになってしまうので修正しました。

```py:TerminateEC2_byTAG
import boto3
import json
client = boto3.client('ec2')

def lambda_handler(event, context):
    print 'Start Lambda function(TerminateEC2 by Tag)'

    detail = event['detail']
    instance_id = detail['instance-id']
    
    if instance_id is None:
        print 'There is no Instance ID'
        return 'false'

    # Show Instance ID/state        
    print "Check start Target instance ID =" + instance_id
    print 'State= '+ str(detail['state'])

    if  detail['state'] != 'pending':
        return "Status fail"
    
    # Exec descibeinstances by instance-id
    # About boto ec2.describe-instaces check below link
    # http://boto3.readthedocs.org/en/latest/reference/services/ec2.html#EC2.Client.describe_instances
    res_json = client.describe_instances(
        DryRun = False,
        Filters=[{'Name':'instance-id', 'Values':[instance_id]}]
        )
    HTTP_code = res_json['ResponseMetadata']['HTTPStatusCode']
    if str(HTTP_code) != '200':
        print 'describe instances command fail: HTTP responce=' + str(HTTP_code)

    res = res_json['Reservations'].pop()
    Instance_info = res['Instances'].pop()
    
    #Tag check
    if check_Tags(Instance_info, instance_id) == 'true':
        return 'Tags are Okay'
    else:
        return 'Need to Setup the mandatory key'
    
    context.done('end of Lambda')

# Check Tag key
#   Input:Tags [associative array]
#   Output:true/false
def check_Tags(Instance_info, instance_id):
    
    #defined
    MANDATORY_KEYNAME='Cost'

    if Instance_info.has_key('Tags'):
        tags = Instance_info['Tags']
    else:
        exec_terminate_Instance(instance_id)
        return

    for tag in tags:
        print tag.get('Key')
        if tag.get('Key') == MANDATORY_KEYNAME:
            return 'true'
            
    exec_terminate_Instance(instance_id)
    return 'false'

# Execute EC2 terminate
#   Input: Instance-ID
#   Output: none
def exec_terminate_Instance(instance_id):
    print 'There is no Mandatory Tag'
    print 'Instance Terminate :' + instance_id

    # See datail:http://boto3.readthedocs.org/en/latest/reference/services/ec2.html#EC2.Client.terminate_instances
    ret = client.terminate_instances(
        DryRun=False,
        InstanceIds=[instance_id]
        )
        
    print 'Execute Instance Termiante'
    return
    
    
```

## Step2 CloudWatch envetsの設定
AWSコンソールから、Cloudwatchを選択し、左メニューの "Events"
"EC2 instance state change notification"を選択し、"specific state(s)"から "pending"を選択します。
"Target"に Step1で作成したLambda functionを選択。
画面としては以下の感じになります。
![events-set.jpg](https://qiita-image-store.s3.amazonaws.com/0/100893/fe885b02-2097-c1a3-be6b-94931346df61.jpeg)

configure detailsを選択し、次の画面でルール名を任意の名前で設定してください。


## Step3 Lamabda function にRoleの設定
LambdaでEC2操作などをするためにIAMの設定を行います。
AWSコンソールから Identity&Access Management を選択し、
step1で作ったLambda role(basic_lambda_exectution)に権限を追加します。
画面では EC2ReadOnlyがありますが、Fullaccessを付けているので不要です。

![policy.JPG](https://qiita-image-store.s3.amazonaws.com/0/100893/27fc4926-cdb6-4fee-8cf4-c0315c7e5d52.jpeg)

続いて、Trust Relationship

```
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Service": "lambda.amazonaws.com"
      },
      "Action": "sts:AssumeRole"
    },
    {
      "Sid": "1",
      "Effect": "Allow",
      "Principal": {
        "Service": "ec2.amazonaws.com"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}
```
とします。

# テスト
Instanceを起動します。
Tag設定で設定しないもしくは、Keyに"Cost"を設定しないで起動してください。
![tag.JPG](https://qiita-image-store.s3.amazonaws.com/0/100893/0216d095-500c-fb5b-2362-522aa048b902.jpeg)

Launchボタン押下後にEC2のメニューを確認してください。
かなり素早く、Shutting down/Terminateに状態が遷移しているはずです。

#log
今回のサンプルプログラムのログです。
CloudWath logsにLambdaのログが出ているはずです。
![log.JPG](https://qiita-image-store.s3.amazonaws.com/0/100893/2c5686d5-9ec8-a0c4-9b01-abb12fff88fe.jpeg)

# まとめ
というわけで、とりあえず出たばかりのCloudWath evnetsを使ったサンプルを作ってみました。
今まではInstanceの状態などを管理しようと思うとPolling型のプログラムが必要だったのですが、Pollingが不要でイベントをトリガにプログラムをかけるのが非常に便利です。