CloudWatch LogsからS3バケットにエクスポートするシェルスクリプト

## はじめに
先日先人が作り上げた、偉大なログエクスポートを実行するStepFunctionsがコケていました。
先人ほどのスキルもないので、CloudShellを利用してCloudWatchLogsからS3へエクスポートするシェルスクリプトを作成したので記事として残します。
StepFunctionsの根本対応が必要ですが、まずは一時的な対応としてシェルスクリプトを作成。
マネジメントコンソールからGUI操作も可能ですが、対象となるCloudWatchLogsの量が多いことや、アクセスキーが払い出されていないことから、本番環境のCloudShellで作業実施を決めました。

参考：[
AWS ドキュメント「AWS CLI を使用してログデータを Amazon S3 にエクスポートする」
](https://docs.aws.amazon.com/ja_jp/AmazonCloudWatch/latest/logs/S3ExportTasks.html)
<br>


## 構成図
![](https://cloud5.jp/wp-content/uploads/2023/05/スクリーンショット-2023-05-28-8.37.43-640x242.png)
<br>


## 構築
### 1.S3バケットの準備
実機ではStepFunctionsから毎晩ログが対象のS3バケットにエクスポートされているため設定は不要。
ただし検証するために新規作成しているため、新たに作成したS3バケットに、以下`バケットポリシー`を追記する。
※東京リージョンを想定
※`arn:aws:s3:::[YOUR-BUCKET-NAME]`部分は、ログが転送されるS3バケット名を入力する

```json
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "Service": "logs.ap-northeast-1.amazonaws.com"
            },
            "Action": "s3:GetBucketAcl",
            "Resource": "arn:aws:s3:::[YOUR-BUCKET-NAME]"
        },
        {
            "Effect": "Allow",
            "Principal": {
                "Service": "logs.ap-northeast-1.amazonaws.com"
            },
            "Action": "s3:PutObject",
            "Resource": "arn:aws:s3:::[YOUR-BUCKET-NAME]/*",
            "Condition": {
                "StringEquals": {
                    "s3:x-amz-acl": "bucket-owner-full-control"
                }
            }
        }
    ]
}

```

<br>

### 2.CloudWatch LogsからS3バケットにエクスポートするシェルスクリプト
#### 2.1.転送するCloudWatchLogsのリストファイルを作成
S3へ転送したい　CloudWatchLogsのロググループをテキストファイル形式で作成して保存
※下記`log.txt`の内容にS3に転送したいロググループ名を記載する
<br>
【例】`log.txt`
```txt
/aws/lambda/cfn-sf-inamura-LambdaHumanApprovalSendEmailFunctio-wPwtEbDmKEg3
/aws/lambda/cfn-stepfunctions-inamura-LambdaHumanApprovalSendE-3j0qFdWkOpat
/aws/lambda/cfn-stepfunctions-inamura-LambdaHumanApprovalSendE-D92dYTTBLAmy
/aws/lambda/cfn-stepfunctions-inamura-LambdaHumanApprovalSendE-IbgVOxScNeUI
/aws/lambda/cfn-stepfunctions-inamura-LambdaHumanApprovalSendE-UTH7yskJ5b79
```

<br>

#### 2.2.CloudWatch LogsからS3バケットにエクスポートするシェルスクリプトを作成

<br>

【シェルスクリプト】`bash export_log_groups_to_s3.sh`

```shell
#!/bin/bash
#エラーが発生した場合停止する
set -e

#引数$1:ログリストパス
#引数$2:保存先S3バケット名の記載がない場合はエラー表示
if [ -z "$1" ] || [ -z "$2" ]; then
  echo "Please provide both a log group name file and a bucket name as arguments."
  exit 1
fi

log_group_name_file=$1
s3_bucket_name=$2

#検索開始・終了時間
start_time=$(($(date -d "2020/01/01" +%s)*1000)) 
end_time=$(($(date -d "2023/05/01" +%s)*1000))

#ログリストを一行ずつ実行
while IFS= read -r log_group_name
do
  #空行になるまで繰り返す
  if [ -z "$log_group_name" ]; then
    continue
  fi

  #タスクネーム作成
  task_name="ExportTask-$(date +%Y%m%d%H%M%S)"

  #ロググループ名の一文字めの"/"を削除するr
  prefix=${log_group_name#/}
  
  #エクスポートタスクを作成しタスクIDを取得
  task_id=$(aws logs create-export-task --task-name "${task_name}" --log-group-name "${log_group_name}" --from ${start_time} --to ${end_time} --destination "${s3_bucket_name}" --destination-prefix "${prefix}" --query 'taskId' --output text)
  
  #エクスポートタスク作成が失敗した場合、どのロググループで失敗したか表示
  if [ $? -ne 0 ]; then
    echo "Failed to create export task for log group ${log_group_name}"
    exit 1
  fi
  
  #エクスポートタスクのステータスを取得
  while true; do
    status=$(aws logs describe-export-tasks --task-id "${task_id}" --query 'exportTasks[0].status.code' --output text)

    #エクスポートステータスが”COMPLETED”なら、次のタスクを実行するためループ終了
    if [[ "${status}" == "COMPLETED" ]]; then
      echo "Export task for log group ${log_group_name} completed successfully."
      break
    fi

    #エクスポートタスクが”FAILED”なら、どのロググループで失敗したか表示して終了
    if [[ "${status}" == "FAILED" ]]; then
      echo "Export task for log group ${log_group_name} failed."
      exit 1
    fi

    #エクスポートタスクが"RUNNING","PENDING"なら30秒sleep後、再実行
    if [[ "${status}" == "RUNNING" ]] || [[ "${status}" == "PENDING" ]]; then
      echo "Export task for log group ${log_group_name} wait please."
      sleep 30
    fi
  done
done < "$log_group_name_file"

```
<br>


## 挙動の確認
### 1.CloudShellで実行
$1引数：S3へ出力したいログリストのテキストファイル
$2引数：出力先とするS3バケット
```shell
$ bash export_log_groups_to_s3.sh "log.txt" "20220214-inamura"
```
<br>

### 2.実行結果の確認
#### 2.1.CloudShell画面での実行結果確認
何度かS3バケットに送れないということで「wait please」が出ましたが、30秒待機して再トライして送信ができてることがターミナル画面からわかる

![](https://cloud5.jp/wp-content/uploads/2023/05/スクリーンショット-2023-05-28-10.20.50-640x210.png)
<br>

#### 2.2.実行後のS3バケット画面での確認
S3にも出力されていることを確認できる
![](https://cloud5.jp/wp-content/uploads/2023/05/スクリーンショット-2023-05-28-10.26.29.png)

<br>

#### 2.3.CloudWatchLogsの画面での確認
##### 2.3.1.「Amazon S3へのすべてのエクスポートを表示」画面へ遷移
CloudWatch > ロググループ > アクション > Amazon S3へのすべてのエクスポートを表示　を押下
![](https://cloud5.jp/wp-content/uploads/2023/05/スクリーンショット-2023-05-28-10.29.21-640x111.png)
<br>

##### 2.3.2.エクスポートタスクでの確認
エクスポートタスクが完了しており、ステータスが「Completed successfully」となっていることを確認
![](https://cloud5.jp/wp-content/uploads/2023/05/スクリーンショット-2023-05-28-10.32.40-640x196.png)
<br>

## さいごに
ログ転送中は同時に2つの処理を実行できないことは新たな学びでした。
さて一時対応が済んだので、StepFuncitonsの根本解決に着手していこうと思います。
※利用したS3バケットは検証終了したため削除されています。