CloudFormationでIAMポリシーを作ってUserに付与するときの注意点

#TL;DR

- IAMユーザにインラインポリシーを当てるときに最大文字制限に引っかかってハマった話です。
- https://aws.amazon.com/jp/iam/faqs/
 > ユーザーポリシーサイズは最大 2,048 文字です。
ロールポリシーサイズは最大 10,240 文字です。
グループポリシーサイズは最大 5,120 文字です。

- マネジメントコンソールからポリシーを書いた場合はエラー内容が出てすぐに分かるのですが、CloudFormationでスタックをアップデートした場合に```CREATE(UPDATE) IN PROGRESS```のまま止まってしまいエラー内容も出ずハマりました
- グループポリシーにインラインポリシーを当て、ユーザをそのグループに入れればとりあえず回避できます。


# 何が起こったのか
Lambdaを中心にserverlessの構成を構築しているのですがLambdaに色々と権限を持たせたためにポリシーが長くなっています。（多分2,500文字程度)
具体的には、

- CloudWatchLogsへログを書き込む権限
- DynamoDBを操作する権限
‐ DynamoDB Triggerを操作する権限
- Cognitoの認証情報を操作する権限
- VPC内に入れるためにENIを操作する権限
- S3にアクセスするための権限
- SNS(プッシュ通知)を管理するための権限
- SQSを管理するための権限

といった権限をつらつらとポリシーに書いてCloudFormationでアップデートしました。ここまでは特に問題はなかったのです。

## 構成例(A)
### テーブル
|リソース|ポリシー|
|---|---|
|IamRoleLambda|IamPolicyLambda|

### CloudFormation
LambdaにRoleを渡すためのCloudFormationです。

```json
{
  "AWSTemplateFormatVersion": "2010-09-09",
  "Description": "Sample",
  "Resources": {
    "IamRoleLambda": {
      "Type": "AWS::IAM::Role",
      "Properties": {
        "AssumeRolePolicyDocument": {
          "Version": "2012-10-17",
          "Statement": [
            {
              "Effect": "Allow",
              "Principal": {
                "Service": [
                  "lambda.amazonaws.com"
                ]
              },
              "Action": [
                "sts:AssumeRole"
              ]
            }
          ]
        },
        "Path": "/"
      }
    },
    "IamPolicyLambda": {
      "Type": "AWS::IAM::Policy",
      "Properties": {
        "PolicyName": "${stage}-${project}-lambda",
        "PolicyDocument": {
          "Version": "2012-10-17",
          "Statement": [
            {
              "Effect": "Allow",
              "Action": [
                "logs:CreateLogGroup",
                "logs:CreateLogStream",
                "logs:PutLogEvents"
              ],
              "Resource": "arn:aws:logs:${region}:*:*"
            },
            {
              "Action": [
                "dynamodb:GetItem",
                "dynamodb:PutItem",
                "dynamodb:UpdateItem",
                "dynamodb:BatchGetItem",
                "dynamodb:BatchWriteItem",
                "dynamodb:DeleteItem",
                "dynamodb:Scan",
                "dynamodb:Query"
              ],
              "Effect": "Allow",
              "Resource": "arn:aws:dynamodb:${region}:${account}:table/${project}-*-${stage}"
            }
          ]
        },
        "Roles": [
          {
            "Ref": "IamRoleLambda"
          }
        ]
      }
    }
  }
}
```

# 問題発生
Lambdaに渡すのはIAMロールなので、ローカルで動作確認をしようと思った時に困ってしまいます。そこで、同じポリシーを持ったIAMユーザを準備しCloudFormationでアップデートした時に問題が発生しました！
いつまで待ってもアップデートが終わらないのです…。
> <img width="585" alt="スクリーンショット 2016-06-10 1.56.32.png" src="https://qiita-image-store.s3.amazonaws.com/0/68048/d2f214d6-fbb9-3202-1a36-68aa203cafe2.png">

※17:22:42に始まったAWS::IAM::Policyのアップデートが一向に終わらないので、17:28:50にキャンセルを掛けました

## 構成例(B)
### テーブル
|リソース|ポリシー||
|---|---|---|
|IamUserLambdaTest|IamPolicyLambda|←これでエラー発生|
|IamRoleLambda|IamPolicyLambda||

### CloudFormation
このときのCloudFormationです。```IamUserLambdaTest```周りが増えています。

