目的・やりたいこと

よくあるAWS検証環境で、AWSリソース作成時にタグを付与し忘れ、結果、作成者不明の無名リソースの残骸になっているところが多い。
作成者が意識して自分の名前などを付けてタグ付けを気をつけていることも多いが、そういった人為的なことに依存するのではなく、利用者とは関係なく透過的にタグが自動付与されると便利である、そこで、自分の名前と作成日を自動付与することで、無駄なリソースの主を検出し、なくすことでコスト削減につながる方法をインフラ技術で検討する。

条件

  • タグ付けを強制するものではないこと
  • タグ付けをしないとリソース作成が失敗するものではないこと(権限がないエラーなのかと勘違いしてしまうため)
  • タグなしリソース検索して通知するものではないこと

参考

注意事項

  • 作成者が意図的に自分の名前を付けても、そういったタグの検知はしていないため、関係なく自動的にタグが付与されます。その結果、名前タグが重複したりする可能性はあります。
  • 1つのAWSリソースにつき、1つのLambdaファンクションと1つのEventBridgeが必要になるため、リソースの数だけこれらの作成が必要になります。
  • CloudTrailの Createresourcename イベントをトリガーしているため、CloudFormationなどで自動作成されるリソースには非対応

作業の流れ

事前作業

名前や日付などのほしい情報を、CloudTrailログから取ってくる必要があります。 そのため、あらかじめ一旦AWSリソースを作成してみます。例えばEC2インスタンスを実際に作成し、CloudTrailログからイベント名=RunInstances でその作成時のログを検索します。
ここで欲しいのは、userIdentity と responseElements の中身です。
userIdentity からは、userName と creationDate が取れます。

    "userIdentity": {
        "type": "IAMUser",
        "principalId": "AIDAXXZWF2FK5CCR5JNK7",
        "arn": "arn:aws:iam::532152701269:user/nozaki",
        "accountId": "532152701269",
        "accessKeyId": "ASIAXXZWF2FK5ZIDSFXB",
        "userName": "nozaki",
        "sessionContext": {
            "sessionIssuer": {},
            "webIdFederationData": {},
            "attributes": {
                "creationDate": "2022-09-09T01:20:24Z",
                "mfaAuthenticated": "false"
            }
        }

responseElements からは、instanceId が取れます。

    "responseElements": {
        "requestId": "4b8dbf8b-a97c-49fd-b1f7-ac7d8669680a",
        "reservationId": "r-09a240f39d6e3d1a4",
        "ownerId": "532152701269",
        "groupSet": {},
        "instancesSet": {
            "items": [
                {
                    "instanceId": "i-0ffb1f94279267ad6",
                    "imageId": "ami-0f36dcfcc94112ea1",
                    "instanceState": {
                        "code": 0,
                        "name": "pending"
                    },
  1. Lambda関数を作成します。

    ランタイムはPython、ロールは実行に必要な権限を付与したものをあらかじめ作成しておきます。
  2. ソースコードは、例えばEC2インスタンスの作成なら次のようになります。
import boto3, json

def lambda_handler(event, content):

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

    # インスタンス作成者の情報
    userIdentity = event['detail']['userIdentity']
    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!')
    }

上記のCloudTrailのログから、リソースである instanceId、ユーザー名である userName、作成日である creationDate の場所を確認し、コード内で指定します。

boto3パッケージのEC2クラスのclientのcreate_tagsメソッドで実際にタグ付けを行います。ここで上記で取ってきた userName や creationDate を指定しています。

3. EventBridgeルールを作成します。

イベントパターン

{
  "source": ["aws.ec2"],
  "detail-type": ["AWS API Call via CloudTrail"],
  "detail": {
    "eventSource": ["ec2.amazonaws.com"],
    "eventName": ["RunInstances"]
  }
}

ターゲット:先ほど作成したLambda関数を指定します。

では実際に動きを見てみよう

もう一度EC2インスタンスを作成してみて、作成後、自分の名前と日付が付与されることを確認します。

タグが付いていない場合は失敗です。CloudWatchログでトラシューしましょう。細かいエラー箇所が指摘してくれます。
以下のようになっていれば成功です。

(カスタマイズ等について)

今回は代表的なEC2インスタンスリソースを例に取り上げました。それ以外にも以下のリソースで自動タグ付けの成功を確認しています。

  • セキュリティグループ
  • キーペア
  • AMI
  • EBS

他のリソースに流用する際も流れは同じで、responseElements内にあるリソース識別IDが変わってくるくらいです。