AWS SSMのSession Manager機能の利用と接続パターン

# はじめに
AWS Systems Manager(SSM)内の機能の1つであるSessionManagerを利用した
EC2インスタンスへの接続を使用する機会があったので、
検証がてら導入手順や接続パターンを記事としてまとめてみました。

https://docs.aws.amazon.com/ja_jp/systems-manager/latest/userguide/session-manager.html

元々SessionManagerを介してWebコンソールまたはAWS CLIでEC2インスタンスへアクセスすることで、
インターネット側へSSHポートを開けることなくアクセスすることが可能でした。
また、接続元PCの任意のローカルポートとEC2インスタンス側のSSHポートでセッションを張ることで、
TeraTerm等のSSHクライアントやWinSCP等のファイル転送ソフトを簡単に利用することが可能です。

加えて2022年11月にポートフォワーディングの機能が追加され、
SSMAgentを導入できないEC2インスタンスやRDS等のサービスに対して、
SSMAgent導入済みのマネージドノード経由でトンネルを張ることも可能になっています。

https://aws.amazon.com/jp/blogs/news/use-port-forwarding-in-aws-systems-manager-session-manager-to-connect-to-remote-hosts-jp/

## 検証方針
今回はAWS CLIでセッション作成しTeraTermを用いての接続と
ポートフォワーディングでの接続が主目的であるため、
WebコンソールやAWS CLIからssm-userでログインする方法及び権限は対象外とします。
また、SessionManager自体のログ出力、KMSを用いたセッションの暗号化は行いません。
検証で利用するリソースについては東京リージョン(ap-northeast-1)上に構築します。

今回、接続先パターンに応じてフェーズを4段階に分けSessionManagerの機能を検証します。
- フェーズ1:パブリックサブネット内のEC2インスタンスに対してのSSMAgentの導入とアクセス
- フェーズ2:プライベートサブネット内のEC2インスタンスに対してのSSMAgentの導入とアクセス
- フェーズ3:ポートフォワーディングにより、
SSMAgent未導入のEC2インスタンスに対してリモートデスクトップアクセス
- フェーズ4:EC2インスタンス構築時にSSMAgentの自動インストール

フェーズ4に関しては、SSMAgent導入時に結局別のサーバからSSHで接続する必要が出てきてしまうため、
どうせなら初めからSessionManager経由でアクセスできるようにした方がスマートであるという考えのもと、
自動でSSMAgentのインストールを行い、EC2インスタンス初回起動時から
SessionManager経由でアクセスできる環境にします。

前提環境となるVPCやEC2インスタンス構築方法、AWS CLIの設定方法等、
基本的な操作は割愛します。

## 構成
### 接続元端末
- OS:Windows 11
- AWS CLI:2.15.0
- SessionManagerPlugin:1.2.536.0
- SSHクライアント:TeraTerm 5.0

### AWS構成
- EC2インスタンス(Linux):Red Hat Enterprise Linux 9 (HVM)
AMI:ami-0a3299a47e8a9111b
インスタンスサイズ:t2.micro
- EC2インスタンス(WindowsServer):Microsoft Windows Server 2022 Base
AMI:ami-0ad71230122734dd0
インスタンスサイズ:t2.micro

OSとしてRHELを選択している理由は、AmazonLinuxでは
デフォルトでSSMAgentが導入されているため、SSMAgent導入の検証ができないためです。

---
- フェーズ1構成図
![SSM_Phase1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/561546/f121f5ab-6dc7-073f-db6b-edd674e8907c.png)

- フェーズ2構成図
![SSM_Phase2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/561546/3f443daa-04ac-2782-bd94-310473c841e2.png)

- フェーズ3構成図
![SSM_Phase3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/561546/485c2cf3-5ca9-1bc7-af94-673a14c5a11b.png)

- フェーズ4構成図
フェーズ2と同じになるため省略

## SSM SessionManager要件調査
公式ドキュメントに記載があるので必要な要件を確認します。

https://docs.aws.amazon.com/ja_jp/systems-manager/latest/userguide/session-manager-prerequisites.html

https://docs.aws.amazon.com/ja_jp/systems-manager/latest/userguide/session-manager-working-with-install-plugin.html

### 要約
- 接続先となるEC2インスタンス(マネージドノード)にSSMAgentの導入が必要。
- 接続元となる端末にAWS CLIの導入・設定が必要。
- AWS CLI実行ユーザーには適切な権限(後述)を記載したIAMポリシーの付与が必要。
- AWS CLIからセッションを開始するためには接続元となる端末にSessionManagerPluginの導入が必要。
- マネージドノードから以下のエンドポイントへのHTTPS(443)アウトバウンドの許可が必要。
    - ec2messages.[region].amazonaws.com
    - ssm.[region].amazonaws.com
    - ssmmessages.[region].amazonaws.com
- マネージドノードとなるEC2インスタンスには
AWS管理ポリシー"AmazonSSMManagedInstanceCore"を付与したIAMロールのアタッチが必要。

## フェーズ1:パブリックサブネット内のEC2インスタンスに対してのSSMAgentの導入とアクセス
![SSM_Phase1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/561546/f121f5ab-6dc7-073f-db6b-edd674e8907c.png)

### EC2インスタンス用IAMロール作成
EC2インスタンス用にAWS管理ポリシー"AmazonSSMManagedInstanceCore"を
付与したIAMロールを作成します。

- アタッチするポリシー
![SSM_IAM_01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/561546/85d39605-7392-cdcd-7c7d-63fe94c07a59.png)

- 信頼されたエンティティ

``` AssumeRolePolicy.json
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "Service": "ec2.amazonaws.com"
            },
            "Action": "sts:AssumeRole"
        }
    ]
}
```

### AWS CLI用IAMポリシー作成
AWS CLIからSSMのセッションを操作するため、公式ドキュメントの記載を参考に
今回の検証に必要な権限を記載したIAMポリシーを作成し、AWS CLIの実行IAMユーザーにアタッチします。

https://docs.aws.amazon.com/ja_jp/systems-manager/latest/userguide/getting-started-restrict-access-examples.html

``` Policy-Allow-SSMSession.json
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "StartSession",
            "Effect": "Allow",
            "Action": [
                "ssm:StartSession"
            ],
            "Resource": [
                "*"
            ]
        },
        {
            "Sid": "TerminateSession",
            "Effect": "Allow",
            "Action": [
                "ssm:TerminateSession",
                "ssm:ResumeSession"
            ],
            "Resource": [
                "arn:aws:ssm:*:*:session/${aws:userid}-*"
            ]
        }
    ]
}
```
接続先となるEC2インスタンスは絞らず、自分が開始したセッションのみ再開と終了を可能としています。
接続可能なEC2インスタンスを指定する場合は公式ドキュメントの通り
`"Sid": "StartSession"`内の`"Resource":`内にEC2インスタンスのIDを記載します。

### セキュリティグループ(SG)の作成
以下のルールを記載したSGを作成します。
EC2インスタンスに対してSSMAgentを導入するため、一旦アクセス元端末からSSHポートでの通信を許可する必要があります。

- インバウンドルール

| タイプ | プロトコル | ポート範囲 | ソース |
|:-:|:-:|:-:|:-:|
| SSH | TCP | 22 | [アクセス元環境グローバルIP] |

- アウトバウンドルール

| タイプ | プロトコル | ポート範囲 | 送信先 |
|:-:|:-:|:-:|:-:|
| HTTPS | TCP | 443 | 0.0.0.0/0 |

### EC2インスタンス準備
フェーズ1の構成図の通り、パブリックサブネット上にIGWを経由して外部からSSHでアクセス可能なEC2インスタンスを作成します。
EC2インスタンスに対しては、ここまでで作成したEC2インスタンス用のIAMロールとSGをアタッチします。
IAMロールについて、後からEC2インスタンスにアタッチする場合はアタッチした後再起動が必要です。

作成したEC2インスタンスに対してログイン完了後、公式ドキュメント記載の通りSSMAgentのインストールを行います。

https://docs.aws.amazon.com/ja_jp/systems-manager/latest/userguide/sysman-manual-agent-install.html

https://docs.aws.amazon.com/ja_jp/systems-manager/latest/userguide/agent-install-rhel-8-9.html

