Skip to content

Commit f0a2266

Browse files
author
Tarun Belani
committed
Addressed review comments
1 parent cf795cd commit f0a2266

File tree

11 files changed

+246
-521
lines changed

11 files changed

+246
-521
lines changed

packages/@aws-cdk/aws-imagebuilder-alpha/lib/image-pipeline.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -476,6 +476,8 @@ export class ImagePipeline extends ImagePipelineBase {
476476
*/
477477
public readonly executionRole?: iam.IRole;
478478

479+
private readonly props: ImagePipelineProps;
480+
479481
public constructor(scope: Construct, id: string, props: ImagePipelineProps) {
480482
super(scope, id, {
481483
physicalName:
@@ -494,6 +496,7 @@ export class ImagePipeline extends ImagePipelineBase {
494496

495497
this.validateImagePipelineName();
496498

499+
this.props = props;
497500
this.infrastructureConfiguration =
498501
props.infrastructureConfiguration ?? new InfrastructureConfiguration(this, 'InfrastructureConfiguration');
499502
this.executionRole = getExecutionRole(
@@ -546,6 +549,23 @@ export class ImagePipeline extends ImagePipelineBase {
546549
});
547550
}
548551

552+
/**
553+
* Grants the default permissions for building an image to the provided execution role.
554+
*
555+
* @param grantee The execution role used for the image build.
556+
*/
557+
public grantDefaultExecutionRolePermissions(grantee: iam.IGrantable): iam.Grant[] {
558+
const policies = defaultExecutionRolePolicy(this, this.props);
559+
return policies.map((policy) =>
560+
iam.Grant.addToPrincipal({
561+
grantee: grantee,
562+
resourceArns: policy.resources,
563+
actions: policy.actions,
564+
scope: this,
565+
}),
566+
);
567+
}
568+
549569
private validateImagePipelineName() {
550570
if (cdk.Token.isUnresolved(this.physicalName)) {
551571
return; // Cannot validate unresolved tokens, given their actual value is rendered at deployment time

packages/@aws-cdk/aws-imagebuilder-alpha/lib/private/policy-helper.ts

Lines changed: 135 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -2,21 +2,26 @@ import * as cdk from 'aws-cdk-lib';
22
import * as iam from 'aws-cdk-lib/aws-iam';
33
import * as logs from 'aws-cdk-lib/aws-logs';
44
import { Construct } from 'constructs';
5+
import { ImagePipelineProps } from '../image-pipeline';
56
import { WorkflowConfiguration } from '../workflow';
67

78
/**
89
* Returns the default execution role policy, for auto-generated execution roles.
910
*
1011
* @param scope The construct scope
12+
* @param props Props input for the construct
1113
*
1214
* @see https://docs.aws.amazon.com/aws-managed-policy/latest/reference/AWSServiceRoleForImageBuilder.html
1315
*/
14-
export const defaultExecutionRolePolicy = (scope: Construct): iam.PolicyStatement[] => {
16+
export const defaultExecutionRolePolicy = <PropsT extends ImagePipelineProps>(
17+
scope: Construct,
18+
props?: PropsT,
19+
): iam.PolicyStatement[] => {
1520
const partition = cdk.Stack.of(scope).partition;
1621

1722
// Permissions are identical to https://docs.aws.amazon.com/aws-managed-policy/latest/reference/AWSServiceRoleForImageBuilder.html
1823
// SLR policies cannot be attached to roles, and no managed policy exists for these permissions yet
19-
return [
24+
const policies = [
2025
new iam.PolicyStatement({
2126
effect: iam.Effect.ALLOW,
2227
actions: ['ec2:RegisterImage'],
@@ -98,7 +103,6 @@ export const defaultExecutionRolePolicy = (scope: Construct): iam.PolicyStatemen
98103
'ec2:DescribeTags',
99104
'ec2:ModifyImageAttribute',
100105
'ec2:DescribeImportImageTasks',
101-
'ec2:DescribeExportImageTasks',
102106
'ec2:DescribeSnapshots',
103107
'ec2:DescribeHosts',
104108
],
@@ -140,11 +144,6 @@ export const defaultExecutionRolePolicy = (scope: Construct): iam.PolicyStatemen
140144
},
141145
},
142146
}),
143-
new iam.PolicyStatement({
144-
effect: iam.Effect.ALLOW,
145-
actions: ['license-manager:UpdateLicenseSpecificationsForResource'],
146-
resources: ['*'],
147-
}),
148147
new iam.PolicyStatement({
149148
effect: iam.Effect.ALLOW,
150149
actions: ['sns:Publish'],
@@ -235,103 +234,18 @@ export const defaultExecutionRolePolicy = (scope: Construct): iam.PolicyStatemen
235234
},
236235
},
237236
}),
238-
new iam.PolicyStatement({
239-
effect: iam.Effect.ALLOW,
240-
actions: ['sts:AssumeRole'],
241-
resources: [`arn:${partition}:iam::*:role/EC2ImageBuilderDistributionCrossAccountRole`],
242-
}),
243237
new iam.PolicyStatement({
244238
effect: iam.Effect.ALLOW,
245239
actions: ['logs:CreateLogStream', 'logs:CreateLogGroup', 'logs:PutLogEvents'],
246240
resources: [`arn:${partition}:logs:*:*:log-group:/aws/imagebuilder/*`],
247241
}),
248-
new iam.PolicyStatement({
249-
effect: iam.Effect.ALLOW,
250-
actions: [
251-
'ec2:CreateLaunchTemplateVersion',
252-
'ec2:DescribeLaunchTemplates',
253-
'ec2:ModifyLaunchTemplate',
254-
'ec2:DescribeLaunchTemplateVersions',
255-
],
256-
resources: ['*'],
257-
}),
258-
new iam.PolicyStatement({
259-
effect: iam.Effect.ALLOW,
260-
actions: ['ec2:ExportImage'],
261-
resources: [`arn:${partition}:ec2:*::image/*`],
262-
conditions: {
263-
StringEquals: {
264-
'ec2:ResourceTag/CreatedBy': 'EC2 Image Builder',
265-
},
266-
},
267-
}),
268-
new iam.PolicyStatement({
269-
effect: iam.Effect.ALLOW,
270-
actions: ['ec2:ExportImage'],
271-
resources: [`arn:${partition}:ec2:*:*:export-image-task/*`],
272-
}),
273-
new iam.PolicyStatement({
274-
effect: iam.Effect.ALLOW,
275-
actions: ['ec2:CancelExportTask'],
276-
resources: [`arn:${partition}:ec2:*:*:export-image-task/*`],
277-
conditions: {
278-
StringEquals: {
279-
'ec2:ResourceTag/CreatedBy': 'EC2 Image Builder',
280-
},
281-
},
282-
}),
283242
new iam.PolicyStatement({
284243
effect: iam.Effect.ALLOW,
285244
actions: ['iam:CreateServiceLinkedRole'],
286245
resources: ['*'],
287246
conditions: {
288247
StringEquals: {
289-
'iam:AWSServiceName': ['ssm.amazonaws.com', 'ec2fastlaunch.amazonaws.com'],
290-
},
291-
},
292-
}),
293-
new iam.PolicyStatement({
294-
effect: iam.Effect.ALLOW,
295-
actions: ['ec2:EnableFastLaunch'],
296-
resources: [`arn:${partition}:ec2:*::image/*`, `arn:${partition}:ec2:*:*:launch-template/*`],
297-
conditions: {
298-
StringEquals: {
299-
'ec2:ResourceTag/CreatedBy': 'EC2 Image Builder',
300-
},
301-
},
302-
}),
303-
new iam.PolicyStatement({
304-
effect: iam.Effect.ALLOW,
305-
actions: ['inspector2:ListCoverage', 'inspector2:ListFindings'],
306-
resources: ['*'],
307-
}),
308-
new iam.PolicyStatement({
309-
effect: iam.Effect.ALLOW,
310-
actions: ['ecr:CreateRepository'],
311-
resources: ['*'],
312-
conditions: {
313-
StringEquals: {
314-
'aws:RequestTag/CreatedBy': 'EC2 Image Builder',
315-
},
316-
},
317-
}),
318-
new iam.PolicyStatement({
319-
effect: iam.Effect.ALLOW,
320-
actions: ['ecr:TagResource'],
321-
resources: [`arn:${partition}:ecr:*:*:repository/image-builder-*`],
322-
conditions: {
323-
StringEquals: {
324-
'aws:RequestTag/CreatedBy': 'EC2 Image Builder',
325-
},
326-
},
327-
}),
328-
new iam.PolicyStatement({
329-
effect: iam.Effect.ALLOW,
330-
actions: ['ecr:BatchDeleteImage'],
331-
resources: [`arn:${partition}:ecr:*:*:repository/image-builder-*`],
332-
conditions: {
333-
StringEquals: {
334-
'ecr:ResourceTag/CreatedBy': 'EC2 Image Builder',
248+
'iam:AWSServiceName': ['ssm.amazonaws.com'],
335249
},
336250
},
337251
}),
@@ -357,6 +271,133 @@ export const defaultExecutionRolePolicy = (scope: Construct): iam.PolicyStatemen
357271
resources: [`arn:${partition}:ssm:*::parameter/aws/service/*`],
358272
}),
359273
];
274+
275+
const hasProps = props !== undefined;
276+
if (!hasProps || (props.recipe._isImageRecipe() && props.distributionConfiguration !== undefined)) {
277+
// Distribution-specific permissions if distribution settings are provided. All permissions here are for AMI builds
278+
// specifically.
279+
policies.push(
280+
new iam.PolicyStatement({
281+
effect: iam.Effect.ALLOW,
282+
actions: ['ec2:DescribeExportImageTasks'],
283+
resources: ['*'],
284+
}),
285+
new iam.PolicyStatement({
286+
effect: iam.Effect.ALLOW,
287+
actions: ['license-manager:UpdateLicenseSpecificationsForResource'],
288+
resources: ['*'],
289+
}),
290+
new iam.PolicyStatement({
291+
effect: iam.Effect.ALLOW,
292+
actions: ['sts:AssumeRole'],
293+
resources: [`arn:${partition}:iam::*:role/EC2ImageBuilderDistributionCrossAccountRole`],
294+
}),
295+
new iam.PolicyStatement({
296+
effect: iam.Effect.ALLOW,
297+
actions: [
298+
'ec2:CreateLaunchTemplateVersion',
299+
'ec2:DescribeLaunchTemplates',
300+
'ec2:ModifyLaunchTemplate',
301+
'ec2:DescribeLaunchTemplateVersions',
302+
],
303+
resources: ['*'],
304+
}),
305+
new iam.PolicyStatement({
306+
effect: iam.Effect.ALLOW,
307+
actions: ['ec2:ExportImage'],
308+
resources: [`arn:${partition}:ec2:*::image/*`],
309+
conditions: {
310+
StringEquals: {
311+
'ec2:ResourceTag/CreatedBy': 'EC2 Image Builder',
312+
},
313+
},
314+
}),
315+
new iam.PolicyStatement({
316+
effect: iam.Effect.ALLOW,
317+
actions: ['ec2:ExportImage'],
318+
resources: [`arn:${partition}:ec2:*:*:export-image-task/*`],
319+
}),
320+
new iam.PolicyStatement({
321+
effect: iam.Effect.ALLOW,
322+
actions: ['ec2:CancelExportTask'],
323+
resources: [`arn:${partition}:ec2:*:*:export-image-task/*`],
324+
conditions: {
325+
StringEquals: {
326+
'ec2:ResourceTag/CreatedBy': 'EC2 Image Builder',
327+
},
328+
},
329+
}),
330+
new iam.PolicyStatement({
331+
effect: iam.Effect.ALLOW,
332+
actions: ['iam:CreateServiceLinkedRole'],
333+
resources: ['*'],
334+
conditions: {
335+
StringEquals: {
336+
'iam:AWSServiceName': ['ec2fastlaunch.amazonaws.com'],
337+
},
338+
},
339+
}),
340+
new iam.PolicyStatement({
341+
effect: iam.Effect.ALLOW,
342+
actions: ['ec2:EnableFastLaunch'],
343+
resources: [`arn:${partition}:ec2:*::image/*`, `arn:${partition}:ec2:*:*:launch-template/*`],
344+
conditions: {
345+
StringEquals: {
346+
'ec2:ResourceTag/CreatedBy': 'EC2 Image Builder',
347+
},
348+
},
349+
}),
350+
);
351+
}
352+
353+
if (!hasProps || props.imageScanningEnabled !== false) {
354+
// Add Inspector permissions if scanning is enabled
355+
policies.push(
356+
new iam.PolicyStatement({
357+
effect: iam.Effect.ALLOW,
358+
actions: ['inspector2:ListCoverage', 'inspector2:ListFindings'],
359+
resources: ['*'],
360+
}),
361+
);
362+
363+
// Add image scanning ECR permissions for container builds
364+
if (!hasProps || props.recipe._isContainerRecipe()) {
365+
policies.push(
366+
new iam.PolicyStatement({
367+
effect: iam.Effect.ALLOW,
368+
actions: ['ecr:CreateRepository'],
369+
resources: ['*'],
370+
conditions: {
371+
StringEquals: {
372+
'aws:RequestTag/CreatedBy': 'EC2 Image Builder',
373+
},
374+
},
375+
}),
376+
new iam.PolicyStatement({
377+
effect: iam.Effect.ALLOW,
378+
actions: ['ecr:TagResource'],
379+
resources: [`arn:${partition}:ecr:*:*:repository/image-builder-*`],
380+
conditions: {
381+
StringEquals: {
382+
'aws:RequestTag/CreatedBy': 'EC2 Image Builder',
383+
},
384+
},
385+
}),
386+
new iam.PolicyStatement({
387+
effect: iam.Effect.ALLOW,
388+
actions: ['ecr:BatchDeleteImage'],
389+
resources: [`arn:${partition}:ecr:*:*:repository/image-builder-*`],
390+
conditions: {
391+
StringEquals: {
392+
'ecr:ResourceTag/CreatedBy': 'EC2 Image Builder',
393+
},
394+
},
395+
}),
396+
);
397+
}
398+
}
399+
400+
return policies;
360401
};
361402

362403
export const getExecutionRole = (

packages/@aws-cdk/aws-imagebuilder-alpha/test/image-pipeline.test.ts

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ describe('ImagePipeline', () => {
8787
});
8888

8989
test('with all parameters - AMI pipeline', () => {
90+
const executionRole = iam.Role.fromRoleName(stack, 'ExecutionRole', 'imagebuilder-execution-role');
9091
const imagePipeline = new ImagePipeline(stack, 'ImagePipeline', {
9192
imagePipelineName: 'test-image-pipeline',
9293
description: 'this is an image pipeline description.',
@@ -102,7 +103,7 @@ describe('ImagePipeline', () => {
102103
'imported-distribution-configuration',
103104
),
104105
status: ImagePipelineStatus.ENABLED,
105-
executionRole: iam.Role.fromRoleName(stack, 'ExecutionRole', 'imagebuilder-execution-role'),
106+
executionRole,
106107
schedule: {
107108
expression: events.Schedule.cron({ minute: '0', hour: '0', weekDay: '0' }),
108109
startCondition: ScheduleStartCondition.EXPRESSION_MATCH_AND_DEPENDENCY_UPDATES_AVAILABLE,
@@ -126,6 +127,7 @@ describe('ImagePipeline', () => {
126127
imageTestsEnabled: false,
127128
imageScanningEnabled: true,
128129
});
130+
imagePipeline.grantDefaultExecutionRolePermissions(executionRole);
129131

130132
expect(ImagePipeline.isImagePipeline(imagePipeline as unknown)).toBeTruthy();
131133
expect(ImagePipeline.isImagePipeline('ImagePipeline')).toBeFalsy();
@@ -206,6 +208,7 @@ describe('ImagePipeline', () => {
206208
});
207209

208210
test('with all parameters - container pipeline', () => {
211+
const executionRole = iam.Role.fromRoleName(stack, 'ExecutionRole', 'imagebuilder-execution-role');
209212
const imagePipeline = new ImagePipeline(stack, 'ImagePipeline', {
210213
imagePipelineName: 'test-image-pipeline',
211214
description: 'this is an image pipeline description.',
@@ -220,7 +223,7 @@ describe('ImagePipeline', () => {
220223
'DistributionConfiguration',
221224
'imported-distribution-configuration',
222225
),
223-
executionRole: iam.Role.fromRoleName(stack, 'ExecutionRole', 'imagebuilder-execution-role'),
226+
executionRole,
224227
status: ImagePipelineStatus.DISABLED,
225228
schedule: {
226229
expression: events.Schedule.rate(cdk.Duration.days(7)),
@@ -242,10 +245,11 @@ describe('ImagePipeline', () => {
242245
),
243246
enhancedImageMetadataEnabled: false,
244247
imageTestsEnabled: true,
245-
imageScanningEnabled: false,
248+
imageScanningEnabled: true,
246249
imageScanningEcrRepository: ecr.Repository.fromRepositoryName(stack, 'Repository', 'scanning-repo'),
247250
imageScanningEcrTags: ['latest-scan'],
248251
});
252+
imagePipeline.grantDefaultExecutionRolePermissions(executionRole);
249253

250254
expect(ImagePipeline.isImagePipeline(imagePipeline as unknown)).toBeTruthy();
251255
expect(ImagePipeline.isImagePipeline('ImagePipeline')).toBeFalsy();
@@ -288,7 +292,7 @@ describe('ImagePipeline', () => {
288292
ContainerTags: ['latest-scan'],
289293
RepositoryName: 'scanning-repo',
290294
},
291-
ImageScanningEnabled: false,
295+
ImageScanningEnabled: true,
292296
},
293297
ImageTestsConfiguration: { ImageTestsEnabled: true },
294298
InfrastructureConfigurationArn: {

0 commit comments

Comments
 (0)