From 2eb7d73d8647d1b3a9e698bfbdabcad95809eb61 Mon Sep 17 00:00:00 2001 From: Tietew Date: Wed, 30 Nov 2022 19:36:25 +0900 Subject: [PATCH] feat(cloudfront): add convenience grant methods to IDistribution (#22709) This PR adds convenience grant methods to `IDistribution`. - `grant(identity, ...actions)` - generic grant method - `grantCreateInvalidation(identity)` - shorthand to grant `cloudfront:CreateInvalidation` action. Fixes #13159 ---- ### All Submissions: * [x] Have you followed the guidelines in our [Contributing guide?](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md) ### Adding new Unconventional Dependencies: * [ ] This PR adds new unconventional dependencies following the process described [here](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md/#adding-new-unconventional-dependencies) ### New Features * [x] Have you added the new feature to an [integration test](https://github.com/aws/aws-cdk/blob/main/INTEGRATION_TESTS.md)? * [x] Did you use `yarn integ` to deploy the infrastructure and generate the snapshot (i.e. `yarn integ` without `--dry-run`)? *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- packages/@aws-cdk/aws-cloudfront/README.md | 12 + .../aws-cloudfront/lib/distribution.ts | 45 +++ .../aws-cloudfront/lib/private/utils.ts | 14 + .../aws-cloudfront/lib/web-distribution.ts | 28 ++ .../aws-cloudfront/test/distribution.test.ts | 60 ++++ .../cdk.out | 2 +- .../integ-distribution-basic.assets.json | 6 +- .../integ-distribution-basic.template.json | 142 ++++++++++ .../integ.json | 12 +- ...efaultTestDeployAssert2D53EBF0.assets.json | 19 ++ ...aultTestDeployAssert2D53EBF0.template.json | 36 +++ .../manifest.json | 75 ++++- .../tree.json | 262 +++++++++++++++++- .../test/integ.distribution-basic.ts | 17 +- .../test/web-distribution.test.ts | 68 +++++ 15 files changed, 777 insertions(+), 21 deletions(-) create mode 100644 packages/@aws-cdk/aws-cloudfront/lib/private/utils.ts create mode 100644 packages/@aws-cdk/aws-cloudfront/test/integ.distribution-basic.js.snapshot/integdistributionbasicdistributionbasictestDefaultTestDeployAssert2D53EBF0.assets.json create mode 100644 packages/@aws-cdk/aws-cloudfront/test/integ.distribution-basic.js.snapshot/integdistributionbasicdistributionbasictestDefaultTestDeployAssert2D53EBF0.template.json diff --git a/packages/@aws-cdk/aws-cloudfront/README.md b/packages/@aws-cdk/aws-cloudfront/README.md index 4991464d5f1b1..69d799f41138f 100644 --- a/packages/@aws-cdk/aws-cloudfront/README.md +++ b/packages/@aws-cdk/aws-cloudfront/README.md @@ -590,6 +590,18 @@ const distribution = cloudfront.Distribution.fromDistributionAttributes(this, 'I }); ``` +### Permissions + +Use the `grant()` method to allow actions on the distribution. +`grantCreateInvalidation()` is a shorthand to allow `CreateInvalidation`. + +```ts +declare const distribution: cloudfront.Distribution; +declare const lambdaFn: lambda.Function; +distribution.grant(lambdaFn, 'cloudfront:ListInvalidations', 'cloudfront:GetInvalidation'); +distribution.grantCreateInvalidation(lambdaFn); +``` + ## Migrating from the original CloudFrontWebDistribution to the newer Distribution construct It's possible to migrate a distribution from the original to the modern API. diff --git a/packages/@aws-cdk/aws-cloudfront/lib/distribution.ts b/packages/@aws-cdk/aws-cloudfront/lib/distribution.ts index 026b11f13d50f..92dc1561c7016 100644 --- a/packages/@aws-cdk/aws-cloudfront/lib/distribution.ts +++ b/packages/@aws-cdk/aws-cloudfront/lib/distribution.ts @@ -1,4 +1,5 @@ import * as acm from '@aws-cdk/aws-certificatemanager'; +import * as iam from '@aws-cdk/aws-iam'; import * as lambda from '@aws-cdk/aws-lambda'; import * as s3 from '@aws-cdk/aws-s3'; import { ArnFormat, IResource, Lazy, Resource, Stack, Token, Duration, Names, FeatureFlags } from '@aws-cdk/core'; @@ -12,6 +13,7 @@ import { IKeyGroup } from './key-group'; import { IOrigin, OriginBindConfig, OriginBindOptions } from './origin'; import { IOriginRequestPolicy } from './origin-request-policy'; import { CacheBehavior } from './private/cache-behavior'; +import { formatDistributionArn } from './private/utils'; import { IResponseHeadersPolicy } from './response-headers-policy'; /** @@ -39,6 +41,22 @@ export interface IDistribution extends IResource { * @attribute */ readonly distributionId: string; + + /** + * Adds an IAM policy statement associated with this distribution to an IAM + * principal's policy. + * + * @param identity The principal + * @param actions The set of actions to allow (i.e. "cloudfront:ListInvalidations") + */ + grant(identity: iam.IGrantable, ...actions: string[]): iam.Grant; + + /** + * Grant to create invalidations for this bucket to an IAM principal (Role/Group/User). + * + * @param identity The principal + */ + grantCreateInvalidation(identity: iam.IGrantable): iam.Grant; } /** @@ -257,6 +275,13 @@ export class Distribution extends Resource implements IDistribution { this.distributionDomainName = attrs.domainName; this.distributionId = attrs.distributionId; } + + public grant(grantee: iam.IGrantable, ...actions: string[]): iam.Grant { + return iam.Grant.addToPrincipal({ grantee, actions, resourceArns: [formatDistributionArn(this)] }); + } + public grantCreateInvalidation(grantee: iam.IGrantable): iam.Grant { + return this.grant(grantee, 'cloudfront:CreateInvalidation'); + } }(); } @@ -345,6 +370,26 @@ export class Distribution extends Resource implements IDistribution { this.additionalBehaviors.push(new CacheBehavior(originId, { pathPattern, ...behaviorOptions })); } + /** + * Adds an IAM policy statement associated with this distribution to an IAM + * principal's policy. + * + * @param identity The principal + * @param actions The set of actions to allow (i.e. "cloudfront:ListInvalidations") + */ + public grant(identity: iam.IGrantable, ...actions: string[]): iam.Grant { + return iam.Grant.addToPrincipal({ grantee: identity, actions, resourceArns: [formatDistributionArn(this)] }); + } + + /** + * Grant to create invalidations for this bucket to an IAM principal (Role/Group/User). + * + * @param identity The principal + */ + public grantCreateInvalidation(identity: iam.IGrantable): iam.Grant { + return this.grant(identity, 'cloudfront:CreateInvalidation'); + } + private addOrigin(origin: IOrigin, isFailoverOrigin: boolean = false): string { const ORIGIN_ID_MAX_LENGTH = 128; diff --git a/packages/@aws-cdk/aws-cloudfront/lib/private/utils.ts b/packages/@aws-cdk/aws-cloudfront/lib/private/utils.ts new file mode 100644 index 0000000000000..256e161181193 --- /dev/null +++ b/packages/@aws-cdk/aws-cloudfront/lib/private/utils.ts @@ -0,0 +1,14 @@ +import { Stack } from '@aws-cdk/core'; +import { IDistribution } from '..'; + +/** + * Format distribution ARN from stack and distribution ID. + */ +export function formatDistributionArn(dist: IDistribution) { + return Stack.of(dist).formatArn({ + service: 'cloudfront', + region: '', + resource: 'distribution', + resourceName: dist.distributionId, + }); +} diff --git a/packages/@aws-cdk/aws-cloudfront/lib/web-distribution.ts b/packages/@aws-cdk/aws-cloudfront/lib/web-distribution.ts index 5f1d8e12b1234..d196f556d0bc8 100644 --- a/packages/@aws-cdk/aws-cloudfront/lib/web-distribution.ts +++ b/packages/@aws-cdk/aws-cloudfront/lib/web-distribution.ts @@ -10,6 +10,7 @@ import { FunctionAssociation } from './function'; import { GeoRestriction } from './geo-restriction'; import { IKeyGroup } from './key-group'; import { IOriginAccessIdentity } from './origin-access-identity'; +import { formatDistributionArn } from './private/utils'; /** * HTTP status code to failover to second origin @@ -758,6 +759,13 @@ export class CloudFrontWebDistribution extends cdk.Resource implements IDistribu this.distributionDomainName = attrs.domainName; this.distributionId = attrs.distributionId; } + + public grant(grantee: iam.IGrantable, ...actions: string[]): iam.Grant { + return iam.Grant.addToPrincipal({ grantee, actions, resourceArns: [formatDistributionArn(this)] }); + } + public grantCreateInvalidation(identity: iam.IGrantable): iam.Grant { + return this.grant(identity, 'cloudfront:CreateInvalidation'); + } }(); } @@ -983,6 +991,26 @@ export class CloudFrontWebDistribution extends cdk.Resource implements IDistribu this.distributionId = distribution.ref; } + /** + * Adds an IAM policy statement associated with this distribution to an IAM + * principal's policy. + * + * @param identity The principal + * @param actions The set of actions to allow (i.e. "cloudfront:ListInvalidations") + */ + public grant(identity: iam.IGrantable, ...actions: string[]): iam.Grant { + return iam.Grant.addToPrincipal({ grantee: identity, actions, resourceArns: [formatDistributionArn(this)] }); + } + + /** + * Grant to create invalidations for this bucket to an IAM principal (Role/Group/User). + * + * @param identity The principal + */ + grantCreateInvalidation(identity: iam.IGrantable): iam.Grant { + return this.grant(identity, 'cloudfront:CreateInvalidation'); + } + private toBehavior(input: BehaviorWithOrigin, protoPolicy?: ViewerProtocolPolicy) { let toReturn = { allowedMethods: this.METHOD_LOOKUP_MAP[input.allowedMethods || CloudFrontAllowedMethods.GET_HEAD], diff --git a/packages/@aws-cdk/aws-cloudfront/test/distribution.test.ts b/packages/@aws-cdk/aws-cloudfront/test/distribution.test.ts index dcf6db0cdfc89..a753ea5e1e1ea 100644 --- a/packages/@aws-cdk/aws-cloudfront/test/distribution.test.ts +++ b/packages/@aws-cdk/aws-cloudfront/test/distribution.test.ts @@ -1,5 +1,6 @@ import { Match, Template } from '@aws-cdk/assertions'; import * as acm from '@aws-cdk/aws-certificatemanager'; +import * as iam from '@aws-cdk/aws-iam'; import * as lambda from '@aws-cdk/aws-lambda'; import * as s3 from '@aws-cdk/aws-s3'; import { App, Duration, Stack } from '@aws-cdk/core'; @@ -1079,3 +1080,62 @@ describe('supported HTTP versions', () => { }); }); }); + +test('grants custom actions', () => { + const distribution = new Distribution(stack, 'Distribution', { + defaultBehavior: { origin: defaultOrigin() }, + }); + const role = new iam.Role(stack, 'Role', { + assumedBy: new iam.AccountRootPrincipal(), + }); + distribution.grant(role, 'cloudfront:ListInvalidations', 'cloudfront:GetInvalidation'); + + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { + PolicyDocument: { + Statement: [ + { + Action: [ + 'cloudfront:ListInvalidations', + 'cloudfront:GetInvalidation', + ], + Resource: { + 'Fn::Join': [ + '', [ + 'arn:', { Ref: 'AWS::Partition' }, ':cloudfront::1234:distribution/', + { Ref: 'Distribution830FAC52' }, + ], + ], + }, + }, + ], + }, + }); +}); + +test('grants createInvalidation', () => { + const distribution = new Distribution(stack, 'Distribution', { + defaultBehavior: { origin: defaultOrigin() }, + }); + const role = new iam.Role(stack, 'Role', { + assumedBy: new iam.AccountRootPrincipal(), + }); + distribution.grantCreateInvalidation(role); + + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { + PolicyDocument: { + Statement: [ + { + Action: 'cloudfront:CreateInvalidation', + Resource: { + 'Fn::Join': [ + '', [ + 'arn:', { Ref: 'AWS::Partition' }, ':cloudfront::1234:distribution/', + { Ref: 'Distribution830FAC52' }, + ], + ], + }, + }, + ], + }, + }); +}); diff --git a/packages/@aws-cdk/aws-cloudfront/test/integ.distribution-basic.js.snapshot/cdk.out b/packages/@aws-cdk/aws-cloudfront/test/integ.distribution-basic.js.snapshot/cdk.out index 588d7b269d34f..8ecc185e9dbee 100644 --- a/packages/@aws-cdk/aws-cloudfront/test/integ.distribution-basic.js.snapshot/cdk.out +++ b/packages/@aws-cdk/aws-cloudfront/test/integ.distribution-basic.js.snapshot/cdk.out @@ -1 +1 @@ -{"version":"20.0.0"} \ No newline at end of file +{"version":"21.0.0"} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-cloudfront/test/integ.distribution-basic.js.snapshot/integ-distribution-basic.assets.json b/packages/@aws-cdk/aws-cloudfront/test/integ.distribution-basic.js.snapshot/integ-distribution-basic.assets.json index 29820d1a3ecc6..b125d782f2c49 100644 --- a/packages/@aws-cdk/aws-cloudfront/test/integ.distribution-basic.js.snapshot/integ-distribution-basic.assets.json +++ b/packages/@aws-cdk/aws-cloudfront/test/integ.distribution-basic.js.snapshot/integ-distribution-basic.assets.json @@ -1,7 +1,7 @@ { - "version": "20.0.0", + "version": "21.0.0", "files": { - "e56a9eb81eb88d748b8062f87488a5f2d9ec1137d330b9d5e0eb33f3ea9de5c7": { + "36c6ded30c4c42464c2753c997c004bf740b9311a744c363fda5f951929a9504": { "source": { "path": "integ-distribution-basic.template.json", "packaging": "file" @@ -9,7 +9,7 @@ "destinations": { "current_account-current_region": { "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", - "objectKey": "e56a9eb81eb88d748b8062f87488a5f2d9ec1137d330b9d5e0eb33f3ea9de5c7.json", + "objectKey": "36c6ded30c4c42464c2753c997c004bf740b9311a744c363fda5f951929a9504.json", "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" } } diff --git a/packages/@aws-cdk/aws-cloudfront/test/integ.distribution-basic.js.snapshot/integ-distribution-basic.template.json b/packages/@aws-cdk/aws-cloudfront/test/integ.distribution-basic.js.snapshot/integ-distribution-basic.template.json index e588e66e45ec6..cb1cd4fc46575 100644 --- a/packages/@aws-cdk/aws-cloudfront/test/integ.distribution-basic.js.snapshot/integ-distribution-basic.template.json +++ b/packages/@aws-cdk/aws-cloudfront/test/integ.distribution-basic.js.snapshot/integ-distribution-basic.template.json @@ -24,6 +24,148 @@ ] } } + }, + "Role13A5C70C1": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "AWS": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::", + { + "Ref": "AWS::AccountId" + }, + ":root" + ] + ] + } + } + } + ], + "Version": "2012-10-17" + } + } + }, + "Role1DefaultPolicyD3EF4D0A": { + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": "cloudfront:CreateInvalidation", + "Effect": "Allow", + "Resource": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":cloudfront::", + { + "Ref": "AWS::AccountId" + }, + ":distribution/", + { + "Ref": "DistB3B78991" + } + ] + ] + } + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "Role1DefaultPolicyD3EF4D0A", + "Roles": [ + { + "Ref": "Role13A5C70C1" + } + ] + } + }, + "Role291939BC6": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "AWS": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::", + { + "Ref": "AWS::AccountId" + }, + ":root" + ] + ] + } + } + } + ], + "Version": "2012-10-17" + } + } + }, + "Role2DefaultPolicy3A7A0A1B": { + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": "cloudfront:ListInvalidations", + "Effect": "Allow", + "Resource": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":cloudfront::", + { + "Ref": "AWS::AccountId" + }, + ":distribution/", + { + "Ref": "DistB3B78991" + } + ] + ] + } + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "Role2DefaultPolicy3A7A0A1B", + "Roles": [ + { + "Ref": "Role291939BC6" + } + ] + } } }, "Parameters": { diff --git a/packages/@aws-cdk/aws-cloudfront/test/integ.distribution-basic.js.snapshot/integ.json b/packages/@aws-cdk/aws-cloudfront/test/integ.distribution-basic.js.snapshot/integ.json index e81ef00d4c6c4..4260a98421150 100644 --- a/packages/@aws-cdk/aws-cloudfront/test/integ.distribution-basic.js.snapshot/integ.json +++ b/packages/@aws-cdk/aws-cloudfront/test/integ.distribution-basic.js.snapshot/integ.json @@ -1,14 +1,12 @@ { - "version": "20.0.0", + "version": "21.0.0", "testCases": { - "integ.distribution-basic": { + "integ-distribution-basic/distribution-basic-test/DefaultTest": { "stacks": [ "integ-distribution-basic" ], - "diffAssets": false, - "stackUpdateWorkflow": true + "assertionStack": "integ-distribution-basic/distribution-basic-test/DefaultTest/DeployAssert", + "assertionStackName": "integdistributionbasicdistributionbasictestDefaultTestDeployAssert2D53EBF0" } - }, - "synthContext": {}, - "enableLookups": false + } } \ No newline at end of file diff --git a/packages/@aws-cdk/aws-cloudfront/test/integ.distribution-basic.js.snapshot/integdistributionbasicdistributionbasictestDefaultTestDeployAssert2D53EBF0.assets.json b/packages/@aws-cdk/aws-cloudfront/test/integ.distribution-basic.js.snapshot/integdistributionbasicdistributionbasictestDefaultTestDeployAssert2D53EBF0.assets.json new file mode 100644 index 0000000000000..67e3cdaa9ec4a --- /dev/null +++ b/packages/@aws-cdk/aws-cloudfront/test/integ.distribution-basic.js.snapshot/integdistributionbasicdistributionbasictestDefaultTestDeployAssert2D53EBF0.assets.json @@ -0,0 +1,19 @@ +{ + "version": "21.0.0", + "files": { + "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22": { + "source": { + "path": "integdistributionbasicdistributionbasictestDefaultTestDeployAssert2D53EBF0.template.json", + "packaging": "file" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22.json", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" + } + } + } + }, + "dockerImages": {} +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-cloudfront/test/integ.distribution-basic.js.snapshot/integdistributionbasicdistributionbasictestDefaultTestDeployAssert2D53EBF0.template.json b/packages/@aws-cdk/aws-cloudfront/test/integ.distribution-basic.js.snapshot/integdistributionbasicdistributionbasictestDefaultTestDeployAssert2D53EBF0.template.json new file mode 100644 index 0000000000000..ad9d0fb73d1dd --- /dev/null +++ b/packages/@aws-cdk/aws-cloudfront/test/integ.distribution-basic.js.snapshot/integdistributionbasicdistributionbasictestDefaultTestDeployAssert2D53EBF0.template.json @@ -0,0 +1,36 @@ +{ + "Parameters": { + "BootstrapVersion": { + "Type": "AWS::SSM::Parameter::Value", + "Default": "/cdk-bootstrap/hnb659fds/version", + "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]" + } + }, + "Rules": { + "CheckBootstrapVersion": { + "Assertions": [ + { + "Assert": { + "Fn::Not": [ + { + "Fn::Contains": [ + [ + "1", + "2", + "3", + "4", + "5" + ], + { + "Ref": "BootstrapVersion" + } + ] + } + ] + }, + "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI." + } + ] + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-cloudfront/test/integ.distribution-basic.js.snapshot/manifest.json b/packages/@aws-cdk/aws-cloudfront/test/integ.distribution-basic.js.snapshot/manifest.json index 4b6ac2d350696..614413871b6ce 100644 --- a/packages/@aws-cdk/aws-cloudfront/test/integ.distribution-basic.js.snapshot/manifest.json +++ b/packages/@aws-cdk/aws-cloudfront/test/integ.distribution-basic.js.snapshot/manifest.json @@ -1,5 +1,5 @@ { - "version": "20.0.0", + "version": "21.0.0", "artifacts": { "Tree": { "type": "cdk:tree", @@ -7,6 +7,53 @@ "file": "tree.json" } }, + "integdistributionbasicdistributionbasictestDefaultTestDeployAssert2D53EBF0.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "integdistributionbasicdistributionbasictestDefaultTestDeployAssert2D53EBF0.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "integdistributionbasicdistributionbasictestDefaultTestDeployAssert2D53EBF0": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "integdistributionbasicdistributionbasictestDefaultTestDeployAssert2D53EBF0.template.json", + "validateOnSynth": false, + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}", + "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", + "additionalDependencies": [ + "integdistributionbasicdistributionbasictestDefaultTestDeployAssert2D53EBF0.assets" + ], + "lookupRole": { + "arn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-lookup-role-${AWS::AccountId}-${AWS::Region}", + "requiresBootstrapStackVersion": 8, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "dependencies": [ + "integdistributionbasicdistributionbasictestDefaultTestDeployAssert2D53EBF0.assets" + ], + "metadata": { + "/integ-distribution-basic/distribution-basic-test/DefaultTest/DeployAssert/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/integ-distribution-basic/distribution-basic-test/DefaultTest/DeployAssert/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "integ-distribution-basic/distribution-basic-test/DefaultTest/DeployAssert" + }, "integ-distribution-basic.assets": { "type": "cdk:asset-manifest", "properties": { @@ -23,7 +70,7 @@ "validateOnSynth": false, "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}", "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}", - "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/e56a9eb81eb88d748b8062f87488a5f2d9ec1137d330b9d5e0eb33f3ea9de5c7.json", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/36c6ded30c4c42464c2753c997c004bf740b9311a744c363fda5f951929a9504.json", "requiresBootstrapStackVersion": 6, "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", "additionalDependencies": [ @@ -45,6 +92,30 @@ "data": "DistB3B78991" } ], + "/integ-distribution-basic/Role1/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "Role13A5C70C1" + } + ], + "/integ-distribution-basic/Role1/DefaultPolicy/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "Role1DefaultPolicyD3EF4D0A" + } + ], + "/integ-distribution-basic/Role2/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "Role291939BC6" + } + ], + "/integ-distribution-basic/Role2/DefaultPolicy/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "Role2DefaultPolicy3A7A0A1B" + } + ], "/integ-distribution-basic/BootstrapVersion": [ { "type": "aws:cdk:logicalId", diff --git a/packages/@aws-cdk/aws-cloudfront/test/integ.distribution-basic.js.snapshot/tree.json b/packages/@aws-cdk/aws-cloudfront/test/integ.distribution-basic.js.snapshot/tree.json index c658ada510875..c174630e6f177 100644 --- a/packages/@aws-cdk/aws-cloudfront/test/integ.distribution-basic.js.snapshot/tree.json +++ b/packages/@aws-cdk/aws-cloudfront/test/integ.distribution-basic.js.snapshot/tree.json @@ -9,7 +9,7 @@ "path": "Tree", "constructInfo": { "fqn": "constructs.Construct", - "version": "10.1.85" + "version": "10.1.140" } }, "integ-distribution-basic": { @@ -25,7 +25,7 @@ "path": "integ-distribution-basic/Dist/Origin1", "constructInfo": { "fqn": "constructs.Construct", - "version": "10.1.85" + "version": "10.1.140" } }, "Resource": { @@ -67,17 +67,267 @@ "fqn": "@aws-cdk/aws-cloudfront.Distribution", "version": "0.0.0" } + }, + "Role1": { + "id": "Role1", + "path": "integ-distribution-basic/Role1", + "children": { + "Resource": { + "id": "Resource", + "path": "integ-distribution-basic/Role1/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::IAM::Role", + "aws:cdk:cloudformation:props": { + "assumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "AWS": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::", + { + "Ref": "AWS::AccountId" + }, + ":root" + ] + ] + } + } + } + ], + "Version": "2012-10-17" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-iam.CfnRole", + "version": "0.0.0" + } + }, + "DefaultPolicy": { + "id": "DefaultPolicy", + "path": "integ-distribution-basic/Role1/DefaultPolicy", + "children": { + "Resource": { + "id": "Resource", + "path": "integ-distribution-basic/Role1/DefaultPolicy/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::IAM::Policy", + "aws:cdk:cloudformation:props": { + "policyDocument": { + "Statement": [ + { + "Action": "cloudfront:CreateInvalidation", + "Effect": "Allow", + "Resource": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":cloudfront::", + { + "Ref": "AWS::AccountId" + }, + ":distribution/", + { + "Ref": "DistB3B78991" + } + ] + ] + } + } + ], + "Version": "2012-10-17" + }, + "policyName": "Role1DefaultPolicyD3EF4D0A", + "roles": [ + { + "Ref": "Role13A5C70C1" + } + ] + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-iam.CfnPolicy", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-iam.Policy", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-iam.Role", + "version": "0.0.0" + } + }, + "Role2": { + "id": "Role2", + "path": "integ-distribution-basic/Role2", + "children": { + "Resource": { + "id": "Resource", + "path": "integ-distribution-basic/Role2/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::IAM::Role", + "aws:cdk:cloudformation:props": { + "assumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "AWS": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::", + { + "Ref": "AWS::AccountId" + }, + ":root" + ] + ] + } + } + } + ], + "Version": "2012-10-17" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-iam.CfnRole", + "version": "0.0.0" + } + }, + "DefaultPolicy": { + "id": "DefaultPolicy", + "path": "integ-distribution-basic/Role2/DefaultPolicy", + "children": { + "Resource": { + "id": "Resource", + "path": "integ-distribution-basic/Role2/DefaultPolicy/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::IAM::Policy", + "aws:cdk:cloudformation:props": { + "policyDocument": { + "Statement": [ + { + "Action": "cloudfront:ListInvalidations", + "Effect": "Allow", + "Resource": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":cloudfront::", + { + "Ref": "AWS::AccountId" + }, + ":distribution/", + { + "Ref": "DistB3B78991" + } + ] + ] + } + } + ], + "Version": "2012-10-17" + }, + "policyName": "Role2DefaultPolicy3A7A0A1B", + "roles": [ + { + "Ref": "Role291939BC6" + } + ] + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-iam.CfnPolicy", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-iam.Policy", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-iam.Role", + "version": "0.0.0" + } + }, + "distribution-basic-test": { + "id": "distribution-basic-test", + "path": "integ-distribution-basic/distribution-basic-test", + "children": { + "DefaultTest": { + "id": "DefaultTest", + "path": "integ-distribution-basic/distribution-basic-test/DefaultTest", + "children": { + "Default": { + "id": "Default", + "path": "integ-distribution-basic/distribution-basic-test/DefaultTest/Default", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.1.140" + } + }, + "DeployAssert": { + "id": "DeployAssert", + "path": "integ-distribution-basic/distribution-basic-test/DefaultTest/DeployAssert", + "constructInfo": { + "fqn": "@aws-cdk/core.Stack", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests.IntegTestCase", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests.IntegTest", + "version": "0.0.0" + } } }, "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.1.85" + "fqn": "@aws-cdk/core.Stack", + "version": "0.0.0" } } }, "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.1.85" + "fqn": "@aws-cdk/core.App", + "version": "0.0.0" } } } \ No newline at end of file diff --git a/packages/@aws-cdk/aws-cloudfront/test/integ.distribution-basic.ts b/packages/@aws-cdk/aws-cloudfront/test/integ.distribution-basic.ts index 61fd0f0872077..a76a957f6d1fa 100644 --- a/packages/@aws-cdk/aws-cloudfront/test/integ.distribution-basic.ts +++ b/packages/@aws-cdk/aws-cloudfront/test/integ.distribution-basic.ts @@ -1,12 +1,25 @@ +import * as iam from '@aws-cdk/aws-iam'; import * as cdk from '@aws-cdk/core'; +import { IntegTest } from '@aws-cdk/integ-tests'; import * as cloudfront from '../lib'; import { TestOrigin } from './test-origin'; const app = new cdk.App(); const stack = new cdk.Stack(app, 'integ-distribution-basic'); -new cloudfront.Distribution(stack, 'Dist', { +const distribution = new cloudfront.Distribution(stack, 'Dist', { defaultBehavior: { origin: new TestOrigin('www.example.com') }, }); -app.synth(); +const role1 = new iam.Role(stack, 'Role1', { + assumedBy: new iam.AccountRootPrincipal(), +}); +const role2 = new iam.Role(stack, 'Role2', { + assumedBy: new iam.AccountRootPrincipal(), +}); +distribution.grantCreateInvalidation(role1); +distribution.grant(role2, 'cloudfront:ListInvalidations'); + +new IntegTest(stack, 'distribution-basic-test', { + testCases: [stack], +}); diff --git a/packages/@aws-cdk/aws-cloudfront/test/web-distribution.test.ts b/packages/@aws-cdk/aws-cloudfront/test/web-distribution.test.ts index 1a960f9f3b8e4..e5b65d12915ed 100644 --- a/packages/@aws-cdk/aws-cloudfront/test/web-distribution.test.ts +++ b/packages/@aws-cdk/aws-cloudfront/test/web-distribution.test.ts @@ -1,5 +1,6 @@ import { Match, Template } from '@aws-cdk/assertions'; import * as certificatemanager from '@aws-cdk/aws-certificatemanager'; +import * as iam from '@aws-cdk/aws-iam'; import * as lambda from '@aws-cdk/aws-lambda'; import * as s3 from '@aws-cdk/aws-s3'; import { testDeprecated } from '@aws-cdk/cdk-build-tools'; @@ -1747,3 +1748,70 @@ added the ellipsis so a user would know there was more to r...`, }); }); + +test('grants custom actions', () => { + const stack = new cdk.Stack(); + const distribution = new CloudFrontWebDistribution(stack, 'Distribution', { + originConfigs: [{ + customOriginSource: { domainName: 'myorigin.com' }, + behaviors: [{ isDefaultBehavior: true }], + }], + }); + const role = new iam.Role(stack, 'Role', { + assumedBy: new iam.AccountRootPrincipal(), + }); + distribution.grant(role, 'cloudfront:ListInvalidations', 'cloudfront:GetInvalidation'); + + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { + PolicyDocument: { + Statement: [ + { + Action: [ + 'cloudfront:ListInvalidations', + 'cloudfront:GetInvalidation', + ], + Resource: { + 'Fn::Join': [ + '', [ + 'arn:', { Ref: 'AWS::Partition' }, ':cloudfront::', { Ref: 'AWS::AccountId' }, ':distribution/', + { Ref: 'DistributionCFDistribution882A7313' }, + ], + ], + }, + }, + ], + }, + }); +}); + +test('grants createInvalidation', () => { + const stack = new cdk.Stack(); + const distribution = new CloudFrontWebDistribution(stack, 'Distribution', { + originConfigs: [{ + customOriginSource: { domainName: 'myorigin.com' }, + behaviors: [{ isDefaultBehavior: true }], + }], + }); + const role = new iam.Role(stack, 'Role', { + assumedBy: new iam.AccountRootPrincipal(), + }); + distribution.grantCreateInvalidation(role); + + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { + PolicyDocument: { + Statement: [ + { + Action: 'cloudfront:CreateInvalidation', + Resource: { + 'Fn::Join': [ + '', [ + 'arn:', { Ref: 'AWS::Partition' }, ':cloudfront::', { Ref: 'AWS::AccountId' }, ':distribution/', + { Ref: 'DistributionCFDistribution882A7313' }, + ], + ], + }, + }, + ], + }, + }); +});