AWS CDK v2(TypeScript)でパブリックアクセス可能なEC2を定義する

## バージョン

- aws-cdk-lib: 2.56.0
- cdk-ec2-key-pair: 3.3.1

## TL;DR

`ec2.Instance`クラスではパブリックIPの自動割り当てが出来ない（オプションにNetworkInterfaceを定義出来ない。実装されていない。[Issue](https://github.com/aws/aws-cdk/issues/19326)はあるみたいだけどプライオリティ低い）。

```typescript
import * as ec2 from 'aws-cdk-lib/aws-ec2';
const ec2Instance = new ec2.Instance(this, 'Instance', {
    vpc,
    instanceType: ec2.InstanceType.of(
        ec2.InstanceClass.T2,
        ec2.InstanceSize.MICRO,
    ),
    machineImage: ami,
    securityGroup: securityGroup,
    keyName: key.keyPairName,
    role: role,
});
```

パブリックIPの自動割り当てには`ec2.CfnInstance`クラスを利用する必要がある。実際、`ec2.Instance`と比べて記述量がさほど変わらないのでそんなに辛くない、はず。

```typescript
import * as ec2 from 'aws-cdk-lib/aws-ec2';
const cfnInstance = new ec2.CfnInstance(this, 'CfnInstance', {
    imageId: new ec2.AmazonLinuxImage().getImage(this).imageId,
    instanceType: new ec2.InstanceType('t2.micro').toString(),
    keyName: key.keyPairName,
    networkInterfaces: [
        {
            deviceIndex: '0',
            associatePublicIpAddress: true,
            deleteOnTermination: true,
            subnetId: vpc.publicSubnets[0].subnetId,
            groupSet: [securityGroup.securityGroupId],
        },
    ],
});
```

## CDKのStack全コード

```typescript:stack.ts
import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';

import * as ec2 from 'aws-cdk-lib/aws-ec2';
import * as keypair from 'cdk-ec2-key-pair';

export class SampleStack extends cdk.Stack {
    constructor(scope: Construct, id: string, props?: cdk.StackProps) {
        super(scope, id, props);

        // Look up the default VPC
        const vpc = ec2.Vpc.fromLookup(this, 'VPC', {
            vpcId: 'sample-vpc-id',
        });

        // Create a key pair to be used with this EC2 Instance
        const key = new keypair.KeyPair(this, 'KeyPair', {
            name: 'sample-keypair',
            description: 'Key Pair created with CDK Deployment',
        });
        key.grantReadOnPublicKey;

        // Security group for the EC2 instance
        const securityGroup = new ec2.SecurityGroup(this, 'SecurityGroup', {
            vpc,
            description: 'Allow SSH (TCP port 22) in',
            allowAllOutbound: true,
        });

        // Allow SSH access on port tcp/22
        securityGroup.addIngressRule(
            ec2.Peer.anyIpv4(),
            ec2.Port.tcp(22),
            'Allow SSH Access',
        );

        const cfnInstance = new ec2.CfnInstance(this, 'CfnInstance', {
            imageId: new ec2.AmazonLinuxImage().getImage(this).imageId,
            instanceType: new ec2.InstanceType('t2.micro').toString(),
            keyName: key.keyPairName,
            networkInterfaces: [
                {
                    deviceIndex: '0',
                    associatePublicIpAddress: true, // パブリックIPの自動割り当て
                    deleteOnTermination: true,
                    subnetId: vpc.publicSubnets[0].subnetId,
                    groupSet: [securityGroup.securityGroupId],
                },
            ],
        });
    }
}
```

- 上記は、パブリックサブネットを持つ既存のVPC以下にEC2インスタンスを追加する例（既存VPCは`sample-vpc-id`というVPC-IDを用いて参照している）。
- キーペアはSecretsManagerに保存される（マネジメントコンソール上で、秘密鍵が手に入る）。
- 上記の例では任意のIPアドレスからのSSHを許可していてセキュリティ的にはよくない。