SKY BLOGBLOG

新規作成したAWSリソースへの自動タグ付け処理

こんにちは。MSP部の佐々木です。 私はMSP部内では監視運用業務から少し離れており、ITSMS推進や社員のスキルアップ推進、Microsoft AzureやGoogle Cloudのサービス開発、社内システムの簡単な自動化など、色々やっています。 好きなAWSのサービスはAWS Lambdaです。

今回のブログでは、このLambdaを用いた簡単な自動化について書かせていただきます。

ブログのテーマ

MSP部では検証や新人研修のためにEC2インスタンスをたくさん立てますが、適切に管理できていないと誰がどんな目的で立てたものなのかがわからなくなります。

既に使用されなくなったインスタンスが消されずにずっと残っていると、例えインスタンスが稼働していなくてもEBSボリュームの課金がされてしまいますし、誰が何のために使用していたものなのかわからないと、まだ使用するかどうかもわからないため勝手に消してしまうわけにもいきません。

そこで1年半ほど前に、EC2インスタンス作成時に自動でいくつかのタグを付けてくれる処理を、AWSのサーバレスコンピューティングサービスであるLambdaを用いて実装しました。

処理の概要

AWSでリソースの作成、更新などの管理タスクを実行すると、その実行履歴がAWSの監査サービスであるCloudTrailに記録されます。

EC2インスタンス作成時のイベントもCloudTrailに記録されるため、それをイベントバスサービスのEventBridgeで拾い、Lambda関数をトリガーしてイベントの内容を渡します。

Lambda関数では、AWSのAPIを扱うことができるPythonライブラリ(Boto3)を用いて、作成されたインスタンスに対するタグ付け処理を行います。

ここでは例として、インスタンスの作成者と作成日を自動でタグ付けする処理を実装していきます。

処理を実装するまでの手順

①Lambda関数で使用するIAMロールを作成する。

必要な権限を持つポリシーはこんな感じです。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "logs:CreateLogGroup",
                "logs:CreateLogStream",
                "logs:PutLogEvents",
                "ec2:CreateTags",
            ],
            "Resource": "*"
        }
    ]
}

②EC2インスタンス作成時のイベントの中身を確認する。

EC2作成時にCloudTrailに記録されるイベントの中身がLambda関数に渡されるため、まずは実際にEC2インスタンスを作成してCloudTrailを確認します。

EC2インスタンスの作成はイベント名「RunInstances」で記録されますが、インスタンスIDで検索するのが確実です。

RunInstancesイベントの中身は以下のようなJSON形式になっています。(長いので必要な部分のみ抜粋)

