EC2へのコンソールアクセスをsession managerで行うようにするIAMロール・ポリシーをAWS CDKで作って付与する

前回EC2を立ち上げたが、このEC2への接続手段はまだ用意しなかった。

ssh接続するためにポート開放、鍵作成を行うのが標準的な方法かもしれないが
せっかくなのでSession Manager経由で接続できるようにしたい。

Session Managerを使うと、IAMユーザー単位で接続ユーザーの認可ができるため
セキュアであるし、セキュリティ上の管理が楽になる。

一方SSHでは鍵管理を別途行う必要があるし、鍵をどこかに保管しておくといった行為は
CDK等IaCでEC2の作成・削除を繰り返すこととの相性が良くない。
せっかくIaCでステートレスなインフラ管理の夢を描いているのに、鍵を失えないという煩雑な要素が残ってしまう。

## 関連資料
* [セッションマネージャーを使って鍵ストレスの無いEC2アクセス！ | DevelopersIO](https://dev.classmethod.jp/articles/ec2-access-with-session-manager/)
    * 画面上での操作を参考にして、必要なCDK上のリソースを見積もって作っていく。

## 前提
* aws cdkでEC2を起動している
    * [AWS CDKでVPCを作りEC2を立ち上げる #vpc - Qiita](https://qiita.com/ashiwa/items/be56264c72964c859ad0)
    * ここでEC2を立ち上げるためのaws cdkのコードがあるので、ここへ追記していく
* session manager pluginがインストールしてある
    * `brew install --cask session-manager-plugin`

```bash
$ session-manager-plugin --version
1.2.633.0
```

## SSMエージェントインストール（はしなくていい）
今回選択したAMIのamazonlinux2023には元からssm agentが入ってるはず。
他のOSだとインストール要。

## IAMロールの作成・session managerのためのポリシーを追加
そもそもCDKではデフォルトでIAMロールが作成されている。

> https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_ec2.Instance.html#role

どんなデフォルトのIAMロールが作成されているか、確認してみる。

```bash
$ cdk synth | grep -A 14 InstanceRole.\*:
  SampleCdkEc2NginxInstanceInstanceRole7F91D057:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Statement:
          - Action: sts:AssumeRole
            Effect: Allow
            Principal:
              Service: ec2.amazonaws.com
        Version: "2012-10-17"
      Tags:
        - Key: Name
          Value: SampleCdkEc2NginxStack/SampleCdkEc2NginxInstance
    Metadata:
      aws:cdk:path: SampleCdkEc2NginxStack/SampleCdkEc2NginxInstance/InstanceRole/Resource

```

といった感じ。
EC2インスタンスがこのIAMロールをAssumeすることができる、というだけのロールで特に権限は与えられていない。
これを代替する形でIAMロールを作成してアタッチする。

### IAMロール作成
```ts
    //IAM Role
    const role = new iam.Role(this, "SampleCdkEc2NginxRole", {
      assumedBy: new iam.ServicePrincipal("ec2.amazonaws.com"),
    });
    // allow using sesson manager
    role.addManagedPolicy(iam.ManagedPolicy.fromAwsManagedPolicyName("AmazonSSMManagedInstanceCore"));
```

### EC2インスタンスにアタッチ
```ts
    const instance = new ec2.Instance(this, "SampleCdkEc2NginxInstance", {
      vpc,
      instanceType: new ec2.InstanceType("t3a.micro"),
      machineImage: ec2.MachineImage.latestAmazonLinux2023(),
      role: role, // 追加
    });
```

## 作成予定のリソースを確認する

```bash
$ cdk diff
# 略

Resources
[+] AWS::EC2::VPC SampleCdkEc2NginxVpc SampleCdkEc2NginxVpc79B44740
[+] AWS::EC2::Subnet SampleCdkEc2NginxVpc/publicSubnet1/Subnet SampleCdkEc2NginxVpcpublicSubnet1SubnetC6BE7495
[+] AWS::EC2::RouteTable SampleCdkEc2NginxVpc/publicSubnet1/RouteTable SampleCdkEc2NginxVpcpublicSubnet1RouteTable69E390ED
[+] AWS::EC2::SubnetRouteTableAssociation SampleCdkEc2NginxVpc/publicSubnet1/RouteTableAssociation SampleCdkEc2NginxVpcpublicSubnet1RouteTableAssociation64BFC4A8
[+] AWS::EC2::Route SampleCdkEc2NginxVpc/publicSubnet1/DefaultRoute SampleCdkEc2NginxVpcpublicSubnet1DefaultRoute7AA3AF5C
[+] AWS::EC2::InternetGateway SampleCdkEc2NginxVpc/IGW SampleCdkEc2NginxVpcIGWE2F2A3CC
[+] AWS::EC2::VPCGatewayAttachment SampleCdkEc2NginxVpc/VPCGW SampleCdkEc2NginxVpcVPCGWA41DCC75
[+] Custom::VpcRestrictDefaultSG SampleCdkEc2NginxVpc/RestrictDefaultSecurityGroupCustomResource SampleCdkEc2NginxVpcRestrictDefaultSecurityGroupCustomResource337ECAE3
[+] AWS::IAM::Role Custom::VpcRestrictDefaultSGCustomResourceProvider/Role CustomVpcRestrictDefaultSGCustomResourceProviderRole26592FE0
[+] AWS::Lambda::Function Custom::VpcRestrictDefaultSGCustomResourceProvider/Handler CustomVpcRestrictDefaultSGCustomResourceProviderHandlerDC833E5E
[+] AWS::IAM::Role SampleCdkEc2NginxRole SampleCdkEc2NginxRoleE5BA6A17
[+] AWS::EC2::SecurityGroup SampleCdkEc2NginxInstance/InstanceSecurityGroup SampleCdkEc2NginxInstanceInstanceSecurityGroup0DD5BDE2
[+] AWS::IAM::InstanceProfile SampleCdkEc2NginxInstance/InstanceProfile SampleCdkEc2NginxInstanceInstanceProfile2CB6865B
[+] AWS::EC2::Instance SampleCdkEc2NginxInstance SampleCdkEc2NginxInstance262C66EB
```

想定通り、`[+] AWS::IAM::Role SampleCdkEc2NginxRole SampleCdkEc2NginxRoleE5BA6A17` が追加されている

## session managerで接続してみる

```bash
# インスタンスIDを取得
$ aws ec2 describe-instances --query 'Reservations[].Instances[].[InstanceId,State.Name]' --output text
# i-0562c7fbf3f2308f4     running

# session managerで接続
$ aws ssm start-session --target i-0562c7fbf3f2308f4 


Starting session with SessionId: xxxxx-nqy4g3pcpr7ejki54hb6ksrgum
# 以降はEC2の上での操作

sh-5.2$ whoami
ssm-user # ssm-userとして入っている

sh-5.2$ sudo su - ec2-user # 普段ec2に入って触っている使うユーザーへ切り替え

Last failed login: Sat Jun 22 12:10:15 UTC 2024 on pts/0
There was 1 failed login attempt since the last successful login.

[ec2-user@ip-10-0-0-81 ~]$ whoami
ec2-user # ec2-userとして入れた

[ec2-user@ip-10-0-0-81 ~]$ sudo -s
[root@ip-10-0-0-81 ec2-user]# whoami
root # rootになれた
```

これで、普段EC2にSSHして入るところをsession managerで入れるようになった。


## （おまけ）後始末
実験終えれたので削除しておく。

```bash
$ cdk destroy

Are you sure you want to delete: SampleCdkEc2NginxStack (y/n)? y
SampleCdkEc2NginxStack: destroying... [1/1]

 ✅  SampleCdkEc2NginxStack: destroyed
```
## まとめ
session manager経由でEC2のシェルに入ることができた。