``` SSMAgentInstall_Pub.log
[ec2-user@ip-192-168-0-4 ~]$ sudo dnf list --installed | grep amazon-ssm-agent
[ec2-user@ip-192-168-0-4 ~]$
[ec2-user@ip-192-168-0-4 ~]$
[ec2-user@ip-192-168-0-4 ~]$ sudo dnf install -y https://s3.amazonaws.com/ec2-downloads-windows/SSMAgent/latest/linux_amd64/amazon-ssm-agent.rpm
Updating Subscription Management repositories.

(省略)

Dependencies resolved.
===============================================================================================
 Package  Architecture  Version  Repository  Size
===============================================================================================
Installing:
 amazon-ssm-agent  x86_64  3.2.1798.0-1  @commandline  25 M

Transaction Summary
===============================================================================================
Install  1 Package

(省略)

Installed products updated.

Installed:
  amazon-ssm-agent-3.2.1798.0-1.x86_64

Complete!
[ec2-user@ip-192-168-0-4 ~]$
[ec2-user@ip-192-168-0-4 ~]$
[ec2-user@ip-192-168-0-4 ~]$ sudo dnf list --installed | grep amazon-ssm-agent
amazon-ssm-agent.x86_64  3.2.1798.0-1  @@commandline
[ec2-user@ip-192-168-0-4 ~]$
[ec2-user@ip-192-168-0-4 ~]$ sudo systemctl status amazon-ssm-agent
● amazon-ssm-agent.service - amazon-ssm-agent
     Loaded: loaded (/etc/systemd/system/amazon-ssm-agent.service; enabled; preset: disabled)

(省略)

[ec2-user@ip-192-168-0-4 ~]$
```

SSMAgentのインストールが完了し、サービスが起動していると、
[Systems Manager] > [フリートマネージャー] > [マネージドノード]内に表示されます。
![SSM_EC2_01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/561546/cef1cf18-1cb7-38e2-b93f-898b343c9b4b.png)

### 接続元端末準備
公式ドキュメント記載の通り接続元端末にAWS CLIとSessionManagerPluginのインストールを行います。
AWS CLIは作成したAWS CLI用IAMポリシーをアタッチしたIAMユーザーで実行されるように設定を行います。

https://docs.aws.amazon.com/ja_jp/cli/latest/userguide/getting-started-install.html

https://docs.aws.amazon.com/ja_jp/systems-manager/latest/userguide/install-plugin-windows.html

### EC2インスタンスへの接続
EC2インスタンスにアタッチしたSGから、SSMAgentインストールのために
一時的に許可していたインバウンドのSSH許可ルールを削除します。
![SSM_EC2_02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/561546/de0762e4-06bd-4eeb-43b2-a8751e1f7848.png)

PowerShellもしくはコマンドプロンプトより、以下のコマンドを実行してSSMのセッションを開始します。

https://docs.aws.amazon.com/ja_jp/systems-manager/latest/userguide/session-manager-working-with-sessions-start.html#sessions-start-port-forwarding

```
# PowerShellで実行
aws ssm start-session `
    --target [インスタンスID] `
    --document-name AWS-StartPortForwardingSession `
    --parameters portNumber="[SSH待ち受けポート]",localPortNumber="[ローカル任意ポート]"
```


実行例として[SSH待ち受けポート]を22、[ローカル任意ポート]を56789として
コマンドを実行しました。
`Waiting for connections...`まで表示されていれば問題ありません。

``` PowerShell.log
PS C:\Users\[Winユーザー名]> aws ssm start-session `
>>     --target [インスタンスID] `
>>     --document-name AWS-StartPortForwardingSession `
>>     --parameters portNumber="22",localPortNumber="56789"

Starting session with SessionId: [AWS CLI実行IAMユーザー名+ID]
Port 56789 opened for sessionId [AWS CLI実行IAMユーザー名+ID].
Waiting for connections...
```

社内イントラ等の環境で、接続元端末がプロキシサーバーを利用している場合は、
環境変数にプロキシサーバ指定したうえでAWS CLIコマンドを実行する必要があります。

https://docs.aws.amazon.com/ja_jp/cli/latest/userguide/cli-configure-proxy.html

実行中のPowerShellを維持したままTeraTerm上からEC2インスタンスに対してアクセスを行います。
[ホスト]に`localhost`もしくはローカルループバックアドレスを指定し、
[TCPポート]は`localPortNumber=`で指定したものと同じポート番号を指定します。

