@@ -333,3 +333,213 @@ def test_updating_stack_with_iam_role(deploy_cfn_template, aws_client):
333333 "Role"
334334 )
335335 assert stack .outputs ["TestStackRoleName" ] == lambda_role_name_new
336+
337+
338+ @markers .aws .validated
339+ def test_managedpolicy_with_fn_sub_json_string (deploy_cfn_template , snapshot , aws_client ):
340+ snapshot .add_transformer (snapshot .transform .iam_api ())
341+
342+ policy_name = f"test-policy-{ short_uid ()} "
343+
344+ template_json = {
345+ "AWSTemplateFormatVersion" : "2010-09-09" ,
346+ "Parameters" : {"ClusterName" : {"Type" : "String" , "Default" : "test-cluster" }},
347+ "Resources" : {
348+ "TestPolicy" : {
349+ "Type" : "AWS::IAM::ManagedPolicy" ,
350+ "Properties" : {
351+ "ManagedPolicyName" : policy_name ,
352+ "PolicyDocument" : {
353+ "Fn::Sub" : json .dumps (
354+ {
355+ "Version" : "2012-10-17" ,
356+ "Statement" : [
357+ {
358+ "Effect" : "Allow" ,
359+ "Action" : "s3:GetObject" ,
360+ "Resource" : "arn:${AWS::Partition}:s3:::bucket-${ClusterName}-${AWS::Region}/*" ,
361+ }
362+ ],
363+ }
364+ )
365+ },
366+ },
367+ }
368+ },
369+ "Outputs" : {"PolicyArn" : {"Value" : {"Ref" : "TestPolicy" }}},
370+ }
371+
372+ stack = deploy_cfn_template (
373+ template = json .dumps (template_json ), parameters = {"ClusterName" : "my-cluster" }
374+ )
375+
376+ policy_arn = stack .outputs ["PolicyArn" ]
377+ policy_version = aws_client .iam .get_policy_version (
378+ PolicyArn = policy_arn ,
379+ VersionId = aws_client .iam .get_policy (PolicyArn = policy_arn )["Policy" ]["DefaultVersionId" ],
380+ )["PolicyVersion" ]
381+
382+ snapshot .match ("policy_document" , policy_version ["Document" ])
383+
384+
385+ @markers .aws .validated
386+ def test_managedpolicy_with_fn_sub_multiline_yaml (deploy_cfn_template , snapshot , aws_client ):
387+ snapshot .add_transformer (snapshot .transform .iam_api ())
388+
389+ policy_name = f"test-policy-{ short_uid ()} "
390+
391+ template_yaml = f"""
392+ AWSTemplateFormatVersion: "2010-09-09"
393+ Parameters:
394+ ClusterName:
395+ Type: String
396+ Default: "test-cluster"
397+ Resources:
398+ TestPolicy:
399+ Type: AWS::IAM::ManagedPolicy
400+ Properties:
401+ ManagedPolicyName: { policy_name }
402+ PolicyDocument: !Sub |
403+ {{
404+ "Version": "2012-10-17",
405+ "Statement": [
406+ {{
407+ "Effect": "Allow",
408+ "Action": "eks:DescribeCluster",
409+ "Resource": "arn:${{AWS::Partition}}:eks:${{AWS::Region}}:${{AWS::AccountId}}:cluster/${{ClusterName}}"
410+ }}
411+ ]
412+ }}
413+ Outputs:
414+ PolicyArn:
415+ Value: !Ref TestPolicy
416+ """
417+
418+ stack = deploy_cfn_template (template = template_yaml , parameters = {"ClusterName" : "my-cluster" })
419+
420+ policy_arn = stack .outputs ["PolicyArn" ]
421+ policy_version = aws_client .iam .get_policy_version (
422+ PolicyArn = policy_arn ,
423+ VersionId = aws_client .iam .get_policy (PolicyArn = policy_arn )["Policy" ]["DefaultVersionId" ],
424+ )["PolicyVersion" ]
425+
426+ snapshot .match ("policy_document_multiline" , policy_version ["Document" ])
427+
428+
429+ @markers .aws .validated
430+ def test_managedpolicy_with_fn_sub_and_arrays (deploy_cfn_template , snapshot , aws_client ):
431+ snapshot .add_transformer (snapshot .transform .iam_api ())
432+
433+ policy_name = f"test-policy-{ short_uid ()} "
434+
435+ template = {
436+ "AWSTemplateFormatVersion" : "2010-09-09" ,
437+ "Parameters" : {"BucketName" : {"Type" : "String" , "Default" : "my-bucket" }},
438+ "Resources" : {
439+ "TestPolicy" : {
440+ "Type" : "AWS::IAM::ManagedPolicy" ,
441+ "Properties" : {
442+ "ManagedPolicyName" : policy_name ,
443+ "PolicyDocument" : {
444+ "Fn::Sub" : json .dumps (
445+ {
446+ "Version" : "2012-10-17" ,
447+ "Statement" : [
448+ {
449+ "Effect" : "Allow" ,
450+ "Action" : ["s3:GetObject" , "s3:PutObject" ],
451+ "Resource" : [
452+ "arn:${AWS::Partition}:s3:::${BucketName}/*" ,
453+ "arn:${AWS::Partition}:s3:::${BucketName}-${AWS::Region}/*" ,
454+ ],
455+ }
456+ ],
457+ }
458+ )
459+ },
460+ },
461+ }
462+ },
463+ "Outputs" : {"PolicyArn" : {"Value" : {"Ref" : "TestPolicy" }}},
464+ }
465+
466+ stack = deploy_cfn_template (
467+ template = json .dumps (template ), parameters = {"BucketName" : "test-bucket" }
468+ )
469+
470+ policy_arn = stack .outputs ["PolicyArn" ]
471+ policy_version = aws_client .iam .get_policy_version (
472+ PolicyArn = policy_arn ,
473+ VersionId = aws_client .iam .get_policy (PolicyArn = policy_arn )["Policy" ]["DefaultVersionId" ],
474+ )["PolicyVersion" ]
475+
476+ snapshot .match ("policy_with_arrays" , policy_version ["Document" ])
477+
478+
479+ @markers .aws .validated
480+ def test_inline_policy_with_fn_sub_json_string (deploy_cfn_template , snapshot , aws_client ):
481+ """
482+ Test inline IAM Policy (AWS::IAM::Policy) with Fn::Sub returning a JSON string.
483+
484+ Verifies that inline policies also handle JSON strings from Fn::Sub correctly.
485+ """
486+ snapshot .add_transformer (snapshot .transform .iam_api ())
487+
488+ role_name = f"test-role-{ short_uid ()} "
489+ policy_name = f"test-policy-{ short_uid ()} "
490+
491+ template = {
492+ "AWSTemplateFormatVersion" : "2010-09-09" ,
493+ "Resources" : {
494+ "TestRole" : {
495+ "Type" : "AWS::IAM::Role" ,
496+ "Properties" : {
497+ "RoleName" : role_name ,
498+ "AssumeRolePolicyDocument" : {
499+ "Version" : "2012-10-17" ,
500+ "Statement" : [
501+ {
502+ "Effect" : "Allow" ,
503+ "Principal" : {"Service" : "lambda.amazonaws.com" },
504+ "Action" : "sts:AssumeRole" ,
505+ }
506+ ],
507+ },
508+ },
509+ },
510+ "TestPolicy" : {
511+ "Type" : "AWS::IAM::Policy" ,
512+ "Properties" : {
513+ "PolicyName" : policy_name ,
514+ "Roles" : [{"Ref" : "TestRole" }],
515+ "PolicyDocument" : {
516+ "Fn::Sub" : json .dumps (
517+ {
518+ "Version" : "2012-10-17" ,
519+ "Statement" : [
520+ {
521+ "Effect" : "Allow" ,
522+ "Action" : "s3:GetObject" ,
523+ "Resource" : "arn:${AWS::Partition}:s3:::my-bucket-${AWS::Region}/*" ,
524+ }
525+ ],
526+ }
527+ )
528+ },
529+ },
530+ },
531+ },
532+ }
533+
534+ deploy_cfn_template (template = json .dumps (template ))
535+
536+ # Verify the inline policy was attached to the role
537+ policies = aws_client .iam .list_role_policies (RoleName = role_name )
538+ assert policy_name in policies ["PolicyNames" ]
539+
540+ # Get the policy document
541+ policy_doc = aws_client .iam .get_role_policy (RoleName = role_name , PolicyName = policy_name )[
542+ "PolicyDocument"
543+ ]
544+
545+ snapshot .match ("inline_policy_document" , policy_doc )
0 commit comments