【问题标题】:AWS CDK testing referencesAWS CDK 测试参考
【发布时间】:2020-07-14 13:17:11
【问题描述】:

我正在尝试对我的 CDK 应用程序进行单元测试。我创建了一个角色,我想确保它分配了所有策略。由于角色和策略是不同的资源,因此 Cloud Formation 角色资源不提供策略。角色仅对策略有引用:

"MyRole4CBCE4C9": {
      "Type": "AWS::IAM::Role",
      "Properties": {
        "AssumeRolePolicyDocument": {
          "Statement": [
            {
              ...
            }
          ],
          "Version": "2012-10-17"
        },
        "ManagedPolicyArns": [
          {
            "Ref": "MyPolicyC18AB378"
          }
        ]
      },

在测试中我有:

expectCDK(stack).to(haveResource("AWS::IAM::Role", {
                AssumeRolePolicyDocument: {
                    Statement: [
                        ...
                    ],
                    Version: "2012-10-17",
                },
            }
        ));

我如何验证这个确切的角色是否有正确的政策?我脑子里的步骤如下:

  1. 从角色属性中获取“Ref”
  2. 通过此参考查找政策
  3. 在政策中声明所有必要的数据

但是,CDK 似乎没有提供通过逻辑 id 获取元素以及从 haveResource 获取资源作为对象的功能。

进行此类测试的 CDK 方法是什么?


UPD:似乎我可以使用 StackInspector 来处理它,但我仍然想知道,true 方法是什么。

【问题讨论】:

    标签: amazon-web-services unit-testing aws-cdk


    【解决方案1】:

    场景 1:您在同一个堆栈中创建角色和策略,并且该策略不会在任何其他堆栈中重复使用。

    在这种情况下,使用 iam.Policy 并将其附加在行内:

    export class CdkGetLogicalIdExampleStack extends cdk.Stack {
      role: iam.IRole;
    
      constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) {
        super(scope, id, props);
    
        const policy = new iam.Policy(this, 'policy', {
          policyName: 'policy',
          statements: [
            new iam.PolicyStatement({
              actions: ["s3:*"],
              resources: ['*'],
              effect: iam.Effect.ALLOW
            })
          ]
        });
    
        this.role = new iam.Role(this, 'my-role', {
          roleName: 'my-role',
          assumedBy: new iam.AccountPrincipal(this.account),
        });
    
        // Alternatively: this.role.attachInlinePolicy(policy);
        policy.attachToRole(this.role);
      }
    }
    

    角色作为堆栈的属性保存,因此在测试中很容易引用它:

    test('Empty Stack', () => {
      const app = new cdk.App();
      const stack = new CdkGetLogicalIdExample.CdkGetLogicalIdExampleStack(app, 'MyTestStack');
      const roleId = stack.getLogicalId(stack.role.node.findChild('Resource') as cdk.CfnElement);
    
      expectCDK(stack).to(haveResource("AWS::IAM::Policy", {
        "PolicyDocument": {
          "Statement": [
            {
              "Action": "s3:*",
              "Effect": "Allow",
              "Resource": "*"
            }
          ],
          "Version": "2012-10-17"
        },
        "PolicyName": "policy",
        "Roles": [
          {
            "Ref": roleId
          }
        ]
      }));
    });
    

    场景 2:您正在创建一个可在各种堆栈中使用的策略,因此您希望将其设为托管策略。

    测试这个场景稍微复杂一些,因为iam.ManagedPolicy 实现了IManagedPolicy 接口,它只提供了managedPolicyArn 属性(我使用的是CDK 1.124.0)。

    尽管如此,iam.ManagedPolicy 扩展了 cdk.Resource,所以我们可以通过以下方式欺骗 TypeScript 的转换机制:

    export class CdkGetLogicalIdExampleStack extends cdk.Stack {
      // note the type here
      managedPolicy: cdk.Resource | iam.IManagedPolicy;
      role: iam.IRole;
    
      constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) {
        super(scope, id, props);
    
        this.managedPolicy = new iam.ManagedPolicy(this, 'managed-policy', {
          managedPolicyName: 'managed-policy',
          document: new iam.PolicyDocument({
            statements: [
              new iam.PolicyStatement({
                actions: ["dynamodb:*"],
                resources: ["*"],
                effect: iam.Effect.ALLOW
              })
            ]
          })
        });
    
        this.role = new iam.Role(this, 'my-role', {
          roleName: 'my-role',
          assumedBy: new iam.AccountPrincipal(this.account),
        });
    
        // here we need to cast to iam.IManagedPolicy
        this.role.addManagedPolicy(this.managedPolicy as iam.IManagedPolicy);
      }
    }
    

    现在,可以进行测试了,因为我们可以将managedPolicy 属性作为cdk.Resource 访问:

      const roleId = stack.getLogicalId(stack.role.node.findChild('Resource') as cdk.CfnElement);
      // here we do the casting to cdk.Resource
      const managedPolicyResource = stack.managedPolicy as cdk.Resource;
      const managedPolicyId = stack.getLogicalId(managedPolicyResource.node.findChild('Resource') as cdk.CfnElement);
    
      expectCDK(stack).to(haveResource("AWS::IAM::ManagedPolicy", {
        "PolicyDocument": {
          "Statement": [
            {
              "Action": "dynamodb:*",
              "Effect": "Allow",
              "Resource": "*"
            }
          ],
          "Version": "2012-10-17"
        },
        "Description": "",
        "ManagedPolicyName": "managed-policy",
        "Path": "/"
      }));
    
      expectCDK(stack).to(haveResource("AWS::IAM::Role", {
        "AssumeRolePolicyDocument": {
          "Statement": [
            {
              "Action": "sts:AssumeRole",
              "Effect": "Allow",
              "Principal": {
                "AWS": {
                  "Fn::Join": [
                    "",
                    [
                      "arn:",
                      {
                        "Ref": "AWS::Partition"
                      },
                      ":iam::",
                      {
                        "Ref": "AWS::AccountId"
                      },
                      ":root"
                    ]
                  ]
                }
              }
            }
          ],
          "Version": "2012-10-17"
        },
        "ManagedPolicyArns": [
          {
            "Ref": managedPolicyId
          }
        ],
        "RoleName": "my-role"
      }));
    

    通过这种方式,您可以测试您的策略是否包含所有必要的数据以及角色和策略之间的关系是否正确。

    您可以访问完整的工作示例here

    【讨论】:

      猜你喜欢
      • 2020-12-13
      • 1970-01-01
      • 1970-01-01
      • 2020-02-08
      • 2021-09-20
      • 2021-12-31
      • 1970-01-01
      • 1970-01-01
      • 2019-12-06
      相关资源
      最近更新 更多