WindowsのEC2インスタンスでUserdataのログをCloudWatchLogsに送る

# 今回やること
WindowsのEC2のユーザーデータ実行ログ（標準出力すべて）をCloudWatchLogsに集約したい。
毎度RDPでログを覗きに行くのは非常につらい。

#### 対象の環境
- Windowsインスタンス（Windows Server 2022)
- EC2LaunchV2を使っている
- CloudWatchへ送りたいログはユーザーデータの全Powershell実行ログ

## 事前にわかっていること
#### 1.ユーザーデータの実行ログ
EC2LaunchV2の場合、Powershell実行ログは決まったファイルに出力される
> EC2Launch v2: C:\ProgramData\Amazon\EC2Launch\log\agent.log

https://repost.aws/knowledge-center/ec2-windows-troubleshoot-user-data

#### 2. CloudWatchLogsのログの集約の仕方
EC2にインストールされたCloudWatchLogsエージェントを使って、
実行ログをCloudWatchへ送ることができる。
2通りやり方がある。

- 方法1: CloudWatchエージェントが任意の指定したログファイルをポーリングし、AWS側へ送信
 
https://docs.aws.amazon.com/ja_jp/AmazonCloudWatch/latest/monitoring/CloudWatch-Agent-Configuration-File-Details.html#CloudWatch-Agent-Configuration-File-Logssection

- 方法2: CloudWatchエージェントをUserdata（Powershell）からキックして任意のログメッセージをAWSへ送信

https://aws.amazon.com/jp/blogs/infrastructure-and-automation/logging-windows-amazon-ec2-userdata-activity-in-amazon-cloudwatch/

# やってみる
2つの方法を試した。

## 方法1 任意の指定したログファイルを送信

#### Step1 ユーザーデータの確認
ユーザーデータを記述
```:UserData
<powershell>
date
</powershell>
<persist>true</persist>
```

インスタンスでログファイルの中身を確認
```log:agent.log
2023-09-24 06:44:32 Info: Script file is created at: C:\Windows\system32\config\systemprofile\AppData\Local\Temp\EC2Launch3280685567\UserScript.ps1
2023-09-24 06:44:33 Info: Error file is created at: C:\Windows\system32\config\systemprofile\AppData\Local\Temp\EC2Launch3280685567\err.tmp
2023-09-24 06:44:33 Info: Output file is created at: C:\Windows\system32\config\systemprofile\AppData\Local\Temp\EC2Launch3280685567\output.tmp
```
```output.tmp
Sunday, September 24, 2023 6:44:42 AM
```

複数の`EC2LaunchXXXXXXX`フォルダの配下にログが出来る様子。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/54100/730edd3f-8c8b-5200-8ed6-ff9115a86988.png)

これはEC2Launchの仕様ではあるが、
`agent.log`には大したUserdataの出力結果が残らない。
`output.tmp`にUserdataの標準出力が残る様子。


#### Step2 CloudWatchエージェントのインストール

手順1. Windows Server用のエージェントをインストールする。

https://docs.aws.amazon.com/ja_jp/AmazonCloudWatch/latest/monitoring/install-CloudWatch-Agent-commandline-fleet.html

手順2. ウィザードを元にConfigファイルを作成する
configをSystemsManagaerのParmaterStoreに保存でき、
そのためのIAMポリシーをインスタンスプロファイルに含めておく必要である。
CLIから対話型でConfig.jsonを生成することができる。

https://docs.aws.amazon.com/ja_jp/AmazonCloudWatch/latest/monitoring/create-cloudwatch-agent-configuration-file-wizard.html


手順3. Cloudwatchエージェントを開始する

```Powershell
cd 'C:\Program Files\Amazon\AmazonCloudWatchAgent\'
& "C:\Program Files\Amazon\AmazonCloudWatchAgent\amazon-cloudwatch-agent-ctl.ps1" -a fetch-config -m ec2 -s -c file:config.json
```
logファイルが1行ずつCloudLogsにパブリッシュされる。
1行ずつ読まれてログが発行されている様子。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/54100/8ae71be8-9580-d77d-776c-ff20a66f6eee.png)

新しく対象のログに行を追加するとCloudWatchのログも増える。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/54100/0104ed7d-3d93-46eb-8159-380d6d417dfe.png)

ただし、拡張子が`.tmp`のファイルだとCloudWatchエージェントで読み取ってくれなかった。
これではやりたいこと(Userdataの標準出力すべてをCloudWatchへ集約)ができない。


#### Step3 ユーザーデータに1行加える(Start-Transcript)。
Powershellの`Start-Transcript`を使う。

https://learn.microsoft.com/ja-jp/powershell/module/microsoft.powershell.host/start-transcript?view=powershell-7.3

1行設定を加える
```Powershell
<powershell>
Start-Transcript "C:\Users\Administrator\Desktop\output.log"
date
</powershell>
<persist>true</persist>
```

CloudWatchAgentでこのログを参照するよう設定し、サービスを再スタートする。
```json:config.json
{
	"logs": {
		"logs_collected": {
			"files": {
				"collect_list": [
					{
						"file_path": "C:\\Users\\Administrator\\Desktop\\output.log",
						"log_group_name": "output.log",
						"log_stream_name": "{instance_id}",
						"retention_in_days": -1
					}
				]
			}
		}
	},
```

結果：無事CloudWatchエージェントに送信できた。`Stop-Transcript`が無くても送信できた。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/54100/486b3be9-eb4e-7ad4-3701-56d3a78ddcf2.png)



## 方法2 ユーザーデータ直接からCloudWatchエージェントにログをPushする

方法1でやりたいことはできたが、一応お試しで下記ハンズオンのままやってみた。
この方法だと、`Write-LogsEntry`で明示的に指定したエントリ**だけ**Publishされる様子。
これでは「Powershell実行ログ全て」が拾えないので、この方法はドロップ。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/54100/d645b06e-2a46-f42d-ce9c-9153a956a49b.png)

## 最後に
CloudWatchエージェントを使ってEC2のWindowsインスタンスのユーザーデータ実行時の標準出力をすべて送信できるようにした。

SSMをインストールしておけば、SSMパラメータストアへのバックアップ、RunCommandで`Config.json`の設定の書き換えもできるっぽい。これ全部使えばでWindowsインスタンスの運用が大分楽になる。全部やらなくては・・・。