![SSM_EC2_03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/561546/7b19e909-eacc-beae-33a8-aec8359da234.png)

環境構築が問題なく完了していれば、TeraTerm上からユーザー認証を行ってEC2インスタンスにログインできます。

![SSM_EC2_04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/561546/9d50380e-0f37-b270-7bf1-42bc74777b06.png)

サーバからログアウトし、PowerShell上で実行していた`aws ssm start-session`コマンドを
Ctrl+Cで停止して終了させます。

以上がフェーズ1の内容です。

## フェーズ2:プライベートサブネット内のEC2インスタンスに対してのSSMAgentの導入とアクセス
![SSM_Phase2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/561546/3f443daa-04ac-2782-bd94-310473c841e2.png)
フェーズ1にて一通りの流れや設定の妥当性を検証できたため、
フェーズ2としてプライベートサブネット上のEC2インスタンスへ
SSMAgentの導入とアクセスを行います。

フェーズ1では、EC2インスタンスがパブリックサブネット上に存在し、
以下のSSMが必要とするエンドポイントに対してIGW経由でアクセスしていました。
- ec2messages.[region].amazonaws.com
- ssm.[region].amazonaws.com
- ssmmessages.[region].amazonaws.com

フェーズ2では、EC2インスタンスがプライベートサブネット上に存在しているため、
VPC Endpointを作成し、VPC Endpoint経由でSSMが必要とするエンドポイントと通信させる必要があります。

### VPC Endpoint用SG作成
VPC Endpoint作成前に、VPC Endpoint用SGを作成しておきます。

- インバウンドルール

| タイプ | プロトコル | ポート範囲 | ソース |
|:-:|:-:|:-:|:-:|
| HTTPS | TCP | 443 | [VPC CIDR] |

- アウトバウンドルール
なし

### VPC Endpoint作成
以下のSSMが必要とするサービスのVPC Endpointを作成し、
VPC Endpoint用SGをアタッチしてプライベートサブネットへ配置します。
- ec2messages.[region].amazonaws.com
- ssm.[region].amazonaws.com
- ssmmessages.[region].amazonaws.com

![SSM_EC2_05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/561546/83f230f9-445c-6c80-4c71-98798654e74d.png)

### EC2インスタンス準備
フェーズ1同様、プライベートサブネット上にEC2インスタンスを構築し、
SSMAgentをインストールします。
しかし、EC2インスタンスがプライベートサブネット上に存在するため、
フェーズ1のように最初にSSHでアクセスを行うことができません。
対処方法としては一時的にEIPの付与とルートテーブルへのIGWへのルーティングを追加してアクセスを行うか、
パブリックサブネット上に踏み台を立ててアクセスを行う方法が思いつきます。

今回はフェーズ1で構築したEC2インスタンスを踏み台として利用し、
プライベートサブネット上のEC2インスタンスにログインする手法を採用します。
踏み台からアクセスするためのSGの設定変更は適宜行います。

踏み台経由でプライベートサブネット上のEC2インスタンスにログインできたら、
フェーズ1同様に以下のコマンドでSSMAgentのインストールを試みます。
`sudo dnf install -y https://s3.amazonaws.com/ec2-downloads-windows/SSMAgent/latest/linux_amd64/amazon-ssm-agent.rpm`

しかし、EC2インスタンスがプライベートサブネット上に存在するため、
リポジトリアクセス時にタイムアウトして指定した入手先へアクセスできません。

