ClassicLinkを設定していないEC2 Classicインスタンスを一覧する

## tl;dr

EC2 Classic で稼働しているインスタンスについて、ClassicLinkを設定しないインスタンスを一覧する

## 背景

インクリメンツでは一部EC2 Classic環境のインスタンスが稼働しています。これらのClassicLinkの状況をチェックしたいのですが、以下の状況から1つのAPIで実現できず、AWS CLIでは厳しい。

- `DescribeInstances` では、EC2 Classicで稼働しているインスタンスのみを取得するのができなさそう
    - filterでは実現できなさそうだし、queryでは結構つらい
    - ClassicLinkの状態は取得できない
- `DescribeClassicLinkInstances` というAPIはありますが、これはClassicLink設定済みのインスタンスしか返却されない

ということでコードを書いた。
AWS Config の Custom Rule を作れば、新しくインスタンスが起動してきてもチェックすることができて安心。

## EC2 Classicのインスタンスを列挙する

いくつか方法はありますが、`VpcId` の要素が無いのは Classic インスタンスです。

```python3
import boto3

ec2 = boto3.client('ec2')
paginator = ec2.get_paginator('describe_instances')
response_iterator = paginator.paginate()

for response in response_iterator:
    for reservations in response['Reservations']:
        for instance in reservations['Instances']:
            if instance.get('VpcId') is not None:
                continue

            instanceId = instance['InstanceId']
            imageId = instance['ImageId']
            state = instance['State']['Name']
            name = dict([[x['Key'], x['Value']] for x in instance.get('Tags')]).get('Name')

            print("\t".join([imageId, instanceId, state, name]))
```

タグの要素は `[{Key: 'key', Value: 'value'}, ..]` とリストの中に辞書が入っているのでリスト内包表記で `[['key', 'value'], ...]` の形式にして`dict()`で辞書にした。

## ClassicLink未設定のインスタンスを列挙

ClassicLinkが未設定のインスタンスは、EC2 Classicインスタンス一覧から、ClassicLinkインスタンス一覧を引いたもの。

```python3
import sys
import boto3

ec2 = boto3.client('ec2')

paginator = ec2.get_paginator('describe_instances')
response_iterator = paginator.paginate()

classicInstances = dict()

# fetch classic instances
for response in response_iterator:
    for reservations in response['Reservations']:

        for instance in reservations['Instances']:
            if instance.get('VpcId') is not None:
                continue

            instanceId = instance['InstanceId']
            imageId = instance['ImageId']
            state = instance['State']['Name']
            name = dict([[x['Key'], x['Value']] for x in instance.get('Tags')]).get('Name')

            classicInstances[instanceId] = {'imageId': imageId, 'state': state, 'name': name}

# fetch ClassicLink-ed instances
response = ec2.describe_classic_link_instances()

classicLinkInstances = dict()

for instance in response['Instances']:
    classicLinkInstances[instance['InstanceId']] = instance

# check ClassicLink not enabled instances
notActivatedInstanceIds = set(classicInstances.keys()) -  set(classicLinkInstances.keys())

print("# ClassicLink is not enabled")
for instanceId in notActivatedInstanceIds:
    print(classicInstances[instanceId])
```

`describe_classic_link_instances` には paginator が無いので、上のコードだとインスタンスが多い場合は駄目ですね。残念。


## ClassicLinkが設定されているインスタンスの情報取得

これは、`describe-classic-link-instances` でいけるので、AWS CLIで。

```
aws ec2 describe-classic-link-instances \
--query 'Instances[].{InstanceId:InstanceId,VpcId:VpcId,Group:Groups[0].GroupId,Name:Tags[?Key==`Name`].Value|[0]}' \
--output table
```

```
aws ec2 describe-classic-link-instances --query 'Instances[].{InstanceId:InstanceId,VpcId:VpcId,Group:Groups[0].GroupId,Name:Tags[?Key==`Name`].Value|[0]}' --output table
---------------------------------------------------------------------------------------------------
|                                  DescribeClassicLinkInstances                                   |
+-------------+----------------------+-------------------------------------------+----------------+
|    Group    |     InstanceId       |                   Name                    |     VpcId      |
+-------------+----------------------+-------------------------------------------+----------------+
|  sg-c4effff|  i-04c1410db8deffff |  classic-foo-bar-baz                        |  vpc-16a2ffff  |
```

Nameタグを取ってくるのが面倒。