Skip to content

Commit 2eb7d73

Browse files
authored
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*
1 parent 85354f6 commit 2eb7d73

15 files changed

+777
-21
lines changed

packages/@aws-cdk/aws-cloudfront/README.md

+12
Original file line numberDiff line numberDiff line change
@@ -590,6 +590,18 @@ const distribution = cloudfront.Distribution.fromDistributionAttributes(this, 'I
590590
});
591591
```
592592

593+
### Permissions
594+
595+
Use the `grant()` method to allow actions on the distribution.
596+
`grantCreateInvalidation()` is a shorthand to allow `CreateInvalidation`.
597+
598+
```ts
599+
declare const distribution: cloudfront.Distribution;
600+
declare const lambdaFn: lambda.Function;
601+
distribution.grant(lambdaFn, 'cloudfront:ListInvalidations', 'cloudfront:GetInvalidation');
602+
distribution.grantCreateInvalidation(lambdaFn);
603+
```
604+
593605
## Migrating from the original CloudFrontWebDistribution to the newer Distribution construct
594606

595607
It's possible to migrate a distribution from the original to the modern API.

packages/@aws-cdk/aws-cloudfront/lib/distribution.ts

+45
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import * as acm from '@aws-cdk/aws-certificatemanager';
2+
import * as iam from '@aws-cdk/aws-iam';
23
import * as lambda from '@aws-cdk/aws-lambda';
34
import * as s3 from '@aws-cdk/aws-s3';
45
import { ArnFormat, IResource, Lazy, Resource, Stack, Token, Duration, Names, FeatureFlags } from '@aws-cdk/core';
@@ -12,6 +13,7 @@ import { IKeyGroup } from './key-group';
1213
import { IOrigin, OriginBindConfig, OriginBindOptions } from './origin';
1314
import { IOriginRequestPolicy } from './origin-request-policy';
1415
import { CacheBehavior } from './private/cache-behavior';
16+
import { formatDistributionArn } from './private/utils';
1517
import { IResponseHeadersPolicy } from './response-headers-policy';
1618

1719
/**
@@ -39,6 +41,22 @@ export interface IDistribution extends IResource {
3941
* @attribute
4042
*/
4143
readonly distributionId: string;
44+
45+
/**
46+
* Adds an IAM policy statement associated with this distribution to an IAM
47+
* principal's policy.
48+
*
49+
* @param identity The principal
50+
* @param actions The set of actions to allow (i.e. "cloudfront:ListInvalidations")
51+
*/
52+
grant(identity: iam.IGrantable, ...actions: string[]): iam.Grant;
53+
54+
/**
55+
* Grant to create invalidations for this bucket to an IAM principal (Role/Group/User).
56+
*
57+
* @param identity The principal
58+
*/
59+
grantCreateInvalidation(identity: iam.IGrantable): iam.Grant;
4260
}
4361

4462
/**
@@ -257,6 +275,13 @@ export class Distribution extends Resource implements IDistribution {
257275
this.distributionDomainName = attrs.domainName;
258276
this.distributionId = attrs.distributionId;
259277
}
278+
279+
public grant(grantee: iam.IGrantable, ...actions: string[]): iam.Grant {
280+
return iam.Grant.addToPrincipal({ grantee, actions, resourceArns: [formatDistributionArn(this)] });
281+
}
282+
public grantCreateInvalidation(grantee: iam.IGrantable): iam.Grant {
283+
return this.grant(grantee, 'cloudfront:CreateInvalidation');
284+
}
260285
}();
261286
}
262287

@@ -345,6 +370,26 @@ export class Distribution extends Resource implements IDistribution {
345370
this.additionalBehaviors.push(new CacheBehavior(originId, { pathPattern, ...behaviorOptions }));
346371
}
347372

373+
/**
374+
* Adds an IAM policy statement associated with this distribution to an IAM
375+
* principal's policy.
376+
*
377+
* @param identity The principal
378+
* @param actions The set of actions to allow (i.e. "cloudfront:ListInvalidations")
379+
*/
380+
public grant(identity: iam.IGrantable, ...actions: string[]): iam.Grant {
381+
return iam.Grant.addToPrincipal({ grantee: identity, actions, resourceArns: [formatDistributionArn(this)] });
382+
}
383+
384+
/**
385+
* Grant to create invalidations for this bucket to an IAM principal (Role/Group/User).
386+
*
387+
* @param identity The principal
388+
*/
389+
public grantCreateInvalidation(identity: iam.IGrantable): iam.Grant {
390+
return this.grant(identity, 'cloudfront:CreateInvalidation');
391+
}
392+
348393
private addOrigin(origin: IOrigin, isFailoverOrigin: boolean = false): string {
349394
const ORIGIN_ID_MAX_LENGTH = 128;
350395

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import { Stack } from '@aws-cdk/core';
2+
import { IDistribution } from '..';
3+
4+
/**
5+
* Format distribution ARN from stack and distribution ID.
6+
*/
7+
export function formatDistributionArn(dist: IDistribution) {
8+
return Stack.of(dist).formatArn({
9+
service: 'cloudfront',
10+
region: '',
11+
resource: 'distribution',
12+
resourceName: dist.distributionId,
13+
});
14+
}

packages/@aws-cdk/aws-cloudfront/lib/web-distribution.ts

+28
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import { FunctionAssociation } from './function';
1010
import { GeoRestriction } from './geo-restriction';
1111
import { IKeyGroup } from './key-group';
1212
import { IOriginAccessIdentity } from './origin-access-identity';
13+
import { formatDistributionArn } from './private/utils';
1314

1415
/**
1516
* HTTP status code to failover to second origin
@@ -758,6 +759,13 @@ export class CloudFrontWebDistribution extends cdk.Resource implements IDistribu
758759
this.distributionDomainName = attrs.domainName;
759760
this.distributionId = attrs.distributionId;
760761
}
762+
763+
public grant(grantee: iam.IGrantable, ...actions: string[]): iam.Grant {
764+
return iam.Grant.addToPrincipal({ grantee, actions, resourceArns: [formatDistributionArn(this)] });
765+
}
766+
public grantCreateInvalidation(identity: iam.IGrantable): iam.Grant {
767+
return this.grant(identity, 'cloudfront:CreateInvalidation');
768+
}
761769
}();
762770
}
763771

@@ -983,6 +991,26 @@ export class CloudFrontWebDistribution extends cdk.Resource implements IDistribu
983991
this.distributionId = distribution.ref;
984992
}
985993

994+
/**
995+
* Adds an IAM policy statement associated with this distribution to an IAM
996+
* principal's policy.
997+
*
998+
* @param identity The principal
999+
* @param actions The set of actions to allow (i.e. "cloudfront:ListInvalidations")
1000+
*/
1001+
public grant(identity: iam.IGrantable, ...actions: string[]): iam.Grant {
1002+
return iam.Grant.addToPrincipal({ grantee: identity, actions, resourceArns: [formatDistributionArn(this)] });
1003+
}
1004+
1005+
/**
1006+
* Grant to create invalidations for this bucket to an IAM principal (Role/Group/User).
1007+
*
1008+
* @param identity The principal
1009+
*/
1010+
grantCreateInvalidation(identity: iam.IGrantable): iam.Grant {
1011+
return this.grant(identity, 'cloudfront:CreateInvalidation');
1012+
}
1013+
9861014
private toBehavior(input: BehaviorWithOrigin, protoPolicy?: ViewerProtocolPolicy) {
9871015
let toReturn = {
9881016
allowedMethods: this.METHOD_LOOKUP_MAP[input.allowedMethods || CloudFrontAllowedMethods.GET_HEAD],

packages/@aws-cdk/aws-cloudfront/test/distribution.test.ts

+60
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { Match, Template } from '@aws-cdk/assertions';
22
import * as acm from '@aws-cdk/aws-certificatemanager';
3+
import * as iam from '@aws-cdk/aws-iam';
34
import * as lambda from '@aws-cdk/aws-lambda';
45
import * as s3 from '@aws-cdk/aws-s3';
56
import { App, Duration, Stack } from '@aws-cdk/core';
@@ -1079,3 +1080,62 @@ describe('supported HTTP versions', () => {
10791080
});
10801081
});
10811082
});
1083+
1084+
test('grants custom actions', () => {
1085+
const distribution = new Distribution(stack, 'Distribution', {
1086+
defaultBehavior: { origin: defaultOrigin() },
1087+
});
1088+
const role = new iam.Role(stack, 'Role', {
1089+
assumedBy: new iam.AccountRootPrincipal(),
1090+
});
1091+
distribution.grant(role, 'cloudfront:ListInvalidations', 'cloudfront:GetInvalidation');
1092+
1093+
Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', {
1094+
PolicyDocument: {
1095+
Statement: [
1096+
{
1097+
Action: [
1098+
'cloudfront:ListInvalidations',
1099+
'cloudfront:GetInvalidation',
1100+
],
1101+
Resource: {
1102+
'Fn::Join': [
1103+
'', [
1104+
'arn:', { Ref: 'AWS::Partition' }, ':cloudfront::1234:distribution/',
1105+
{ Ref: 'Distribution830FAC52' },
1106+
],
1107+
],
1108+
},
1109+
},
1110+
],
1111+
},
1112+
});
1113+
});
1114+
1115+
test('grants createInvalidation', () => {
1116+
const distribution = new Distribution(stack, 'Distribution', {
1117+
defaultBehavior: { origin: defaultOrigin() },
1118+
});
1119+
const role = new iam.Role(stack, 'Role', {
1120+
assumedBy: new iam.AccountRootPrincipal(),
1121+
});
1122+
distribution.grantCreateInvalidation(role);
1123+
1124+
Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', {
1125+
PolicyDocument: {
1126+
Statement: [
1127+
{
1128+
Action: 'cloudfront:CreateInvalidation',
1129+
Resource: {
1130+
'Fn::Join': [
1131+
'', [
1132+
'arn:', { Ref: 'AWS::Partition' }, ':cloudfront::1234:distribution/',
1133+
{ Ref: 'Distribution830FAC52' },
1134+
],
1135+
],
1136+
},
1137+
},
1138+
],
1139+
},
1140+
});
1141+
});
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
{"version":"20.0.0"}
1+
{"version":"21.0.0"}

packages/@aws-cdk/aws-cloudfront/test/integ.distribution-basic.js.snapshot/integ-distribution-basic.assets.json

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
{
2-
"version": "20.0.0",
2+
"version": "21.0.0",
33
"files": {
4-
"e56a9eb81eb88d748b8062f87488a5f2d9ec1137d330b9d5e0eb33f3ea9de5c7": {
4+
"36c6ded30c4c42464c2753c997c004bf740b9311a744c363fda5f951929a9504": {
55
"source": {
66
"path": "integ-distribution-basic.template.json",
77
"packaging": "file"
88
},
99
"destinations": {
1010
"current_account-current_region": {
1111
"bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}",
12-
"objectKey": "e56a9eb81eb88d748b8062f87488a5f2d9ec1137d330b9d5e0eb33f3ea9de5c7.json",
12+
"objectKey": "36c6ded30c4c42464c2753c997c004bf740b9311a744c363fda5f951929a9504.json",
1313
"assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}"
1414
}
1515
}

0 commit comments

Comments
 (0)