Amazon S3からのスクリプトの実行をやってみた。

# はじめに
`AWS Systems Manager`のユーザーガイドを眺めていたところ、「**Amazon S3からのスクリプトの実行**」という内容が目に留まったので実際にやってみました。

- [Amazon S3 からのスクリプトの実行](https://docs.aws.amazon.com/ja_jp/systems-manager/latest/userguide/integration-s3.html)

# AWS Systems Managerとは
`SSM Agent`と呼ばれるエージェントソフトがインストールされているノードに対して、パッケージ・パッチの適用管理を行ったり、対象ノードにリモートログインしたり、今回のように対象ノードに対してスクリプトを実行したりするサービスです。

別々のサービスに分けてもいいんじゃないかと思われるほど`Systems Manager`は様々な機能を提供しているサービスですが、特にリモートログイン機能は、セキュリティグループで`SSH`を許可しなくてもアクセスでき、`Systems Manager`側で操作ログや権限制御を行うことができることから踏み台サーバの代わりとしても使えるようなサービスになります。

- [AWS Systems Manager とは?](https://docs.aws.amazon.com/ja_jp/systems-manager/latest/userguide/what-is-systems-manager.html)

# 実行構成
実際に試してみたところ、`AWS Systems Manager`の`RunCommand`機能で`S3`に格納したスクリプトを実行する場合の構成としては以下のようになるようです。

```plantuml
@startuml
!include <awslib/AWSCommon>
!include <awslib/ManagementGovernance/SystemsManagerRunCommand.puml>
!include <awslib/Storage/SimpleStorageServiceBucket.puml>
!include <awslib/Compute/EC2.puml>
!include <awslib/SecurityIdentityCompliance/IdentityAccessManagementRole.puml>
!include <awslib/General/Client.puml>

top to bottom direction
title Amazon S3 からのスクリプトの実行

Client(cl,"操作PC","操作PC")
SimpleStorageServiceBucket(s3,"スクリプト格納バケット","S3")
SystemsManagerRunCommand(ssm,"スクリプト実行","SSM")
EC2(ec2,"実行対象インスタンス","EC2")
IdentityAccessManagementRole(role,"アクセス用IAMロール","IAM_Role")

cl --> s3 :①スクリプトアップロード
cl --> ssm :②RunCommand実行
ssm --> ec2 :③RunCommandの処理をEC2上で実行
ec2 --> s3 :④スクリプト取得&実行
role ..0 ec2 :S3&Systems Managerアクセス用ロールのアタッチ
@enduml
```

やっていることはEC2インスタンスからスクリプトを実行していることと変わりませんが、実行操作を`Systems Manager`が行うことで、`Systems Manager`に実行履歴や実行結果が残るため、 作業ログの取り忘れなどは防止できるかなと思います。

また、`Systems　Manager`を介することで、他のAWSサービスと連携させやすくなるので、例えば定期実行処理のエラー通知などもやりやすくなります。

## 構成の制約事項
`AWS Systems Manager`から、実行対象のEC2インスタンスが管理できていないと実行できないので、`AWS Systems Manager`サービスと連携できるようにする必要があります。

具体的には対象のEC2インスタンスから`AWS Systems Manager`に連携する際に、通常インターネットゲートウェイ、またはNATゲートウェイを経由した通信となるため、インターネットゲートウェイ、NATゲートウェイどちらにも繋がっていない場合、実行できません。

```plantuml
@startuml
!include <awslib/AWSCommon>
!include <awslib/ManagementGovernance/SystemsManager.puml>
!include <awslib/Compute/EC2.puml>
!include <awslib/AWSSimplified>

top to bottom direction
title Systems Managerへのアクセス

frame AWS{
  SystemsManager(ssm,"Systems Manager","SSM")
  frame VPC {
    EC2(ec2,"EC2","EC2")
    note right : インターネットゲートウェイ、\nまたはNATゲートウェイ経由でアクセス
  }
}

ec2 <--> ssm
@enduml
```

どうしてもどちらのゲートウェイも使用できないといった場合は、`AWS PrivateLink`を利用すればインターネットアクセス無しでアクセスすることが可能です。

- [ステップ 6: (オプション) AWS PrivateLink を使用して Session Manager の VPC エンドポイントを設定する](https://docs.aws.amazon.com/ja_jp/systems-manager/latest/userguide/session-manager-getting-started-privatelink.html)

# 事前作業
実際に`AWS Systems Manager`からスクリプトを実行するために必要となる事前作業は以下となります。

- 対象のEC2インスタンスに`SSM Agent`をインストールする。（必要であれば）
- S3バケットに実行するスクリプトを格納する。
- S3&Systems Managerアクセス用ロールを作成する。
- 対象のEC2インスタンスにS3&Systems Managerアクセス用ロールをアタッチする。

## SSM Agentインストール
対象のEC2インスタンスが古い場合や、オンプレミスのサーバに対してコマンド実行を行いたいといった場合には`SSM Agent`を別途インストールする必要があります。

- [Linux 用 EC2 インスタンスに手動で SSM Agent をインストールする](https://docs.aws.amazon.com/ja_jp/systems-manager/latest/userguide/sysman-manual-agent-install.html)

最近のマシンイメージ（`AMI`）を使用したEC2インスタンスであれば、デフォルトで導入済みとなるため、特に何もしなくても問題ありません。

- [SSM Agent がプリインストールされた Amazon Machine Images (AMIs)](https://docs.aws.amazon.com/ja_jp/systems-manager/latest/userguide/ami-preinstalled-agent.html)

インストールされているか不明な場合は以下のコマンドで確認できるので、入っていなければインストールしておきましょう。

```bash:SSM_Agentインストール確認
sudo systemctl status amazon-ssm-agent
```

## S3バケットへのスクリプト格納
`Systems Manager`の`RunCommand`機能で実行するスクリプトをS3バケットに格納しておきます。

今回は`test.sh`という以下のようなスクリプトを格納しました。

```bash:test.sh
#!/bin/bash
echo "Hello World"
uname -n
pwd
```

## S3&Systems Managerアクセス用ロールの作成
対象のEC2インスタンスには`Systems Manager`へのアクセスと、スクリプトが格納されているS3バケットへのアクセスを許可する必要があるので、以下のポリシーをアタッチしたIAMロールを作成しておく必要があります。

今回は以下のIAMポリシーをアタッチした「**RunComanndTestRole**」という名前のロールを作成するようにします。

|アタッチポリシー|説明|
|:--|:--|
|AmazonSSMManagedInstanceCore|Systems Managerとの通信用|
|AmazonS3ReadOnlyAccess|S3に格納されているスクリプトダウンロード用|

### IAMロールの作成
IAMダッシュボードから「**ロールを作成**」でロールを作成します。

今回はEC2インスタンスに適用するロールを作成するため、以下のようにして次へ進みます。

![Monosnap_20220827_132948.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569912/7acacf85-cb1d-a5fc-e963-7eae7c60def8.png)

検索ウィンドウ等で上で紹介した２つのポリシーを探しだし、チェックを行ったら次へ進みます。

![Monosnap_20220827_142117.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569912/661f0a17-e1fb-1919-ea13-3b3763ce5a4b.png)

適当なロール名（今回は**RunComanndTestRole**）を入力し、画面下部の「**ロールを作成**」からロールを作成します。

![Monosnap_20220827_142504.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569912/00e93959-09b9-5408-8c22-32c11fada88e.png)

## 対象のEC2インスタンスにS3&Systems Managerアクセス用ロールをアタッチ
EC2ダッシュボードから対象のEC2インスタンスに先程作成したIAMロールを対象のEC2インスタンスに適用します。

![Monosnap_20220827_143227.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569912/967863b1-6eb0-0698-e92c-d1b45a2b391b.png)

尚、EC2インスタンスの作成と同時にIAMロールをアタッチした場合はすぐに`Systems Manager`の画面に**マネージドノード**として登録されますが、あとからIAMロールをアタッチした場合はEC2インスタンス自体を再起動するか、サービスを再起動して`Systems Manager`への通信を行わせてあげる必要があるようなので、今回はEC2インスタンスにログインしてサービス再起動を行います。

```bash:SSM_Agentサービスの再起動
sudo systemctl restart amazon-ssm-agent
```

`Systems Manager`で管理されているかは、`Systems Manager`→**フリートマネージャー**から確認でき、対象のノードが存在して**実行中**、**オンライン**と表示されていれば、`Systems Manager`で管理されていることになります。

![Monosnap_20220827_145652.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569912/8fc83d35-15ad-62e2-0ba7-39a220dfb9b2.png)

# Amazon S3からのスクリプトの実行
いよいよ`Systems Manager`の`Run　Command`機能でコマンドを実行してみます。

`Systems Manager`→`Run Command`より「**Run command**」を選択。

![Monosnap_20220827_150147.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569912/185ae3dd-d24b-b28f-2f9f-35db1b885adf.png)

実行可能な**コマンドドキュメント**が色々と表示されますが、検索ボックス等を使って**AWS-RunRemoteScript**を見つけ出します。

![Monosnap_20220827_150323.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569912/96da8462-c150-4cec-3dcf-c626b6275586.png)

「**コマンドのパラメータ**」を以下のように設定。

「**Source Info**」の「**path**」と「**S3URL**」はダブルクォーテーションで閉じる必要があるので、忘れずにくくります。

![Monosnap_20220827_151715.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569912/d64e640a-bffd-ea67-bc6a-dfc6b584e36a.png)

「**ターゲット**」で、スクリプトを実行したいインスタンスを選択します。

インスタンスタグやリソースグループから対象を選択したり、複数選択したりすることもできるので、対象が多い場合は他のターゲット項目を活用してチェックします。

![Monosnap_20220827_151104.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569912/eb519edd-11da-684e-d6d5-3d30a4878112.png)

全て設定したら画面下部の実行ボタンで実行します。

尚、出力オプションに以下のようなコマンド出力結果をS3バケットへ書き込みする設定もありますが、有効にする場合は先程作成したIAMロールにS3への書き込み権限を付与させる必要があるので、S3にも残しておきたい場合はIAMロールを修正しておきます。

![Monosnap_20220827_151232.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569912/8ae8d313-384d-7499-e2be-021bd7b7fda1.png)

# 結果の確認
実行に成功すると、以下のようにコマンド実行結果が表示されます。

結果をコピーしたりダウンロードすることもできるので、取っておく必要がある場合は選択します。

![Monosnap_20220827_153615.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569912/46485f3f-6638-fddd-b8b2-fd9bb5c56dd7.png)

# Run Commandからのスクリプト実行について
前述している通り、S3に格納したスクリプトは対象のEC2インスタンスで実行することになります。

動作的には`Systems Manager`からEC2インスタンスにログインして、EC2インスタンス自身がS3バケットにあるスクリプトを所定のディレクトリにダウンロードして実行するといった流れになります。

実行時に使用したスクリプトは上記`pwd`の結果からも分かる通り、「`/var/lib/amazon/ssm/[インスタンスID]/document/orchestration/[UUID]/download`」配下に格納されており、特に削除するようなことは行わないため、EC2インスタンス上にそのまま残ります。

`Systems Manager`の`Run Command`機能で実行完了後もEC2インスタンス上に残り続けることをリスクと考えるのであれば、意図的に削除するような処理を導入したほうが良いかと思います。

※上記については気になったので別途記事を書きたいと思います。

**2022年9月3日追記**
書きました！
- [SSM Agentの実行スクリプトを削除する方法](https://qiita.com/sakai00kou/items/f6e04760783b805d55d5)

# おわりに
EC2インスタンスに対してコマンド実行するような方法は沢山ありますが、IAMロールを追加するくらいで手軽に実行できるため、簡単な運用見直しを提案したりする場合に使えるかもと思いました。