``` TeraTerm.log
[ec2-user@ip-192-168-2-4 ~]$ sudo dnf install -y https://s3.amazonaws.com/ec2-downloads-windows/SSMAgent/latest/linux_amd64/amazon-ssm-agent.rpm
Updating Subscription Management repositories.
Unable to read consumer identity

This system is not registered with an entitlement server. You can use subscription-manager to register.

Red Hat Enterprise Linux 9 for x86_64 - AppStream from RHUI (RPMs)                  0.0  B/s |   0  B     06:00
Errors during downloading metadata for repository 'rhel-9-appstream-rhui-rpms':
  - Curl error (28): Timeout was reached for https://rhui.ap-northeast-1.aws.ce.redhat.com/pulp/mirror/content/dist/rhel9/rhui/9/x86_64/appstream/os [Connection timed out after 30001 milliseconds]
  - Curl error (28): Timeout was reached for https://rhui.ap-northeast-1.aws.ce.redhat.com/pulp/mirror/content/dist/rhel9/rhui/9/x86_64/appstream/os [Connection timed out after 30000 milliseconds]
Error: Failed to download metadata for repo 'rhel-9-appstream-rhui-rpms': Cannot prepare internal mirrorlist: Curl error (28): Timeout was reached for https://rhui.ap-northeast-1.aws.ce.redhat.com/pulp/mirror/content/dist/rhel9/rhui/9/x86_64/appstream/os [Connection timed out after 30000 milliseconds]
[ec2-user@ip-192-168-2-4 ~]$
```

他のパッケージであればNATGatewayを構築・ルーティング設定を行い、インターネット側へ出られるようにする必要がありますが、SSMAgentのパッケージはS3上に存在するため、
S3のVPC Endpointを用意することでS3と通信を行える環境を用意します。

![SSM_EC2_06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/561546/7e7619cc-29a4-b117-049e-ca67814ea2ee.png)

使用するコマンドとしては下記ドキュメントを参考にURLにリージョンを追加します。

https://docs.aws.amazon.com/ja_jp/systems-manager/latest/userguide/agent-install-rhel-8-9.html

RHELのAMIの場合、dnf(yum)利用時にインターネット側へ認証が走り、
S3のVPC Endpointを構築しても結果としてアクセスできずにタイムアウトしてしまいます。
この認証問題が解消できずに悩んでいたのですが、下記記事に解決方法がありました。
`--disablerepo="*"`オプションを追加することで、
デフォルトのリポジトリへのアクセスを無効にしています。

https://dev.classmethod.jp/articles/ssm-agent-private-install/

コマンドと実行結果
``` SSMAgentInstall_Pri.log
[ec2-user@ip-192-168-2-4 ~]$ sudo dnf --disablerepo="*" install -y https://s3.ap-northeast-1.amazonaws.com/amazon-ssm-ap-northeast-1/latest/linux_amd64/amazon-ssm-agent.rpm
amazon-ssm-agent.rpm  53 MB/s |  25 MB     00:00
Dependencies resolved.
(省略)
Installed products updated.

Installed:
  amazon-ssm-agent-3.2.2016.0-1.x86_64

Complete!
[ec2-user@ip-192-168-2-4 ~]$
[ec2-user@ip-192-168-2-4 ~]$ sudo dnf list --installed | grep amazon-ssm-agent
amazon-ssm-agent.x86_64                       3.2.2016.0-1                  @@commandline
[ec2-user@ip-192-168-2-4 ~]$
[ec2-user@ip-192-168-2-4 ~]$ sudo systemctl status amazon-ssm-agent
● amazon-ssm-agent.service - amazon-ssm-agent
     Loaded: loaded (/etc/systemd/system/amazon-ssm-agent.service; enabled; preset: disabled)
(省略)
[ec2-user@ip-192-168-2-4 ~]$
```

### EC2インスタンスへの接続
プライベートサブネット上に作成したEC2インスタンスのインスタンスIDを指定して
PowerShellからセッションを開始します。
コマンドや結果はフェーズ1と同じであるため省略します。

プライベートサブネット上にあるEC2インスタンスでも
VPC Endpoin経由でSSMやEC2のエンドポイントにアクセスできるため、
問題なくSessionManagerを使用してアクセスが可能です。

以上がフェーズ2の内容です。

## フェーズ3:ポートフォワーディングにより、SSMAgent未導入のEC2インスタンスに対してリモートデスクトップアクセス
![SSM_Phase3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/561546/485c2cf3-5ca9-1bc7-af94-673a14c5a11b.png)
新規にプライベートサブネット上にWindowsServerのEC2インスタンスを作成します。
フェーズ2で作成したプライベートサブネット上のSSMAgent導入済みのEC2インスタンスを経由して、
WindowsServerへのリモートデスクトップ(RDP)接続を行います。

### WindowsServer用SG作成・既存SG修正
WindowsServerEC2インスタンス用SGを以下の通りに作成しておきます。
WindwosServerにはSSMAgentをインストールしないため、
アウトバウンドHTTPS(443)の許可は不要です。