{
    "eventVersion": "1.08",
    "userIdentity": {
        "type": "AssumedRole",
        "principalId": "XXXXXXXXXXXXXXXXXXXXX:ssasaki@sky365.co.jp",
        "arn": "arn:aws:sts::XXXXXXXXXXXX:assumed-role/SKY365_Dev/ssasaki@sky365.co.jp",
        "accountId": "XXXXXXXXXXXX",
        "accessKeyId": "XXXXXXXXXXXXXXXXXXXX",
        "sessionContext": {
            "sessionIssuer": {
                "type": "Role",
                "principalId": "XXXXXXXXXXXXXXXXXXXXX",
                "arn": "arn:aws:iam::XXXXXXXXXXXX:role/SKY365_Dev",
                "accountId": "XXXXXXXXXXXX",
                "userName": "SKY365_Dev"
            },
            "webIdFederationData": {},
            "attributes": {
                "creationDate": "2021-08-25T07:02:15Z",
                "mfaAuthenticated": "false"
            }
        }
    },
     ~~~長いので省略~~~

    "responseElements": {
        "requestId": "f19880c3-2d53-4385-b34d-525289a4c983",
        "reservationId": "r-094a6b0914a376e69",
        "ownerId": "XXXXXXXXXXXX",
        "groupSet": {},
        "instancesSet": {
            "items": [
                {
                    "instanceId": "i-0cc9cf3d69e73f526",
                    "imageId": "ami-09ebacdc178ae23b7",
                    "instanceState": {
                        "code": 0,
                        "name": "pending"
                    },

     ~~~長いので省略~~~

今回は、インスタンスの作成者の情報として"userIdentity"内の"arn"(もしくは"userName")、作成日の情報として"creationDate"、インスタンスIDの情報として"responseElements"内の"instanceId"の値を使用していきます。

③Lambda関数を作成する。

①で作成したロールを使用してLambda関数を作成します。

ここではPythonで処理を書いていきます。

EC2へのタグ付け処理は以下のように書きます。

import boto3

# EC2インスタンスへのタグ付け
ec2 = boto3.client('ec2')
ec2.create_tags(
    Resources=[instanceId,],
    Tags=[
        {'Key': 'key1', 'Value': 'value1'},
        {'Key': 'key2', 'Value': 'value2'},
    ]
)

PythonでLambda関数を作成する場合、②で確認したイベントの中身はハンドラーメソッド(Lambda関数が呼び出された際に実行されるメソッド)のevent引数に格納されています。(正確にはevent['detail']でイベントの中身にアクセスできます

def lambda_handler(event, content):

スカイ365の検証環境では、個人ユーザーは自分のメールアドレスと「SKY365_」から始まるロールを用いてフェデレーションアクセスをしているので、イベント内の"arn"から、メールアドレスの@前をインスタンス作成者の情報として取り出します。

# インスタンス作成者の情報
userIdentity = event['detail']['userIdentity']
if "SKY365_" in userIdentity['arn'] and  "@" in userIdentity['arn']:
    userName = userIdentity['arn'].split('/')[2].split('@')[0]  # フェデレーションアクセスの場合、メールアドレスの@の前を使用
else:
    userName = userIdentity['userName']  # IAMユーザーやその他の場合はこちらを使用する
 

イベントからインスタンス作成日の情報を取り出します。
"creationDate"はUTCになっているのですが、JSTにする処理は今回は割愛します。

# インスタンス作成日の情報
creationDate = userIdentity['sessionContext']['attributes']['creationDate'][:10]  # インスタンス作成日

完成したLambda関数のコードは以下のようになります。
インスタンス作成者の情報はOwnerをキーとするタグ、作成日の情報はCreationDateをキーとするタグとしてインスタンスに付与していきます。

import boto3, json


def lambda_handler(event, content):

    # 作成されたインスタンスのID
    instanceId = event['detail']['responseElements']['instancesSet']['items'][0]['instanceId']

    # インスタンス作成者の情報
    userIdentity = event['detail']['userIdentity']
    if "SKY365_" in userIdentity['arn'] and  "@" in userIdentity['arn']:
        userName = userIdentity['arn'].split('/')[2].split('@')[0]  # フェデレーションアクセスの場合、メールアドレスの@の前を使用
    else:
        userName = userIdentity['userName']  # IAMユーザーやその他の場合はこちらを使用する
    
    # インスタンス作成日の情報
    creationDate = userIdentity['sessionContext']['attributes']['creationDate'][:10]
    
    
    # 作成されたインスタンスへのタグ付け
    ec2 = boto3.client('ec2')
    ec2.create_tags(
        Resources=[instanceId,],
        Tags=[
            {'Key': 'Owner', 'Value': userName},
            {'Key': 'CreationDate', 'Value': creationDate},
        ]
    )

    return {
        'statusCode': 200,
        'body': json.dumps('Excuted!')
    }

④EventBridgeのルールを作成する。

RunInstancesイベントを検知してLambda関数をトリガーするルールを作成します。

イベントパターンを以下のように設定すればOKです。

トリガーの項目で、③で作成したLambda関数を指定します。

作成後、ルールが有効化されていることを確認します。

⑤動作確認

実際にEC2インスタンスを作成すると、以下のようにタグが自動で付与されます。

MSP部ではこれらのタグ以外にも、CreationDate情報から算出した使用期限日をタグで設定し、期限日が過ぎたインスタンスを別のLambda関数で拾い、Ownerに対してSlackで通知を行ったりなどしています。
最近、通知を数回無視した場合にインスタンスを自動で削除する処理も実装しました。

また、RDSやELBなどのリソースに対しても同様の処理を簡単に実装することが可能です。
自動でタグを付けるという簡単な処理だけでも、そのタグをどう使うかによって様々な運用が可能になります。

終わりに

AWS Lambdaは個人利用ではそうそう無料枠の範囲を超えることがなく、様々なAWSリソースをAPI経由で操作することができ、またそれに応じて適切なIAM権限の設定も学ぶことができるため、AWS初心者の学習に適したサービスであると感じています。

このブログで作成したLambda関数は、自分のような独学のプログラミング初心者でも簡単に書ける内容になっていますが、これだけでもリソース管理がかなり楽になるかと思います。

簡単なものであれば無料で処理を実行でき、サーバレスゆえに管理が楽ということで、導入のハードルが低いのも利点です。
是非、皆さんも触ってみてください。

関連ドキュメント

AWS Lambda

Amazon EventBridge

AWS SDK for Python (Boto3)