AWS CLI を使って AWS Systems Manager Run Command を実行する

# はじめに
AWS Systems Manager の Run Command についてまとめてみました。
操作は全て aws-cli で行い、内容もコマンドベースの内容になります。

# Run Command について
SSM エージェントをインストールしたインスタンスに対し、ドキュメントとしてあらかじめ用意したタスクをコマンドとして実行することができます。
簡単にいうと、AWS Systems Manager からリモートでコマンド実行などの操作ができるということです。

# 前提条件
https://docs.aws.amazon.com/ja_jp/systems-manager/latest/userguide/systems-manager-prereqs.html

# aws-cli コマンド
「list-documents」「describe-instance-information」「describe-document」「send-command」「list-commands」「list-command-invocations」の6コマンドについてまとめました。

## ■ list-documents - 利用可能なドキュメントの表示
利用可能なすべてのドキュメントを表示します。

```bash:list-documents
$ aws ssm list-documents
```
特定のドキュメントのみに絞りたい場合は `--document-filter-list`オプション を利用します。

```bash:list-documents
$ aws ssm list-documents --document-filter-list key=Name,value=AWS-RunShellScript
{
    "DocumentIdentifiers": [
        {
            "Name": "AWS-RunShellScript",
            "Owner": "Amazon",
            "PlatformTypes": [
                "Linux"
            ],
            "DocumentVersion": "1",
            "DocumentType": "Command",
            "SchemaVersion": "1.2",
            "DocumentFormat": "JSON",
            "Tags": []
        }
    ]
}
```

## ■ describe-instance-information - 実行可能インスタンスの表示
コマンドを受信する準備ができているインスタンスを確認します。

```bash:describe-instance-information
$ aws ssm describe-instance-information --query "InstanceInformationList[*]"
[
    {
        "InstanceId": "i-0c8a643ed4eba0d50",
        "PingStatus": "Online",
        "LastPingDateTime": "2021-04-02T09:45:35.443000+09:00",
        "AgentVersion": "3.0.161.0",
        "IsLatestVersion": false,
        "PlatformType": "Linux",
        "PlatformName": "Amazon Linux",
        "PlatformVersion": "2",
        "ResourceType": "EC2Instance",
        "IPAddress": "172.31.33.239",
        "ComputerName": "ip-172-31-33-239.ap-northeast-1.compute.internal"
    }
]
```

特定のインスタンスについての詳細を表示するには、次のように `--instance-information-filter-list` オプションで **インスタンスID** を指定することで行えます。
ここでは先ほど `describe-instance-information` で取得したインスタンスIDを利用しています。

```bash:describe-instance-information
$ aws ssm describe-instance-information --instance-information-filter-list key=InstanceIds,valueSet=i-0c8a643ed4eba0d50
```


## ■ describe-document - 説明と使用可能なパラメータを表示する
Systems Manager JSON ドキュメントの説明を表示するには、次のコマンドを使用します。

```bash:describe-document
$ aws ssm describe-document
```
このままだと全てのドキュメントが表示されるため、特定のドキュメント(ここでは*AWS-RunShellScript*)の表示のみ行いたい場合は `--name`オプション を使って指定することができます。

```bash:describe-document
$ aws ssm describe-document --name "AWS-RunShellScript"                                               
{
    "Document": {
        "Hash": "99749de5e62f71e5ebe9a55c2321e2c394796afe7208cff048696541e6f6771e",
        "HashType": "Sha256",
        "Name": "AWS-RunShellScript",
        "Owner": "Amazon",
        "CreatedDate": "2017-08-31T06:43:10.613000+09:00",
        "Status": "Active",
        "DocumentVersion": "1",
        "Description": "Run a shell script or specify the commands to run.",
        "Parameters": [
            {
                "Name": "commands",
                "Type": "StringList",
                "Description": "(Required) Specify a shell script or a command to run."
            },
            {
                "Name": "workingDirectory",
                "Type": "String",
                "Description": "(Optional) The path to the working directory on your instance.",
                "DefaultValue": ""
            },
            {
                "Name": "executionTimeout",
                "Type": "String",
                "Description": "(Optional) The time in seconds for a command to complete before it is considered to have failed. Default is 3600 (1 hour). Maximum is 172800 (48 hours).",
                "DefaultValue": "3600"
            }
        ],
        "PlatformTypes": [
            "Linux"
        ],
        "DocumentType": "Command",
        "SchemaVersion": "1.2",
        "LatestVersion": "1",
        "DefaultVersion": "1",
        "DocumentFormat": "JSON",
        "Tags": []
    }
}
```
さらに、`--query`オプション を利用すると、出力内容を整形することもできます。
次のコマンドはドキュメント名とその説明を抽出した例です。

