EC2インスタンスに関連するEBSボリュームにタグを設定するPythonコードを生成AIで

こんにちは、インサイトテクノロジーの松尾です。

本記事では、AWS アカウント内の EC2 関連リソースへのタグ付けを、Python と boto3 を使って実施した例をご紹介します。また、Python コードの生成は ChatGPT でさせてみました。

## はじめに

自チームの AWS アカウントで利用状況をコスト管理をするのにあたりタグをつける運用を行っています。厳密にやるならアカウントを分ければいいとは思いますが、利便性の確保を現時点で選択しています。
一方で、以下のようなことに困っていました。

* EC2 インスタンスにはタグは付与されているものの EBS ボリュームに設定されていない
* AMI や AMI に関連する EBS のスナップショットにタグが設定されていない
* AMI を削除するときにスナップショットを残したまま

我々のチームでは EC2 と RDS の利用が圧倒的に多く、RDS の利用でも同様の課題があるのですが、ここでは EC2 のみにフォーカスします。

## スクリプトで対応しよう

そこそこの数があったので、さすがに AWS コンソールで目検して対応していくのはどうかと思いました。私はもともとはソフトウェア開発を仕事としてやってきましたので、python で boto3 を使えば簡単にできそうなことはわかります。でも「忙しくて。。。」と理由をつけてずっと後回しにしてきたんですよね・・・。

ただいつまでも、というわけにもいかないので、試せていなかった生成 AI を使ったコード生成をやってみることに。

利用したのは ChatGPT です。

## 指定リージョン内のEC2に対して、EC2についているprojectタグとNameタグを、関連するEBSに付与するPythonコードを作成して。

すごいですね！この質問に対して適切な Python のコードを返してきました。

```python:指定リージョン内のEC2に対して、EC2についているprojectタグとNameタグを、関連するEBSに付与するPythonコードを作成して。
import boto3

def tag_ebs_volumes_from_instance_tags(region_name):
    ec2 = boto3.client('ec2', region_name=region_name)
    
    # 全EC2インスタンスの情報を取得
    response = ec2.describe_instances()
    
    # インスタンスとボリュームのID、および関連タグの収集
    volumes_to_tag = {}
    
    for reservation in response['Reservations']:
        for instance in reservation['Instances']:
            instance_id = instance['InstanceId']
            
            # インスタンスに付けられているタグを取得
            tags = {tag['Key']: tag['Value'] for tag in instance.get('Tags', [])}
            
            # `project` と `Name` タグの値を取得
            tags_to_apply = {key: tags[key] for key in ['project', 'Name'] if key in tags}
            
            if tags_to_apply:
                # インスタンスに関連付けられているボリュームIDを取得
                for volume in instance['BlockDeviceMappings']:
                    volume_id = volume['Ebs']['VolumeId']
                    
                    if volume_id not in volumes_to_tag:
                        volumes_to_tag[volume_id] = tags_to_apply
                    else:
                        # 既にボリュームにタグがある場合は、タグをマージ
                        existing_tags = volumes_to_tag[volume_id]
                        existing_tags.update(tags_to_apply)
    
    if not volumes_to_tag:
        print(f"No EBS volumes with relevant tags found in region {region_name}.")
        return
    
    # ボリュームにタグを付ける
    for volume_id, tags in volumes_to_tag.items():
        ec2.create_tags(
            Resources=[volume_id],
            Tags=[{'Key': k, 'Value': v} for k, v in tags.items()]
        )
    
    print(f"Tags have been added to the volumes: {list(volumes_to_tag.keys())}")

if __name__ == "__main__":
    region_name = 'us-west-2'  # 指定したリージョンを入力
    
    tag_ebs_volumes_from_instance_tags(region_name)
```

しかも実行方法まで詳しく。。。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3480312/03d46f4b-f06a-c810-75bd-bed6b13b883e.png)

すごいです。
ちなみに、実際に ChatGPT に与える依頼の文章は、想定通りのコードになるまで 4-5 回程度修正しています。ここはやはり出力結果を見て判断している人が使わないと、出力結果を使っていいのかダメなのかの判断が難しいのでしょうね。

boto3 のインストール、権限の設定などを準備し、リージョンを設定したところ見事に動作してくれました。

### 指定リージョン内のAMIに対して、AMIに設定されているprojectタグとNameタグを、関連するEBSスナップショットに付与するPythonコードを作成して。

今回も同様にヘルプ情報もついてます。すばらしいですね。質問の仕方も慣れたので、一発で所望のコードを得られました。

