Repository: aws-cloudformation/cfn-lint

Issue:
Add correct size checking to IAM ManagedPolicy resource types
*cfn-lint version: (cfn-lint 0.58.1)*

*Description of issue.*

The PolicyDocument specified in an IAM Managed Policy has a maximum length of 6,144 characters (after converted to JSON and minified).
https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_iam-limits.html

When creating through CloudFormation an AWS::IAM::ManagedPolicy and using conditionals to leverage a 3 level environment creation differentiation, is throwing an error **E3502 PolicyDocument JSON text cannot be longer than 6144 characters**.

That happens because the policy defined in CloudFormation with the 3 level conditionals exceeds the 6144 characters limit, but when it is created it only uses 1 of the paths in the conditionals so the size is not really over 6144 characters.

This is the sample code in CloudFormation:
```
  ResourcePolicy:
    Type: AWS::IAM::ManagedPolicy
    Properties:
      ManagedPolicyName: !Sub
        - ResourcePolicy
      PolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Sid: LambdaExecutionLogs
            Effect: Allow
            Action:
              - logs:CreateLogGroup
              - logs:CreateLogStream
              - logs:PutLogEvents
            Resource:
              !Split
                - ","
                - !Join
                    - ","
                    - - !If
                        - IsDev
                        - !Join
                          - ","
                          - 
                            - !Sub
                              arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/lambda/lambdadev1-${EnvNameCamelCase}
                            - !Sub
                              arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/lambda/lambdadev1-:*
                            - !Sub
                              arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/lambda/lambdadev2
                            - !Sub
                              arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/lambda/lambdadev2:*
                        - !Ref AWS::NoValue
                      - !If
                        - IsUat
                        - !Join
                          - ","
                          - 
                            - !Sub
                              arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/lambda/lambdauat1
                            - !Sub
                              arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/lambda/lambdauat1:*
                            - !Sub
                              arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/lambda/lambdauat2
                            - !Sub
                              arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/lambda/lambdauat2:*
                            - !Sub
                              arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/lambda/lambdauat3
                            - !Sub
                              arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/lambda/lambdauat3:*
                        - !Ref AWS::NoValue
                      - !If
                        - IsProd
                        - !Join
                          - ","
                          - 
                            - !Sub
                              arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/lambda/lambdaprod
                            - !Sub
                              arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/lambda/lambdaprod:*
                        - !Ref AWS::NoValue
          - Sid: ........
          . . .
```

So the missing feature is to be able to check the size of the policy which is going to be finally created, not the source CloudFormation code, so in that regard it needs to evaluate the parameter and resolve the conditionals to end up with the final resource code to be created.



Discussion:
Unfortunately probably approaching diminishing returns handling all those nested CloudFormation functions specifically for this IAM rule in my opinion

**Recommended workaround with [resource-level ignores](https://github.com/aws-cloudformation/cfn-python-lint#resource-based-metadata):**

```yaml
Resources:
  ResourcePolicy:
    Type: AWS::IAM::ManagedPolicy
    Metadata:
      cfn-lint:
        config:
          ignore_checks:
            - E3502
```

Resolving patch:
diff --git a/src/cfnlint/rules/resources/properties/JsonSize.py b/src/cfnlint/rules/resources/properties/JsonSize.py
--- a/src/cfnlint/rules/resources/properties/JsonSize.py
+++ b/src/cfnlint/rules/resources/properties/JsonSize.py
@@ -36,6 +36,7 @@ def check_value(self, value, path, prop, cfn, specs):
         """Check Role.AssumeRolePolicyDocument is within limits"""
         matches = []
 
+        #pylint: disable=too-many-return-statements
         def remove_functions(obj):
             """ Replaces intrinsic functions with string """
             if isinstance(obj, dict):
@@ -48,6 +49,8 @@ def remove_functions(obj):
                                     return re.sub(r'\${.*}', '', v)
                                 if isinstance(v, list):
                                     return re.sub(r'\${.*}', '', v[0])
+                            else:
+                                return ''
                         else:
                             new_obj[k] = remove_functions(v)
                             return new_obj


Test patch: