【boto3入門】起動されたEC2にAWS Lambdaでタグを自動的に付与してみる【30分】

記事タイトルとURLをコピーする

CI部の柿﨑です。
つい最近、キーマカレーを作るのがとても簡単なことに気づきました。
あのじゃがいもの皮むきをやらなくて済むんです!!
たまねぎをみじん切りにする手間さえクリアすれば、ひき肉と鍋に入れてジエンドなんです。
と、いうことでフードプロセッサーを購入して、快適なキーマカレーライフを送っています。

私がSWXに入社して初めて書いたこちらの記事の続きに近い内容を書きます。
今回も内容はなるべくシンプルにしつつ、EC2の停止忘れで思わぬ課金がされないようにEC2を自動停止する仕組みを作ります。
自動停止にはEC2のタグを用いるため、タグ付けまではAWS上で自動化し、自動停止部分は弊社Cloud Automatorを利用します。
お試し版もございますので、ぜひ利用してみてください♪

私の環境では以下のような、タグ名「AutoStop」に値「true」を設定してEC2を自動停止していますので、今回はこちらをベースに設定していきます。

目次

  1. 実装方法を考える
  2. 実装する

1.実装方法を考える

まずはEC2を自動停止できるようになるまでの筋道を後ろから考えてみます。
(私がいつも後ろから考えているだけで、こうしろということではありません。)
詳細は割愛していますので、いきなり粒度が細かいのはご愛嬌ということで。

1.running状態のEC2を自動停止する
2.EC2を自動停止する機能を実装し、その判別にEC2のタグを利用する
3.running状態のEC2に任意のタグを付与する機能が必要である
4.タグを付与する機能のトリガーを考える

ざっとこんな感じでしょうか。
それぞれの実装方法を考えてみます。

1.running状態のEC2を自動停止する
2.EC2を自動停止する機能を実装し、その判別にEC2のタグを利用する

こちらはCloud Automatorで簡単に実装できるため、今回はCloud Automatorを採用。
もちろんAWS Lambdaでも実装可能です。

3.running状態のEC2に任意のタグを付与する機能が必要である

AWS Lambdaにboto3を書いて実装可能と考えます。
念のためにboto3のドキュメントを確認してみましょう。

Ctrl + Fで「tag」を引っ掛けてみると、create_tags()というものがヒットしました。

先へ進んでみますと、


Adds or overwrites the specified tags for the specified Amazon EC2 resource or resources. Each resource can have a maximum of 50 tags. Each tag consists of a key and optional value. Tag keys must be unique per resource.   指定されたAmazon EC2リソースに指定されたタグを追加または上書きします。 (Google翻訳)
 
4.タグを付与する機能のトリガーを考える
 
こちらはEC2のステータスがrunning状態になったことをトリガーとしたいです。
そうすることでAWS Lambdaは単純にタグを付与する機能だけ実装できればよくなるためです。
AWSリソース上のステータスをトリガーとする場合は、CloudWatch Eventsが便利です。
※どういうイベントが出力されるかは、こちらのドキュメントで確認可能ですが、今回は勉強がてらロググループを使って確認します。

以下のようにCloudWatchコンソールからルールの作成を押下します。
 
イベントパターンにて、サービス名を「EC2」、イベントタイプを「EC2 Instance State-change Notification」とそれっぽい項目を選択。
特定の状態に「running」が存在することを確認します。
ここまでの確認で、CloudWatch EventsはEC2がrunning状態になったことを検出して、AWS Lambdaのトリガーとなり得ることが判明しました。
ここでEC2、CloudWatch Events、AWS Lambdaの関係を構成図におこしてみます。
AWS側で上記の構成を実装して、Cloud Automatorで自動停止の仕組みを実装すれば問題なさそうですね。(わーい)
 

2.実装する

1.Lambda関数を作成する

まずはLambda関数を作成していきます。

関数名は自由ですが、今回は「Enable-Tags-for-EC2」とします。
言語はPython 3系であれば問題ありません。

アクセス権限では「基本的な Lambda アクセス権限で新しいロールを作成」を選択して、関数を作成します。

Lambda関数が作成されたら「アクセス権限」タブを押下し、IAMロールの権限を変更します。

IAMコンソールへ遷移したら「ポリシーをアタッチします」を押下。
今回はLambda関数でEC2にタグ付けするため、「AmazonEC2FullAccess」ポリシーをLambda関数のIAMロールに付与します。

IAMロールに「AmazonEC2FullAccess」ポリシーが付与されていることを確認。

Lambda関数の画面へ戻り、デフォルトのコードを確認します。
ここではトリガーからeventとcontextを受け取って関数が動くことが分かりますね。

今回はインスタンスIDを指定してタグを付与したいため、running状態になったEC2のインスタンスIDをどのように取得できるのかを調べます。
と、いうことでトリガーから受け取るeventの中身を確認するために、以下のようにコードを書き替えて保存します。
(boto3を利用することが確定しているため、ついでにboto3もインポートしておきます。)

※どういうイベントが出力されるかは、こちらのドキュメントで確認可能ですが、今回は勉強がてらロググループを使って確認します。

コード

import json
import boto3

def lambda_handler(event, context):
    # print文でeventを表示
    print (event)
    
    # TODO implement
    return {
        'statusCode': 200,
        'body': json.dumps('Hello from Lambda!')
    }

2.Lambda関数のトリガーを作成する

以前の手順で参照したようにCloudWatch Eventsのルールを作成します。
特定の状態にEC2の「running」のみ選択されていれば、問題ありません。
「ターゲットの追加 * 」を押下し、先ほど作成したLambda関数を選択して、「設定の詳細」を押下します。

任意の名前を設定し、「状態」の「有効化」にチェックが入っていることを確認してルールを作成します。

ルールが作成されていることを確認できたら、トリガーの設定は完了です。

3.Lambda関数のコードを詰める

まずはコード上でインスタンスIDを取得するために、トリガーイベントの中身を確認します。


EC2を起動してLambda関数を動かしてみます。
(EC2がない場合は、適当なEC2を新規作成してください。)
EC2のステータスがrunning状態になったらLambda関数が動きます。
EC2が無事起動したら、忘れないうちにEC2を停止します。


Lambda関数のログはロググループに出力されますので、「/aws/lambda/<Lambda関数名>」のログループを押下します。

ログストリームが作成されていますので、こちらも押下します。

そうしますとLambda関数のprint文で指定していたeventの中身を確認することができます。
CloudWatch Eventsでrunning状態を検知したEC2の情報も確認できますね。
インスタンスIDもあります。
これを前回の記事でやったようにインスタンスIDだけ取得したいため、JSON Viewerを利用してさらに確認します。
Textにイベントログを貼り付けて、「Viewer」タブを押下。

「detail」の中に「instance-id」がありますので、「event["detail"]["instance-id"]」でインスタンスIDを取得できそうです。

Lambda関数の画面へ戻ります。
インスタンスIDを受け取ってからEC2にタグを付与する動作を関数にしたいため、以下のようにコードを修正します。

import json
import boto3

# EC2にタグを付与する関数
def set_tags_for_ec2(instance_id):
    

def lambda_handler(event, context):

    # トリガーからインスタンスIDを取得し、EC2にタグを付与する関数へ渡す
    set_tags_for_ec2(event["detail"]["instance-id"])
    
    # TODO implement
    return {
        'statusCode': 200,
        'body': json.dumps('Hello from Lambda!')
    }

ここまで書ければ、あとはEC2にタグを付与する動作をboto3で記述すれば終わりです。
boto3のドキュメントから以下の一文を拝借します。

import json
import boto3

def set_tags_for_ec2(instance_id):
    # 低レベルAPIでEC2にアクセス
    client = boto3.client('ec2')
    
def lambda_handler(event, context):
    
    set_tags_for_ec2(event["detail"]["instance-id"])
    
    # TODO implement
    return {
        'statusCode': 200,
        'body': json.dumps('Hello from Lambda!')
    }

続いてcreate_tags()のドキュメントから以下の一文を拝借します。

import json
import boto3

def set_tags_for_ec2(instance_id):
    
    client = boto3.client('ec2')
    # EC2にタグ付けをする
    response = client.create_tags(
        DryRun=True|False,
        Resources=[
            'string',
        ],
        Tags=[
            {
                'Key': 'string',
                'Value': 'string'
            },
        ]
    )

貼り付けたコードを編集します。
今回はDryRun=True|False,の行が不要なため削除します。
Resourcesで指定するIDをトリガーで取得したインスタンスIDにしたいため、instance_idとします。
Tagsで指定するKeyValueは、それぞれAutoStoptrueにします。
ついでに'body': json.dumps('Hello from Lambda!')'body': json.dumps('AutoStop Enabled!')にします。


修正後のコードは以下のとおりです。

import json
import boto3

def set_tags_for_ec2(instance_id):
    
    client = boto3.client('ec2')
    # EC2にタグ付けをする
    response = client.create_tags(
        Resources=[
            instance_id,
        ],
        Tags=[
            {
                'Key': 'AutoStop',
                'Value': 'true'
            },
        ]
    )

def lambda_handler(event, context):
    
    set_tags_for_ec2(event["detail"]["instance-id"])
    
    # TODO implement
    return {
        'statusCode': 200,
        'body': json.dumps('AutoStop  Enabled!')
    }

 

 

上記のとおりにコードを修正できたら保存してAWS Lambdaの設定は完了です。

4.動作確認

まずはEC2にお目当てのタグが付与されていないことを確認。
今回は5台同時に起動してみます。
以下の画像では1台だけですが、すべてのEC2にお目当てのタグが付与されていました。(わーい)

これでEC2がrunning状態に遷移するたびにタグが付与されるようになりました。
残りはこのタグを利用して自動停止を実装するのですが、Cloud Automatorの設定はこちらの記事に分かりやすくまとまっていましたのでご確認ください。

EC2などに任意のタグを付与できれば、タグで振る舞いを変えたりと様々なことができるようになります。
ここからがAWSの楽しいところですので、是非みなさまもAWS Lambdaを触ってみてはいかがでしょうか。