- インバウンドルール

| タイプ | プロトコル | ポート範囲 | ソース |
|:-:|:-:|:-:|:-:|
| RDP | TCP | 3389 | [フェーズ2で作成のEC2のIP] |

- アウトバウンドルール
なし

踏み台となるEC2インスタンスからRDPの通信が行われるため、
フェーズ2で構築したプライベートサブネット上のEC2インスタンスにアタッチされたSGに、
以下を追加します。

- アウトバウンドルール

| タイプ | プロトコル | ポート範囲 | 送信先 |
|:-:|:-:|:-:|:-:|
| RDP | TCP | 3389 | [WindowsServerEC2のIP] |

### EC2インスタンス準備
プライベートサブネット上にWindowsServerを構築します。
フェーズ1、フェーズ2とは違い、EC2用のIAMロールのアタッチは不要です。


### EC2インスタンスへの接続
コマンドは公式ドキュメント記載の通りです。

https://docs.aws.amazon.com/ja_jp/systems-manager/latest/userguide/session-manager-working-with-sessions-start.html#sessions-remote-port-forwarding

フェーズ1、フェーズ2とは違い、使用するドキュメント名が異なり、
`--target`でSSMAgent導入済みの踏み台となるEC2インスタンスのIDを指定し、
`host=`で最終的な接続先となるEC2インスタンスのホスト名またはIPを指定します。

```
# PowerShellで実行
aws ssm start-session `
    --target [SSMAgent導入済み踏み台EC2のインスタンスID] `
    --document-name AWS-StartPortForwardingSessionToRemoteHost `
    --parameters  host="[最終接続先ホスト名/IP]",portNumber="[RDP待ち受けポート]",localPortNumber="[ローカル任意ポート]"
```

実行例として[最終接続先ホスト名/IP]をWindowsServerのプライベートIP、
[RDP待ち受けポート]を3389、[ローカル任意ポート]を56789としてコマンドを実行しました。
`Waiting for connections...`まで表示されていれば問題ありません。

``` PowerShell.log
PS C:\Users\[Winユーザー名]> aws ssm start-session `
>>     --target [踏み台インスタンスID] `
>>     --document-name AWS-StartPortForwardingSessionToRemoteHost `
>>     --parameters host="192.168.3.4",portNumber="3389",localPortNumber="56789"

Starting session with SessionId: [AWS CLI実行IAMユーザー名+ID]
Port 56789 opened for sessionId [AWS CLI実行IAMユーザー名+ID].
Waiting for connections...
```

実行中のPowerShellを維持したまま接続元端末からRDPでアクセスを行います。
[コンピューター]に`localhost`もしくはローカルループバックアドレス、
加えて指定した任意のポート番号を入力しAdministratorで接続します。
![SSM_EC2_07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/561546/71b68157-6a4c-8891-8573-842d98f3d1ef.png)

環境構築が問題なく完了していれば、踏み台を経由したポートフォワーディングで
RDP接続することができます。
![SSM_EC2_08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/561546/eaab6989-c8ff-9536-22ef-13ca915a0b45.png)

以上がフェーズ3の内容です。

## フェーズ4:EC2インスタンス構築時にSSMAgentの自動インストール
フェーズ4ではフェーズ2で発生した、「プライベートサブネット上のEC2インスタンスへSSMAgentをインストールするためには、一旦別の踏み台サーバからSSHで接続を行わなければならない。」
という問題に対処します。

### 前提
プライベートサブネット上にフェーズ2で作成した4種のVPC Endpointが存在すること。

![SSM_EC2_06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/561546/7e7619cc-29a4-b117-049e-ca67814ea2ee.png)

### SSMAgentの自動インストール
対応方法は以下のAWS re:Post記事に記載がありました。
EC2インスタンス作成時にユーザーデータでコマンドを実行させる方式です。

https://repost.aws/ja/knowledge-center/install-ssm-agent-ec2-linux

プライベートサブネット上かつRHEL9の場合はフェーズ2で発生したリポジトリへの認証問題があるため、
インストールコマンドは`sudo dnf --disablerepo="*" install -y https://s3.ap-northeast-1.amazonaws.com/amazon-ssm-ap-northeast-1/latest/linux_amd64/amazon-ssm-agent.rpm`にする必要があります。