```json
{
  "AWSTemplateFormatVersion": "2010-09-09",
  "Description": "Sample",
  "Resources": {
    "IamUserLambdaTest": {
      "Type": "AWS::IAM::User",
    },
    "IamRoleLambda": {
      "Type": "AWS::IAM::Role",
      "Properties": {
        "AssumeRolePolicyDocument": {
          "Version": "2012-10-17",
          "Statement": [
            {
              "Effect": "Allow",
              "Principal": {
                "Service": [
                  "lambda.amazonaws.com"
                ]
              },
              "Action": [
                "sts:AssumeRole"
              ]
            }
          ]
        },
        "Path": "/"
      }
    },
    "IamPolicyLambda": {
      "Type": "AWS::IAM::Policy",
      "Properties": {
        "PolicyName": "${stage}-${project}-lambda",
        "PolicyDocument": {
          "Version": "2012-10-17",
          "Statement": [
            {
              "Effect": "Allow",
              "Action": [
                "logs:CreateLogGroup",
                "logs:CreateLogStream",
                "logs:PutLogEvents"
              ],
              "Resource": "arn:aws:logs:${region}:*:*"
            },
            {
              "Action": [
                "dynamodb:GetItem",
                "dynamodb:PutItem",
                "dynamodb:UpdateItem",
                "dynamodb:BatchGetItem",
                "dynamodb:BatchWriteItem",
                "dynamodb:DeleteItem",
                "dynamodb:Scan",
                "dynamodb:Query"
              ],
              "Effect": "Allow",
              "Resource": "arn:aws:dynamodb:${region}:${account}:table/${project}-*-${stage}"
            }
          ]
        },
        "Roles": [
          {
            "Ref": "IamRoleLambda"
          }
        ],
        "Users": [
          {
            "Ref": "IamUserLambdaTest"
          }
        ]
      }
    }
  }
}
```

# 何が起こっていたのか
ここで全く分からずハマっていたのですが、マネジメントコンソールでIAMユーザに対して同様のポリシーを与えてみたところ、原因が発覚しました…。
> <img width="579" alt="スクリーンショット 2016-06-10 2.02.45.png" src="https://qiita-image-store.s3.amazonaws.com/0/68048/e96e17fd-f246-ba96-759a-7b0047187c1b.png">

なんか制限が掛かっている模様ですね…。
ということで、ドキュメントを確認します。（最初から見ておきましょう）
>https://aws.amazon.com/jp/iam/faqs/
ユーザーポリシーサイズは最大 2,048 文字です。
ロールポリシーサイズは最大 10,240 文字です。
グループポリシーサイズは最大 5,120 文字です。

ガーン…そういうことですか！
今回書いていたポリシーが恐らく2,500文字程度と思われるので、IAMユーザに対しては文字数オーバーだったのですね…。

CloudFormationでもエラーが発生しているはずなのに、エラーで終了しないのは何故なんでしょうね。

# 解決策
ドキュメントに依るとグループポリシーは5,120文字まで行けるということなので、そちらを使って対処しましょう。
すなわち、Lambdaに渡すロールと同様のポリシーをグループに与えて、ユーザはそのグループに属するという構成になります。


## 構成例(C)
### テーブル
|リソース|ポリシー||
|---|---|---|
|IamGroupLambdaTest|IamPolicyLambda|←グループにポリシーを当てる|
|IamUserLambdaTest|IamGroupLambdaTest|←ユーザにはグループを当てる|
|IamRoleLambda|IamPolicyLambda||

### CloudFormation
このときのCloudFormationです。グループが増えて、ユーザがそのグループに属する形となっております。


```json
{
  "AWSTemplateFormatVersion": "2010-09-09",
  "Description": "Sample",
  "Resources": {
    "IamGroupLambdaTest": {
      "Type": "AWS::IAM::Group"
    },
    "IamUserLambdaTest": {
      "Type": "AWS::IAM::User",
      "Properties": {
        "Groups": [
          {
            "Ref": "IamGroupLambdaTest"
          }
        ]
      }
    },
    "IamRoleLambda": {
      "Type": "AWS::IAM::Role",
      "Properties": {
        "AssumeRolePolicyDocument": {
          "Version": "2012-10-17",
          "Statement": [
            {
              "Effect": "Allow",
              "Principal": {
                "Service": [
                  "lambda.amazonaws.com"
                ]
              },
              "Action": [
                "sts:AssumeRole"
              ]
            }
          ]
        },
        "Path": "/"
      }
    },
    "IamPolicyLambda": {
      "Type": "AWS::IAM::Policy",
      "Properties": {
        "PolicyName": "${stage}-${project}-lambda",
        "PolicyDocument": {
          "Version": "2012-10-17",
          "Statement": [
            {
              "Effect": "Allow",
              "Action": [
                "logs:CreateLogGroup",
                "logs:CreateLogStream",
                "logs:PutLogEvents"
              ],
              "Resource": "arn:aws:logs:${region}:*:*"
            },
            {
              "Action": [
                "dynamodb:GetItem",
                "dynamodb:PutItem",
                "dynamodb:UpdateItem",
                "dynamodb:BatchGetItem",
                "dynamodb:BatchWriteItem",
                "dynamodb:DeleteItem",
                "dynamodb:Scan",
                "dynamodb:Query"
              ],
              "Effect": "Allow",
              "Resource": "arn:aws:dynamodb:${region}:${account}:table/${project}-*-${stage}"
            }
          ]
        },
        "Roles": [
          {
            "Ref": "IamRoleLambda"
          }
        ],
        "Groups": [
          {
            "Ref": "IamGroupLambdaTest"
          }
        ]
      }
    }
  }
}
```

# 結果
この変更により、無事CloudFormationでスタックをアップデートすることが出来ました！
> <img width="583" alt="スクリーンショット 2016-06-10 2.13.30.png" src="https://qiita-image-store.s3.amazonaws.com/0/68048/2eaf9633-901b-2047-9fd6-a41569fc14a9.png">

参考になれば幸いです。