```python:指定リージョン内のAMIに対して、AMIに設定されているprojectタグとNameタグを、関連するEBSスナップショットに付与するPythonコードを作成して。
import boto3

def tag_snapshots_from_ami_tags(region_name):
    ec2 = boto3.client('ec2', region_name=region_name)
    
    # 全AMIの情報を取得
    response = ec2.describe_images(Owners=['self'])  # 自分が所有しているAMIのみを対象
    
    # AMIとスナップショットのID、および関連タグの収集
    snapshots_to_tag = {}
    
    for image in response['Images']:
        image_id = image['ImageId']
        
        # AMIに付けられているタグを取得
        tags = {tag['Key']: tag['Value'] for tag in image.get('Tags', [])}
        
        # `project` と `Name` タグの値を取得
        tags_to_apply = {key: tags[key] for key in ['project', 'Name'] if key in tags}
        
        if tags_to_apply:
            # AMIに関連付けられているスナップショットIDを取得
            for block_device in image.get('BlockDeviceMappings', []):
                if 'Ebs' in block_device:
                    snapshot_id = block_device['Ebs'].get('SnapshotId')
                    if snapshot_id:
                        if snapshot_id not in snapshots_to_tag:
                            snapshots_to_tag[snapshot_id] = tags_to_apply
                        else:
                            # 既にスナップショットにタグがある場合は、タグをマージ
                            existing_tags = snapshots_to_tag[snapshot_id]
                            existing_tags.update(tags_to_apply)
    
    if not snapshots_to_tag:
        print(f"No EBS snapshots with relevant tags found in region {region_name}.")
        return
    
    # スナップショットにタグを付ける
    for snapshot_id, tags in snapshots_to_tag.items():
        ec2.create_tags(
            Resources=[snapshot_id],
            Tags=[{'Key': k, 'Value': v} for k, v in tags.items()]
        )
    
    print(f"Tags have been added to the snapshots: {list(snapshots_to_tag.keys())}")

if __name__ == "__main__":
    region_name = 'us-west-2'  # 指定したリージョンを入力
    
    tag_snapshots_from_ami_tags(region_name)
```

## 指定リージョン内のEBSスナップショットに対して、関連するAMIが存在しないものを削除するpythonコード

最後のこちらも同様にヘルプ情報もついてます。すばらしいですね。こちらも一発で所望のコードを得られました。

```python:指定リージョン内のEBSスナップショットに対して、関連するAMIが存在しないものを削除するpythonコード
import boto3

def delete_orphaned_snapshots(region_name):
    ec2 = boto3.client('ec2', region_name=region_name)
    
    # AMIのスナップショットIDを取得
    image_snapshots = set()
    response = ec2.describe_images(Owners=['self'])
    
    for image in response['Images']:
        for block_device in image.get('BlockDeviceMappings', []):
            if 'Ebs' in block_device:
                snapshot_id = block_device['Ebs'].get('SnapshotId')
                if snapshot_id:
                    image_snapshots.add(snapshot_id)
    
    # EBSスナップショットの情報を取得
    response = ec2.describe_snapshots(OwnerIds=['self'])
    
    # 存在しないAMIに関連付けられていないスナップショットを削除
    orphaned_snapshots = []
    for snapshot in response['Snapshots']:
        snapshot_id = snapshot['SnapshotId']
        if snapshot_id not in image_snapshots:
            orphaned_snapshots.append(snapshot_id)
    
    if not orphaned_snapshots:
        print(f"No orphaned snapshots found in region {region_name}.")
        return
    
    # スナップショットを削除
    print(f"Deleting orphaned snapshots: {orphaned_snapshots}")
    for snapshot_id in orphaned_snapshots:
        ec2.delete_snapshot(SnapshotId=snapshot_id)
    
    print("Orphaned snapshots have been deleted.")

if __name__ == "__main__":
    region_name = 'us-west-2'  # 指定したリージョンを入力
    
    delete_orphaned_snapshots(region_name)
```

すごいです。ここまで簡単にコードを得られるとは思ってませんでした。実際に boto3 のドキュメントを見ながらやろうとすると、確認しながらになると思うので最低でも数10分以上はかかるでしょう。

## おわりに

本記事では、AWS アカウント内の EC2 関連リソースへのタグ付けを、Python と boto3 を使って実施した例をご紹介しました。また、Python コードの生成は ChatGPT でさせてみました。仕様を明確に質問するという質問の仕方にさえ慣れれば、他のやりたいことも簡単に実現できそうです。
最初にも書きましたが、自チームでは RDS でも同じ問題を抱えているので、RDS 側の対処も同様に行ってみたいと思います。