【AWS】SAMでStep Functionsを利用してCloudWatch Logsにログ出力する際、sam deploy時にエラーが発生するケースがある【SAM】

# はじめに
AWS SAMがStep Functionsをサポートするようになりましたが、CloudWatch Logsへのログ出力設定に不具合があり、ハマりましたので、その内容と対処方法をのこしておきます。

# 事象内容
SAMでStep Functionsを含むアプリケーションを作成し、 「$ sam deploy」 によるCloudFormation Stackの更新の際に以下のエラーが表示されることがあります。

> The state machine IAM Role is not authorized to access the Log Destination (Service: AWSStepFunctions; Status Code: 400; Error Code: AccessDeniedException; Request ID: 4598526e-a9b6-4745-880d-1dd24f47f500; Proxy: null)

![2021-01-01_14h41_24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/137777/f181c858-8bfb-8794-3407-0165946ff4fd.png)

具体的には、CloudWatch Logsへのログ出力を有効にしたState Machineを作成する、ログ出力を行っていないState Machineをログ出力するよう変更する、といった更新の際に発生する可能性があります。

これはIAM Role の権限不足のケースだけではなく、CloudWatchLogs自体のResource based policyの権限不足によるケースもあります。
この場合、当該のStep Functions State Machineに付与するIAM Roleの定義をいくら見直しても解決できません。

下記のフォーラムの内容と同等のものとなります。

https://forums.aws.amazon.com/thread.jspa?threadID=321488


# 原因／調査方法
Step FunctionsのState MachineからCloudWatch Logsへログを出力する場合、大きく、State MachineのIAM Roleと、CloudWatch LogsのResource based policyの2つのPermissionsが必要となります。

どちらか一方のPermissionsが不足していた場合、State Machine作成／更新時に前述のエラーが発生するようです。どちらのケースであっても、発生するエラーメッセージは変わらないようです。

単純いState MachineそのものにCloudWatch Logsのログ出力用のIAM Permissionsを付与すればよいだけではないようで、中で少し複雑なことを行っているようですが、詳細は未確認です。

## State Machineに付与するIAM Role
まず、State Machineに付与するIAM Roleに、CloudWatch Logsに関する権限（具体的には以下のIAM Permissions）が必要なようです。
このIAM Permissionsは、AWS Management ConsoleのStep Functions Consoleにて、IAM Roleを作成し、且つログを有効にしてState Machineを作成する際に付与されるものと同等です。下記公式ドキュメントにも記載があるものです。

Logging using CloudWatch Logs - AWS Step Functions
https://docs.aws.amazon.com/step-functions/latest/dg/cw-logs.html#cloudwatch-iam-policy

```
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Action": [
                "logs:CreateLogDelivery",
                "logs:GetLogDelivery",
                "logs:UpdateLogDelivery",
                "logs:DeleteLogDelivery",
                "logs:ListLogDeliveries",
                "logs:PutResourcePolicy",
                "logs:DescribeResourcePolicies",
                "logs:DescribeLogGroups"
            ],
            "Resource": "*",
            "Effect": "Allow"
        }
    ]
}
```


## CloudWatch LogsのResource based policy
CloudWatch Logsには、それ自体で権限制御するためのResource based policyが備わっています。（このResource based policyは、Log GroupやLog Streamごとではなく、リージョンごとに1つずつという単位で存在している模様）

このCloudWatch LogsのResource based policyにはサイズ上限があり、SAMによりこの上限を超える変更をしようとする場合に発生します。

通常、AWSサービスからCloudWatch Logsにログ出力する際には、出力する側のIAM Entity（≒主体）にIAM Permissionsを付与して権限制御します。
このCloudWatch Logs側のResource based policyの目的は大きく以下の2つあるようですが、Step Functionsは前者に該当するため、これが用いられているものと推測できます。

- 一部のサービスで出力する側のIAM EntityにIAM Roleを付与できない場合
- AWSアカウントをまたいでCloudWatch Logsにログを出力する場合

## Step Functionsの挙動
SAMでStep Functions State Machineを作る場合、SAMとしてのCloudFormation template内の定義を元に、自動でCloudWatchLogsのResource based policyを生成し、付与しようとします。
しかしこの際、CloudWatchLogsのResource based policyサイズは5120バイトが上限値であるため、きめ細かな制御を行おうとするとサイズが足りず、必要な権限を付与できないようです。

またStep Functions側の挙動として、CloudWatchLogsへの出力を有効にする設定でState Machine定義を作成・更新する際、CloudWatch LogsのResource based policyに必要な権限があるかチェックしているようです。
Step Functionsとして必要な権限が不足していると判断すると、前述のエラーを発生させ、State Machine定義の作成・更新は失敗します。

以下のコマンドで、設定済みのCloudWatch LogsのResource based policyを参照可能です。


