CloudWatchのリソースポリシーのサイズ上限エラーにぶつかった話

## 事の発端
AWS内、StepFunctionsのステートマシンという機能を使って開発を進めていた時に事件は発生しました。
そこそこ規模が大きく、必要なステートマシンもなかなかに多いという状況でした。さらに1つのAWSアカウント内にdev1,dev2,test1...と様々な環境面を作成していたので、作成数が**必要なステートマシン数×必要な環境面数=∞**となっていました。
リソースポリシーのサイズ上限など特に気にしていなかった私は何も考えずステートマシンを新環境面に複製していたのですが、急にこのようなエラーにぶち当たりました...
~~~
InvalidLoggingConfiguration:Invalid Logging Configuration: The CloudWatch Logs Resource Policy size was exceeded.
We suggest prefixing your CloudWatch log group name with /aws/vendedlogs/states/.
~~~

そもそもStepFunctionsとかステートマシンとかなんぞや？という方は以下の公式サイトを確認してみてください！
>参考
>* [AWS Step Functions（分散アプリケーションの調整）](https://aws.amazon.com/jp/step-functions/)
>* [Step Functionsとは](https://docs.aws.amazon.com/ja_jp/step-functions/latest/dg/welcome.html)

## エラーを解読
どうやらCloudWatchのリソースポリシーのサイズを超えているらしい???
加えて、"/aws/vendedlogs/states/"をロググループ名の前につけることを提案されてます。
なんだそれと思い、検索をかけたところちゃんと公式ドキュメントに載っていました。
>参考
>* [Step Functions のベストプラクティス](https://docs.aws.amazon.com/ja_jp/step-functions/latest/dg/sfn-best-practices.html#bp-cwl)(CloudWatch ログリソースポリシーのサイズ制限セクション)
>* [CloudWatch ログを使用した Step Functions の実行履歴のログ記録](https://docs.aws.amazon.com/ja_jp/step-functions/latest/dg/cw-logs.html)

なんと、リソースポリシー全体で**5120文字**を超えてはいけないみたいですね…

・リソースポリシー確認コマンド
~~~
aws logs describe-resource-policies 
~~~
・確認コマンドオプション(今回はリージョンとpolicyNameを指定してjson形式で出力)
~~~
--region ap-northeast-1 | jq '.resourcePolicies[] | select(.policyName=="AWSLogDeliveryWrite20150319")'
~~~

>参考
>* [AWS CLI Command Reference/describe-resource-policies](https://docs.aws.amazon.com/cli/latest/reference/logs/describe-resource-policies.html)
>* [./jq](https://jqlang.org/)

ステートマシン１つに対して１つのロググループを作り続けていたため、リソースポリシーの文量がどんどん増えてしまい、
~~~json
{
	"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:ap-northeast-1:<AWSアカウントID>:log-group:/aws/stateMachine/001:log-stream:*",
					"arn:aws:logs:ap-northeast-1:<AWSアカウントID>:log-group:/aws/stateMachine/002:log-stream:*",
					"arn:aws:logs:ap-northeast-1:<AWSアカウントID>:log-group:/aws/stateMachine/003:log-stream:*",
					"arn:aws:logs:ap-northeast-1:<AWSアカウントID>:log-group:/aws/stateMachine/004:log-stream:*",
					"arn:aws:logs:ap-northeast-1:<AWSアカウントID>:log-group:/aws/stateMachine/005:log-stream:*",
                    ~~~省略~~~
                    "arn:aws:logs:ap-northeast-1:<AWSアカウントID>:log-group:/aws/stateMachine/098:log-stream:*",
					"arn:aws:logs:ap-northeast-1:<AWSアカウントID>:log-group:/aws/stateMachine/099:log-stream:*",
					"arn:aws:logs:ap-northeast-1:<AWSアカウントID>:log-group:/aws/stateMachine/100:log-stream:*"
				],
				"Condition": {
					"StringEquals": {
						"aws:SourceAccount": "<AWSアカウントID>"
					},
					"ArnLike": {
						"aws:SourceArn": "arn:aws:logs:ap-northeast-1:<AWSアカウントID>:*"
					}
				}
			}
		]
	},
	"lastUpdatedTime": 999999999999
}
~~~
上記のようにサイズが膨れ上がっていたようです。
主にResource部分がステートマシンの数だけ膨れ上がってしまっていることが確認できます。
※lastUpdatedTimeはソースポリシーが最後に更新された時間を「1970-01-01 00:00:00 UTC」からのミリ秒数表しているみたいです

## /aws/vendedlogs/states/
まずはエラー文からもおすすめされていた **/aws/vendedlogs/states/** を素直に使用してサイズ上限を回避してみます。
公式ドキュメントでもおすすめされているくらいなので、ステートマシンを作成する際、「/aws/vendedlogs/states/<ステートマシン名>」が新しいロググループ名としてマネコン上にあらかじめ書いてありました。
さすがAWSと思いながらこのままステートマシンを作成したところ、サイズ上限エラーは発生せず難なく作成できました！
これを使えば例外的にサイズ上限回避できるんだなと思いながらリソースポリシーを見に行くと…
~~~json
{
	"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:ap-northeast-1:<AWSアカウントID>:log-group:/aws/stateMachine/001:log-stream:*",
					"arn:aws:logs:ap-northeast-1:<AWSアカウントID>:log-group:/aws/stateMachine/002:log-stream:*",
                    ~~~省略~~~
					"arn:aws:logs:ap-northeast-1:<AWSアカウントID>:log-group:/aws/stateMachine/099:log-stream:*",
					"arn:aws:logs:ap-northeast-1:<AWSアカウントID>:log-group:/aws/stateMachine/100:log-stream:*",
                    "arn:aws:logs:ap-northeast-1:<AWSアカウントID>:log-group:/aws/vendedlogs/*"　←変更箇所
				],
				"Condition": {
					"StringEquals": {
						"aws:SourceAccount": "<AWSアカウントID>"
					},
					"ArnLike": {
						"aws:SourceArn": "arn:aws:logs:ap-northeast-1:<AWSアカウントID>:*"
					}
				}
			}
		]
	},
	"lastUpdatedTime": 999999999999
}
~~~
Resourceの末尾(上記ソース内Resource5行目)にvendedlog関連の記載が追加されていました。
注目すべきは、ほかの各自で命名したロググループと違いワイルドカード(*)がついているという点です。
AWS側で/aws/vendedlogs/（states/）に対応したロググループをすべて自動的にリソースポリシー内に反映してくれたということになります。

今後ステートマシンを作成するときに合わせて作成するロググループは/aws/vendedlogs/*に準ずるものにすればいいので一応解決！ではあるのですが…
ロググループの命名規約をすでに定めてしまっているのでそういうわけにもいきませんでした。

命名規約を変更するとなるとこれまで作り続けてきた大量のステートマシンとロググループ<span style="text-transform: lowercase;">(ついでにCFnテンプレートも)</span>を改修する必要が出てきてしまうため、別の道を模索します。

## リソースポリシーを直接書き換える
先の対応でリソースポリシーにワイルドカード(*)が使えることが分かったので、それを活かしてリソースポリシーを直接書き換えていけば自由に権限周りを設定できそうな予感がします。

すでにロググループ名は「/aws/stateMachine/<ステートマシン名>」と命名規約によって統一しているので、「/aws/stateMachine/*」をリソースポリシーのResource部分に追記できれば万事解決しそうです。

調べたところ
~~~
aws logs put-resource-policy --policy-name <policyName> --policy-document file://<反映させたいpolicyDocumentが書かれたファイルパス>
~~~
でリソースポリシーを書き換えることができそうなことがわかりました！
>参考
>* [AWS CLI Command Reference/put-resource-policy](https://docs.aws.amazon.com/cli/latest/reference/logs/put-resource-policy.html#put-resource-policy)

### 書き換え用ファイル作成

ということで書き換え用ファイルを作成していきます。
~~~policyDocument.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:ap-northeast-1:<AWSアカウントID>:log-group:/aws/stateMachine/001:log-stream:*",
					"arn:aws:logs:ap-northeast-1:<AWSアカウントID>:log-group:/aws/stateMachine/002:log-stream:*",
					"arn:aws:logs:ap-northeast-1:<AWSアカウントID>:log-group:/aws/stateMachine/003:log-stream:*",
					"arn:aws:logs:ap-northeast-1:<AWSアカウントID>:log-group:/aws/stateMachine/004:log-stream:*",
					"arn:aws:logs:ap-northeast-1:<AWSアカウントID>:log-group:/aws/stateMachine/005:log-stream:*",
                    ~~~省略~~~
                    "arn:aws:logs:ap-northeast-1:<AWSアカウントID>:log-group:/aws/stateMachine/098:log-stream:*",
					"arn:aws:logs:ap-northeast-1:<AWSアカウントID>:log-group:/aws/stateMachine/099:log-stream:*",
					"arn:aws:logs:ap-northeast-1:<AWSアカウントID>:log-group:/aws/stateMachine/100:log-stream:*",
				    "arn:aws:logs:ap-northeast-1:<AWSアカウントID>:log-group:/aws/stateMachine/*" ←変更箇所
			],
			"Condition": {
				"StringEquals": {
					"aws:SourceAccount": "<AWSアカウントID>"
				},
				"ArnLike": {
					"aws:SourceArn": "arn:aws:logs:ap-northeast-1:<AWSアカウントID>:*"
				}
			}
		}
	]
}
~~~
「/aws/vendedlogs/*」と同じように「/aws/stateMachine/*」をResourceの末尾(上記ソース内Resource9行目)に足しました。しかし、足しただけではサイズ上限を超えてしまっている事実は変わりません。

ロググループ名「/aws/stateMachine/001」～「/aws/stateMachine/100」は「/aws/stateMachine/*」に包括されているのでこれらすべて消してしまいましょう。
~~~policyDocument.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:ap-northeast-1:<AWSアカウントID>:log-group:/aws/stateMachine/*"
			],
			"Condition": {
				"StringEquals": {
					"aws:SourceAccount": "<AWSアカウントID>"
				},
				"ArnLike": {
					"aws:SourceArn": "arn:aws:logs:ap-northeast-1:<AWSアカウントID>:*"
				}
			}
		}
	]
}
~~~
今まで∞行あったResource部分が１行にまとまり、とてもすっきりしました！
当然5120字の上限に抵触しているわけもありません。

このファイルをリソースポリシーに反映させていきましょう。

#### ファイル作成時の注意点
あくまで書き換えるのはリソースポリシー内の**policyDocumentのみ**です。
ファイル内に"policyName"から"lastUpdatedTime"まで含めると書き換えコマンドを打った時にエラーが吐かれてしまいます。
ファイル内に記載するのはpolicyDocumentのみにしましょう。
(私はここのエラーでしばらくつまりました…)

### 実際にリソースポリシーを書き換える

先に挙げたコマンドを使用して書き換えていくことにします。

おさらい
~~~
aws logs put-resource-policy --policy-name <policyName> --policy-document file://<反映させたいpolicyDocumentが書かれたファイルパス>
~~~
今回に対応させます↓
~~~
aws logs put-resource-policy --policy-name AWSLogDeliveryWrite20150319 --policy-document file:///aws/document/policyDocument.json
~~~
--policy-nameオプションで指定するものを間違えると意図しないリソースポリシーを書き換えてしまう可能性があるので注意してください。

実際に打ち込むとリソースポリシーのプレビューのようなものが出てきて正常終了したことがわかるような仕組みになっていました！(少なくとも私の環境では…)

もう一度該当リソースポリシーを表示してみます。
~~~
aws logs describe-resource-policies --region ap-northeast-1 | jq '.resourcePolicies[] | select(.policyName=="AWSLogDeliveryWrite20150319")'
~~~
~~~json
{
	"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:ap-northeast-1:<AWSアカウントID>:log-group:/aws/stateMachine/*"
				],
				"Condition": {
					"StringEquals": {
						"aws:SourceAccount": "<AWSアカウントID>"
					},
					"ArnLike": {
						"aws:SourceArn": "arn:aws:logs:ap-northeast-1:<AWSアカウントID>:*"
					}
				}
			}
		]
	},
	"lastUpdatedTime": 999999999999
}
~~~
* Resource部分が１行になっていること
* policyDocument部分以外は書き換わっていないこと
* リソースポリシーのサイズが相当小さくなっていること

が確認できました！
感動です。


#### 念のため確認
リソースポリシー内のpolicyDocumentを確認できるコマンドがあるのでそれを使ってみるとピンポイントに確認することができます。
~~~
aws logs describe-resource-policies --region ap-northeast-1 | jq '.resourcePolicies[] | select(.policyName=="AWSLogDeliveryWrite20150319") | .policyDocument | fromjson'
~~~
policyDocumentをjson形式にきれいに整形して表示してくれます。

### 動作確認
書き換えた後にステートマシンの動作確認を行ったところ以下の4点が確認できました。
* エラーなく正常にステートマシン＆ロググループが作成できること
* 「/aws/stateMachine/*」に準じたロググループ名で新規作成した場合、リソースポリシーには何も追記されないこと
* 「/aws/stateMachine/*」に準じていないロググループ名で新規作成した場合、今までと同様にリソースポリシーに追記されていること
* ステートマシンを動かしたときのログも正常にCloudWatchに出力されていること

完璧ですね。

案件内に制定されていた命名規約を変更せず、既存のステートマシンに手を入れることもなく、リソースポリシーの書き換えのみでサイズ上限エラーを回避することができました！

## 最後に
念のため、、、本当に念のためAWSサポートにこのことを質問したところ認識はあっていました。
公式に認められてる手法でもありそうです。

今回紹介したいくつかのコマンドはほかのリソースポリシーを操作する際にも流用できるものだと思うので、応用を効かせていろいろ役に立てていただけると嬉しいです。

サイズ上限エラーは落ち着いて対処すれば思ったより簡単に対処できるものなので、エラーにぶつかっても慌てずにいきましょう！