起動中のECSタスクでECS Execを簡単に利用するための自動化スクリプト

# はじめに
Amazon ECS (Elastic Container Service) でタスク内のコマンドを実行する際、ECS Exec は非常に便利です。しかし、コマンドの実行やロールの権限変更といった作業を都度行うのは手間がかかります。
この問題を解消するために、自動化スクリプトを作成しました。本記事では、ECS Exec を利用するための基本手順と、その手順を自動化するスクリプトについてご紹介します。


# 前提
この記事では以下の内容については説明していませんので、事前に設定されていることを前提としています。これらの設定がまだの場合は、AWSの公式ドキュメントや別の記事を参考にしてください。

### スクリプトを実行するためのIAMロールやユーザーの作成

スクリプトを実行するには、AWS CLIを使用できる権限が必要です。
権限を持つロールまたはユーザーを事前に作成してください。

### AWS CLIの--profileオプションの設定
スクリプトでは--profileオプションを使用しています。
プロファイルは、AWS CLIのaws configureコマンドで事前に設定してください。

# ECS Exec を利用する手順
ECS Execを利用するには、以下の手順を踏む必要があります。
今回は以下の手順をスクリプト化しました。

### ECS Execを有効化
ECSサービスでexecコマンドを使うには、`aws ecs update-service`を使って`--enable-execute-command`を有効にする必要があります。
また、有効にした後はタスクを立ち上げ直す必要があります。

### 適切なIAMポリシーを付与
ECS Execを使うには、ECSタスクがSSM経由で接続されるため以下の権限が必要です。
本記事ではサンプルとして広めの権限を付与しておりますが、適宜変更してください。

```
ecs:ExecuteCommand
ssmmessages:CreateDataChannel
ssmmessages:OpenDataChannel
ssmmessages:CreateControlChannel
ssmmessages:OpenControlChannel
```
## IaC の活用について
本記事では、スクリプトを利用してECS Execの有効化を手動で設定する方法を解説していますが、より効率的な運用を目指す場合、IaC (Infrastructure as Code) を用いて ECS Exec を有効化した状態でリソースを構築することも可能です。Terraform や AWS CDK を使用して、ECS サービスに必要な設定をあらかじめ含めておくと、作業の簡略化が可能です。


# 手順をスクリプトで自動化する

この記事で紹介するスクリプトは、ECS クラスターおよびサービスが既に存在していることを前提としています。また、既に稼働中のタスクがある場合は、スクリプト実行後にタスクを再起動する必要があります。なお、タスクの再起動については、誤って本番環境でスクリプトを実行した際のリスクを考慮し、スクリプトに含めていません。

:::note warn
policyの権限は要件に合わせて適宜変更してください
:::

```shell
#!/bin/bash

# Variables
PROFILE="" # 使用するAWS CLIプロファイル名
POLICY_NAME="ECSExecuteCommandPolicy"
ROLE_NAME="" # ECSロール名(タスク実行ロールではない)
CLUSTER_NAME="" # クラスターの名前
SERVICE_NAME="" # サービスの名前

# Replace placeholders
ACCOUNT_ID=$(aws sts get-caller-identity --query 'Account' --output text --profile $PROFILE)

# JSON Policy Content
POLICY_DOCUMENT=$(cat <<EOT
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor0",
            "Effect": "Allow",
            "Action": "ecs:ExecuteCommand",
            "Resource": [
                "arn:aws:ecs:*:${ACCOUNT_ID}:task/*/*",
                "arn:aws:ecs:ap-northeast-1:${ACCOUNT_ID}:cluster/${CLUSTER_NAME}"
            ]
        },
        {
            "Sid": "VisualEditor1",
            "Effect": "Allow",
            "Action": [
                "ssmmessages:CreateDataChannel",
                "ssmmessages:OpenDataChannel",
                "ssmmessages:OpenControlChannel",
                "ssmmessages:CreateControlChannel"
            ],
            "Resource": "*"
        }
    ]
}
EOT
)

# Create Policy
echo "Creating policy $POLICY_NAME..."
POLICY_ARN=$(aws iam create-policy \
    --profile $PROFILE \
    --policy-name $POLICY_NAME \
    --policy-document "$POLICY_DOCUMENT" \
    --query 'Policy.Arn' \
    --output text)

if [ $? -eq 0 ]; then
    echo "Policy created: $POLICY_ARN"
else
    echo "Failed to create policy."
    exit 1
fi

# Attach Policy to Role
echo "Attaching policy to role $ROLE_NAME..."
aws iam attach-role-policy \
    --profile $PROFILE \
    --role-name $ROLE_NAME \
    --policy-arn $POLICY_ARN

if [ $? -eq 0 ]; then
    echo "Policy attached successfully to $ROLE_NAME."
else
    echo "Failed to attach policy to $ROLE_NAME."
    exit 1
fi

# Enable ECS Execute Command
echo "Enabling execute command for service $SERVICE_NAME in cluster $CLUSTER_NAME..."
aws ecs update-service \
    --profile $PROFILE \
    --cluster $CLUSTER_NAME \
    --service $SERVICE_NAME \
    --enable-execute-command

if [ $? -eq 0 ]; then
    echo "Execute command enabled successfully for service $SERVICE_NAME in cluster $CLUSTER_NAME."
else
    echo "Failed to enable execute command for service $SERVICE_NAME in cluster $CLUSTER_NAME."
    exit 1
fi

echo "All tasks completed successfully."

```

# ECS Execコマンドの実行

タスクARNを動的に取得してECS Execを実行します。


```sh
#!/bin/bash

CLUSTER_NAME=""
SERVICE_NAME=""
CONTAINER_NAME=""
PROFILE_NAME=

TASK_ARN=$(aws ecs list-tasks \
    --cluster "$CLUSTER_NAME" \
    --service-name "$SERVICE_NAME" \
    --desired-status RUNNING \
    --query "taskArns[0]" \
    --output text \
    --profile "$PROFILE_NAME")

aws ecs execute-command \
    --cluster "$CLUSTER_NAME" \
    --task "$TASK_ARN" \
    --container "$CONTAINER_NAME" \
    --interactive \
    --command "/bin/sh" \
    --profile "$PROFILE_NAME"

```

# 最後に
ECS Exec を利用する際、コマンド実行までの手間を軽減するため、スクリプトを作成しました。手動で作業する場合に比べ、時間を短縮できるかと思います。本スクリプトを活用して、より効率的な運用を目指してください。