```bash:describe-document
$ aws ssm describe-document --name "AWS-RunShellScript" --query "[Document.Name,Document.Description]"
[
    "AWS-RunShellScript",
    "Run a shell script or specify the commands to run."
]
```
もう一つ例を挙げておきます。
こちらはドキュメントを実行する際に利用するパラメータ関連の情報を出力しています。

```bash:describe-document
aws ssm describe-document --name "AWS-RunShellScript" --query "Document.Parameters[*]"              
[
    {
        "Name": "commands",
        "Type": "StringList",
        "Description": "(Required) Specify a shell script or a command to run."
    },
    {
        "Name": "workingDirectory",
        "Type": "String",
        "Description": "(Optional) The path to the working directory on your instance.",
        "DefaultValue": ""
    },
    {
        "Name": "executionTimeout",
        "Type": "String",
        "Description": "(Optional) The time in seconds for a command to complete before it is considered to have failed. Default is 3600 (1 hour). Maximum is 172800 (48 hours).",
        "DefaultValue": "3600"
    }
]
```
`アスタリスク(*)` を用いることで、ネストされている部分をまとめて抽出しています。


## ■ send-command - ドキュメントを使用してコマンドを送信
インスタンスへコマンドを送信するにはドキュメントを指定して行います。
今回は `AWS-RunShellScript`ドキュメント を使って **ifconfig** コマンドを送信します。

```bash:send-command
$ aws ssm send-command --instance-ids i-0c8a643ed4eba0d50 --document-name "AWS-RunShellScript" --comment "IP config" --parameters commands=ifconfig
{
    "Command": {
        "CommandId": "3230ba49-d49c-4c25-b7f0-e753c5713738",
        "DocumentName": "AWS-RunShellScript",
        "DocumentVersion": "",
        "Comment": "IP config",
        "ExpiresAfter": "2021-04-02T12:17:39.453000+09:00",
        "Parameters": {
            "commands": [
                "ifconfig"
            ]
        },
        "InstanceIds": [
            "i-0c8a643ed4eba0d50"
        ],
        "Targets": [],
        "RequestedDateTime": "2021-04-02T10:17:39.453000+09:00",
        "Status": "Pending",
        "StatusDetails": "Pending",
        "OutputS3BucketName": "",
        "OutputS3KeyPrefix": "",
        "MaxConcurrency": "50",
        "MaxErrors": "0",
        "TargetCount": 1,
        "CompletedCount": 0,
        "ErrorCount": 0,
        "DeliveryTimedOutCount": 0,
        "ServiceRole": "",
        "NotificationConfig": {
            "NotificationArn": "",
            "NotificationEvents": [],
            "NotificationType": ""
        },
        "CloudWatchOutputConfig": {
            "CloudWatchLogGroupName": "",
            "CloudWatchOutputEnabled": false
        },
        "TimeoutSeconds": 3600
    }
}
```


## ■ list-commands - コマンドステータスの取得
コマンドステータスの取得を行います。
取得には `send-command` で実行した際に発行される **CommandId** を指定します。
ここでは先ほど実行して発行された **CommandId** を利用します。

```bash:list-commands
$ aws ssm list-commands --command-id 3230ba49-d49c-4c25-b7f0-e753c5713738
{
    "Commands": [
        {
            "CommandId": "3230ba49-d49c-4c25-b7f0-e753c5713738",
            "DocumentName": "AWS-RunShellScript",
            "DocumentVersion": "",
            "Comment": "IP config",
            "ExpiresAfter": "2021-04-02T12:17:39.453000+09:00",
            "Parameters": {
                "commands": [
                    "ifconfig"
                ]
            },
            "InstanceIds": [
                "i-0c8a643ed4eba0d50"
            ],
            "Targets": [],
            "RequestedDateTime": "2021-04-02T10:17:39.453000+09:00",
            "Status": "Success",
            "StatusDetails": "Success",
            "OutputS3BucketName": "",
            "OutputS3KeyPrefix": "",
            "MaxConcurrency": "50",
            "MaxErrors": "0",
            "TargetCount": 1,
            "CompletedCount": 1,
            "ErrorCount": 0,
            "DeliveryTimedOutCount": 0,
            "ServiceRole": "",
            "NotificationConfig": {
                "NotificationArn": "",
                "NotificationEvents": [],
                "NotificationType": ""
            },
            "CloudWatchOutputConfig": {
                "CloudWatchLogGroupName": "",
                "CloudWatchOutputEnabled": false
            },
            "TimeoutSeconds": 3600
        }
    ]
}
```


## ■ list-command-invocations - コマンド実行に関するステータス取得
コマンド実行に関するステータスを取得します。

```bash:list-command-invocations
$ aws ssm list-command-invocations --command-id 3230ba49-d49c-4c25-b7f0-e753c5713738          
{
    "CommandInvocations": [
        {
            "CommandId": "3230ba49-d49c-4c25-b7f0-e753c5713738",
            "InstanceId": "i-0c8a643ed4eba0d50",
            "InstanceName": "",
            "Comment": "IP config",
            "DocumentName": "AWS-RunShellScript",
            "DocumentVersion": "",
            "RequestedDateTime": "2021-04-02T10:17:39.597000+09:00",
            "Status": "Success",
            "StatusDetails": "Success",
            "StandardOutputUrl": "",
            "StandardErrorUrl": "",
            "CommandPlugins": [],
            "ServiceRole": "",
            "NotificationConfig": {
                "NotificationArn": "",
                "NotificationEvents": [],
                "NotificationType": ""
            },
            "CloudWatchOutputConfig": {
                "CloudWatchLogGroupName": "",
                "CloudWatchOutputEnabled": false
            }
        }
    ]
}
```
`--details`オプション を追加すると **CommandPlugins** の部分も表示されます。

```text:list-command-invocations
            "CommandPlugins": [
                {
                    "Name": "aws:runShellScript",
                    "Status": "Success",
                    "StatusDetails": "Success",
                    "ResponseCode": 0,
                    "ResponseStartDateTime": "2021-04-02T10:17:40.129000+09:00",
                    "ResponseFinishDateTime": "2021-04-02T10:17:40.141000+09:00",
                    "Output": "eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 9001\n        inet 172.31.33.239  netmask 255.255.240.0  broadcast 172.31.47.255\n        inet6 fe80::439:f6ff:fe5e:ad7f  prefixlen 64  scopeid 0x20<link>\n        ether 06:39:f6:5e:ad:7f  txqueuelen 1000  (Ethernet)\n        RX packets 475097  bytes 213995563 (204.0 MiB)\n        RX errors 0  dropped 0  overruns 0  frame 0\n        TX packets 378752  bytes 44378719 (42.3 MiB)\n        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0\n\nlo: flags=73<UP,LOOPBACK,RUNNING>  mtu 65536\n        inet 127.0.0.1  netmask 255.0.0.0\n        inet6 ::1  prefixlen 128  scopeid 0x10<host>\n        loop  txqueuelen 1000  (Local Loopback)\n        RX packets 10  bytes 726 (726.0 B)\n        RX errors 0  dropped 0  overruns 0  frame 0\n        TX packets 10  bytes 726 (726.0 B)\n        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0\n\n",
                    "StandardOutputUrl": "",
                    "StandardErrorUrl": "",
                    "OutputS3Region": "ap-northeast-1",
                    "OutputS3BucketName": "",
                    "OutputS3KeyPrefix": ""
                }
            ],
```

初めは、`list-commands`コマンド と `list-command-invocations`コマンド の違いわかりませんでした。
ただ、実行結果を良く見ると、`list-commands` では **Parameters** や **MaxConcurrency** などの値が出力され、`list-command-invocations` では **CommandPlugins** の部分で `send-command` で実行しされた結果(ここではifconfigの戻り値)が確認できます。
ぱっと見同じような値が出力されているのでどっちを使っても良いのかなと思いますが、取得したい値によっては使い分けが必要がありそうです。


# おわりに
Run Command は管理コンソールから操作するとステップが多くて時間がかかります。
aws-cli だとコマンド一つで実行できるので便利です。