``` user-data.sh
#!/bin/bash
cd /tmp
sudo dnf --disablerepo="*" install -y https://s3.ap-northeast-1.amazonaws.com/amazon-ssm-ap-northeast-1/latest/linux_amd64/amazon-ssm-agent.rpm
sudo systemctl enable amazon-ssm-agent
sudo systemctl start amazon-ssm-agent
```

EC2インスタンス作成時のユーザーデータ欄に上記の内容を記載するか、
ファイルをアップロードしてEC2インスタンスを作成します。

![SSM_EC2_09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/561546/a8664412-ed1a-0796-ea0a-d3ecf66552c0.png)

EC2インスタンス用IAMロールをアタッチして再起動。
フェーズ1～2と同じように以下のコマンドでセッションを開始し、TeraTermで接続します。

```
# PowerShellで実行
aws ssm start-session `
    --target [インスタンスID] `
    --document-name AWS-StartPortForwardingSession `
    --parameters portNumber="[SSH待ち受けポート]",localPortNumber="[ローカル任意ポート]"
```

環境構築が問題なく完了していれば、フェーズ2同様EC2インスタンスにログインできます。
念のため`/var/log/cloud-init-output.log`を見てcloud-initのログを確認しておきます。

``` cloud-init-output.log
[ec2-user@ip-192-168-2-5 ~]$ cat /var/log/cloud-init-output.log

(省略)

Cloud-init v. 23.1.1-11.el9 running 'modules:final' at Sun, 17 Dec 2023 16:55:08 +0000. Up 15.49 seconds.
Updating Subscription Management repositories.
Unable to read consumer identity

This system is not registered with an entitlement server. You can use subscription-manager to register.

amazon-ssm-agent.rpm                             41 MB/s |  25 MB     00:00
Dependencies resolved.
================================================================================
 Package               Arch        Version              Repository         Size
================================================================================
Installing:
 amazon-ssm-agent      x86_64      3.2.2016.0-1         @commandline       25 M

Transaction Summary
================================================================================
Install  1 Package

Total size: 25 M
Installed size: 105 M
Downloading Packages:
Running transaction check
Transaction check succeeded.
Running transaction test
Transaction test succeeded.
Running transaction
  Running scriptlet: amazon-ssm-agent-3.2.2016.0-1.x86_64                   1/1
  Preparing        :                                                        1/1
  Running scriptlet: amazon-ssm-agent-3.2.2016.0-1.x86_64                   1/1
  Installing       : amazon-ssm-agent-3.2.2016.0-1.x86_64                   1/1
  Running scriptlet: amazon-ssm-agent-3.2.2016.0-1.x86_64                   1/1
Created symlink /etc/systemd/system/multi-user.target.wants/amazon-ssm-agent.service → /etc/systemd/system/amazon-ssm-agent.service.

  Verifying        : amazon-ssm-agent-3.2.2016.0-1.x86_64                   1/1
Installed products updated.

Installed:
  amazon-ssm-agent-3.2.2016.0-1.x86_64

Complete!
Cloud-init v. 23.1.1-11.el9 finished at Sun, 17 Dec 2023 16:55:22 +0000. Datasource DataSourceEc2Local.  Up 29.46 seconds

(省略)
```

以上がフェーズ4の内容です。

## まとめ
AWS Systems Manager(SSM)内の機能の1つであるSession Managerを利用した
EC2インスタンスへの接続を検証しました。

必要な要件を抑え、SSMAgentの導入さえしてしまえば
インターネット側からのSSHポート許可をせずに済むことに加え、
プライベートサブネット上のEC2インスタンスにも踏み台サーバを使うことなく
ログインすることができます。

インターネットへ通信できないRHELサーバへSSMAgentをインストールする場合は、
dnf(yum)実行時にRHELリポジトリを使用するための認証をかけにいく動作が入るため、
`--disablerepo="*"`オプションを追加することで、
デフォルトのRHELリポジトリへのアクセスを無効にして
インストールしなければならないという点がハマりポイントでした。

## その他参考記事

https://blog.serverworks.co.jp/aws-systems-manager-support-port-forwarding-remote-hosts-using-session-manager