From a2d712e71363dadee4c9fde028ac0f714c957cd9 Mon Sep 17 00:00:00 2001 From: corymhall <43035978+corymhall@users.noreply.github.com> Date: Thu, 16 Jun 2022 19:12:48 +0000 Subject: [PATCH 1/2] feat(integ-tests): expose adding IAM policies to the assertion provider Currently the `AwsApiCall` construct will try and automatically create the correct IAM policy based on the service and api call being used. The assumption was that in most cases this would work, but it turns out that in the first couple use cases we've seen this is not the case. This PR adds another method `addToRolePolicy` on the `AssertionsProvider` construct and then makes the provider a `public` attribute on `AwsApICall`. This allows you to add additional policies. --- packages/@aws-cdk/integ-tests/README.md | 28 +++++++++++ .../lib/assertions/providers/provider.ts | 38 ++++++++++++++ .../integ-tests/lib/assertions/sdk.ts | 16 +++++- .../integ-tests/rosetta/default.ts-fixture | 1 + .../integ-tests/test/assertions/sdk.test.ts | 49 +++++++++++++++++++ 5 files changed, 131 insertions(+), 1 deletion(-) diff --git a/packages/@aws-cdk/integ-tests/README.md b/packages/@aws-cdk/integ-tests/README.md index 17e3419c286ca..ca2d294b3e048 100644 --- a/packages/@aws-cdk/integ-tests/README.md +++ b/packages/@aws-cdk/integ-tests/README.md @@ -264,6 +264,34 @@ integ.assertions.awsApiCall('SQS', 'receiveMessage', { }); ``` +By default, the `AwsApiCall` construct will automatically add the correct IAM policies +to allow the Lambda function to make the API call. It does this based on the `service` +and `api` that is provided. In the above example the service is `SQS` and the api is +`receiveMessage` so it will create a policy with `Action: 'sqs:ReceiveMessage`. + +There are some cases where the permissions do not exactly match the service/api call, for +example the S3 `listObjectsV2` api. In these cases it is possible to add the correct policy +by accessing the `provider` object. + +```ts +declare const app: App; +declare const stack: Stack; +declare const integ: IntegTest; + +const apiCall = integ.assertions.awsApiCall('SQS', 'receiveMessage', { + QueueUrl: 'url', +}); + +apiCall.provider.addToRolePolicy({ + Effect: 'Allow', + Action: ['s3:GetObject', 's3:ListBucket'], + Resource: ['*'], +}); +``` + +Note that addToRolePolicy() uses direct IAM JSON policy blobs, not a iam.PolicyStatement +object like you will see in the rest of the CDK. + ### EqualsAssertion This library currently provides the ability to assert that two values are equal diff --git a/packages/@aws-cdk/integ-tests/lib/assertions/providers/provider.ts b/packages/@aws-cdk/integ-tests/lib/assertions/providers/provider.ts index 1b4c64e963646..5a5e3378fa4fa 100644 --- a/packages/@aws-cdk/integ-tests/lib/assertions/providers/provider.ts +++ b/packages/@aws-cdk/integ-tests/lib/assertions/providers/provider.ts @@ -134,6 +134,24 @@ class SingletonFunction extends Construct { return new LambdaFunctionProvider(Stack.of(this), constructName); } + /** + * Add an IAM policy statement to the inline policy of the + * lambdas function's role + * + * **Please note**: this is a direct IAM JSON policy blob, *not* a `iam.PolicyStatement` + * object like you will see in the rest of the CDK. + * + * + * singleton.addToRolePolicy({ + * Effect: 'Allow', + * Action: 's3:GetObject', + * Resources: '*', + * }); + */ + public addToRolePolicy(statement: any): void { + this.policies.push(statement); + } + /** * Create a policy statement from a specific api call */ @@ -216,6 +234,26 @@ export class AssertionsProvider extends Construct { public addPolicyStatementFromSdkCall(service: string, api: string, resources?: string[]): void { this.handler.addPolicyStatementFromSdkCall(service, api, resources); } + + /** + * Add an IAM policy statement to the inline policy of the + * lambdas function's role + * + * **Please note**: this is a direct IAM JSON policy blob, *not* a `iam.PolicyStatement` + * object like you will see in the rest of the CDK. + * + * + * @example + * declare const provider: AssertionsProvider; + * provider.addToRolePolicy({ + * Effect: 'Allow', + * Action: 's3:GetObject', + * Resources: '*', + * }); + */ + public addToRolePolicy(statement: any): void { + this.handler.addToRolePolicy(statement); + } } function slugify(x: string): string { diff --git a/packages/@aws-cdk/integ-tests/lib/assertions/sdk.ts b/packages/@aws-cdk/integ-tests/lib/assertions/sdk.ts index a7322738fa8d9..97972e4c568ba 100644 --- a/packages/@aws-cdk/integ-tests/lib/assertions/sdk.ts +++ b/packages/@aws-cdk/integ-tests/lib/assertions/sdk.ts @@ -9,6 +9,20 @@ import { AssertionsProvider, SDK_RESOURCE_TYPE_PREFIX } from './providers'; * an API call using the AWS SDK */ export interface IAwsApiCall extends IConstruct { + /** + * access the AssertionsProvider. This can be used to add additional IAM policies + * the the provider role policy + * + * @example + * declare const apiCall: AwsApiCall; + * apiCall.provider.addToRolePolicy({ + * Effect: 'Allow', + * Action: ['s3:GetObject'], + * Resource: ['*'], + * }); + */ + readonly provider: AssertionsProvider; + /** * Returns the value of an attribute of the custom resource of an arbitrary * type. Attributes are returned from the custom resource provider through the @@ -110,7 +124,7 @@ export class AwsApiCall extends Construct implements IAwsApiCall { private flattenResponse: string = 'false'; private readonly name: string; - protected provider: AssertionsProvider; + public readonly provider: AssertionsProvider; constructor(scope: Construct, id: string, props: AwsApiCallProps) { super(scope, id); diff --git a/packages/@aws-cdk/integ-tests/rosetta/default.ts-fixture b/packages/@aws-cdk/integ-tests/rosetta/default.ts-fixture index 847ddd48128e2..7cf368abcf6db 100644 --- a/packages/@aws-cdk/integ-tests/rosetta/default.ts-fixture +++ b/packages/@aws-cdk/integ-tests/rosetta/default.ts-fixture @@ -11,6 +11,7 @@ import { AssertionType, LambdaInvokeFunction, Match, + AssertionsProvider, } from '@aws-cdk/integ-tests'; import { Construct } from 'constructs'; import { diff --git a/packages/@aws-cdk/integ-tests/test/assertions/sdk.test.ts b/packages/@aws-cdk/integ-tests/test/assertions/sdk.test.ts index d8d3d70ec1694..7be64290b1b01 100644 --- a/packages/@aws-cdk/integ-tests/test/assertions/sdk.test.ts +++ b/packages/@aws-cdk/integ-tests/test/assertions/sdk.test.ts @@ -44,6 +44,55 @@ describe('AwsApiCall', () => { param2: 2, }, }); + + }); + + test('add policy to provider', () => { + // GIVEN + const app = new App(); + const deplossert = new DeployAssert(app); + + // WHEN + const apiCall = deplossert.awsApiCall('MyService', 'MyApi', { + param1: 'val1', + param2: 2, + }); + apiCall.provider.addToRolePolicy({ + Effect: 'Allow', + Action: ['s3:GetObject'], + Resource: ['*'], + }); + + Template.fromStack(deplossert.scope).hasResourceProperties('AWS::IAM::Role', { + Policies: [ + { + PolicyName: 'Inline', + PolicyDocument: { + Version: '2012-10-17', + Statement: [ + { + Action: [ + 'myservice:MyApi', + ], + Effect: 'Allow', + Resource: [ + '*', + ], + }, + { + Action: [ + 's3:GetObject', + ], + Effect: 'Allow', + Resource: [ + '*', + ], + }, + ], + }, + }, + ], + }); }); describe('get attribute', () => { From 509e52d1c668addee070300bb9ffc5e30f926505 Mon Sep 17 00:00:00 2001 From: corymhall <43035978+corymhall@users.noreply.github.com> Date: Thu, 7 Jul 2022 12:38:15 +0000 Subject: [PATCH 2/2] fix readme example --- packages/@aws-cdk/integ-tests/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/@aws-cdk/integ-tests/README.md b/packages/@aws-cdk/integ-tests/README.md index ca2d294b3e048..b27610cbd531f 100644 --- a/packages/@aws-cdk/integ-tests/README.md +++ b/packages/@aws-cdk/integ-tests/README.md @@ -278,8 +278,8 @@ declare const app: App; declare const stack: Stack; declare const integ: IntegTest; -const apiCall = integ.assertions.awsApiCall('SQS', 'receiveMessage', { - QueueUrl: 'url', +const apiCall = integ.assertions.awsApiCall('S3', 'listObjectsV2', { + Bucket: 'mybucket', }); apiCall.provider.addToRolePolicy({