```json
$ aws logs describe-resource-policies
{
    "resourcePolicies": [
        {
            "policyName": "AWSLogDeliveryWrite20150319",
            "policyDocument": "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Sid\":\"AWSLogDeliveryWrite\",\"Effect\":\"Allow\",\"Principal\":{\"Service\":\"delivery.logs.amazonaws.com\"},\"Action\":[\"logs:CreateLogStream\",\"logs:PutLogEvents\"],\"Resource\":[\"arn:aws:logs:us-west-2:123456789012:log-group:/aws/vendedlogs/states/HelloWorld-Logs:log-stream:*\",\"arn:aws:logs:us-west-2:123456789012:log-group:/aws/vendedlogs/states/InputOutputStateMachine-GrBZXWGHTyuW-Logs:log-stream:*\",\"arn:aws:logs:us-west-2:123456789012:log-group:/aws/vendedlogs/states/InputOutput-Logs:log-stream:*\",\"arn:aws:logs:us-west-2:123456789012:log-group:/aws/vendedlogs/states/InputOutputStateMachine-Logs:log-stream:*\"]}]}",
            "lastUpdatedTime": 1609057420992
        }
    ]
}
```

SAMのCFn template側でログ出力設定をいろいろ変えながら試していると、下記のように「Resource」がどんどん追加されていく状況になります。

```json
$ aws logs describe-resource-policies | jq ".resourcePolicies[0].policyDocument | fromjson"
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "AWSLogDeliveryWrite",
      "Effect": "Allow",
      "Principal": {
        "Service": "delivery.logs.amazonaws.com"
      },
      "Action": [
        "logs:CreateLogStream",
        "logs:PutLogEvents"
      ],
      "Resource": [
        "arn:aws:logs:us-west-2:123456789012:log-group:/aws/vendedlogs/states/HelloWorld-Logs:log-stream:*",
        "arn:aws:logs:us-west-2:123456789012:log-group:/aws/vendedlogs/states/InputOutputStateMachine-GrBZXWGHTyuW-Logs:log-stream:*",
        "arn:aws:logs:us-west-2:123456789012:log-group:/aws/vendedlogs/states/InputOutput-Logs:log-stream:*",
        "arn:aws:logs:us-west-2:123456789012:log-group:/aws/vendedlogs/states/InputOutputStateMachine-Logs:log-stream:*"
      ]
    }
  ]
}
```

# 対応方法
AWSフォーラムの回答に記載の通り、CloudWatchLogsのResource based policyを手動で更新し、「Resource」の範囲を緩くすることで対処可能です。

手順は以下のとおりです。

```json:cloudwatchlogs-resourcebased-policy.json
### 更新用のPolicy documentを作る
$ vi cloudwatchlogs-resourcebased-policy.json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "AWSLogDeliveryWrite",
      "Effect": "Allow",
      "Principal": {
        "Service": "delivery.logs.amazonaws.com"
      },
      "Action": [
        "logs:CreateLogStream",
        "logs:PutLogEvents"
      ],
      "Resource": [
        "arn:aws:logs:us-west-2:123456789012:log-group:/aws/vendedlogs/states/*:*"
      ]
    }
  ]
}
```

```json:aws&nbsp;logs&nbsp;put-resource-policy
### CloudWatchLogsのResource based policyを、上述のPolicy documentで上書きする
$ aws logs put-resource-policy --policy-name AWSLogDeliveryWrite20150319 --policy-document file://cloudwatchlogs-resourcebased-policy.json
{
    "resourcePolicy": {
        "policyName": "AWSLogDeliveryWrite20150319",
        "policyDocument": "{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Sid\": \"AWSLogDeliveryWrite\",\n      \"Effect\": \"Allow\",\n      \"Principal\": {\n        \"Service\": \"delivery.logs.amazonaws.com\"\n      },\n      \"Action\": [\n        \"logs:CreateLogStream\",\n        \"logs:PutLogEvents\"\n      ],\n      \"Resource\": [\n        \"arn:aws:logs:us-west-2:123456789012:log-group:/aws/vendedlogs/states/*:*\"\n      ]\n    }\n  ]\n}\n",
        "lastUpdatedTime": 1609077195669
    }
}
```

```json:aws&nbsp;logs&nbsp;describe-resource-policies
### 更新後のCloudWatchLogs Resource based policyを確認する
$ aws logs describe-resource-policies | jq ".resourcePolicies[0].policyDocument | fromjson"
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "AWSLogDeliveryWrite",
      "Effect": "Allow",
      "Principal": {
        "Service": "delivery.logs.amazonaws.com"
      },
      "Action": [
        "logs:CreateLogStream",
        "logs:PutLogEvents"
      ],
      "Resource": "arn:aws:logs:us-west-2:123456789012:log-group:/aws/vendedlogs/states/*:*"
    }
  ]
}
```

# Furthermore
Step Functions State Machineからのログ出力が誰の権限によって行われるか、当該State Machineに付与したIAM Roleの権限で行われるのか、Step Functionsサービス自体の権限で行われるのか、不明確であるため引き続き確認したいと思います。

→検証しました。CloudWatch Logsへのログ出力操作は、当該State Machineに付与したIAM Roleの権限とは無関係でした。
[【AWS】Step FunctionsからCloudWatch Logsへのログ出力時の権限を確認する【Step Functions】](https://qiita.com/tmiki/items/519d517fe6e9edc5353b)


# 参照した情報／関連情報
AWS Developer Forums: Bug with LoggingConfiguration ...
https://forums.aws.amazon.com/thread.jspa?threadID=321488

Amazon CloudWatch Logs resource policy size restrictions - AWS Step Functions
https://docs.aws.amazon.com/step-functions/latest/dg/bp-cwl.html

Enabling Logging from Certain AWS Services - Amazon CloudWatch Logs
https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/AWS-logs-and-resource-policy.html