Skip to content
This repository has been archived by the owner on Jun 13, 2024. It is now read-only.

Commit

Permalink
feat(ecr): support imageScanOnPush when creating the repository
Browse files Browse the repository at this point in the history
Add a new imageScanOnPush property for the new repository creation.

Fix aws#7471
  • Loading branch information
pahud authored May 3, 2020
1 parent 1b8236e commit 9df5486
Show file tree
Hide file tree
Showing 6 changed files with 280 additions and 7 deletions.
18 changes: 18 additions & 0 deletions packages/@aws-cdk/aws-ecr/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,24 @@ holds multiple verions of a single container image.
const repository = new ecr.Repository(this, 'Repository');
```

### Image scanning

Amazon ECR image scanning helps in identifying software vulnerabilities in your container images. You can manually scan container images stored in Amazon ECR, or you can configure your repositories to scan images when you push them to a repository. To create a new reposity to scan on push, simply enable `imageScanOnPush` in the properties

```ts
const repository = new ecr.Repository(stack, 'Repo', {
imageScanOnPush: true
});
```

To create an `onImageScanCompleted` event rule and trigger the event target

```ts
repository.onImageScanCompleted('ImageScanComplete')
.addTarget(...)
```


### Automatically clean up repositories

You can set life cycle rules to automatically clean up old images from your
Expand Down
38 changes: 38 additions & 0 deletions packages/@aws-cdk/aws-ecr/lib/repository.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import * as events from '@aws-cdk/aws-events';
import * as iam from '@aws-cdk/aws-iam';
import { Construct, IConstruct, IResource, Lazy, RemovalPolicy, Resource, Stack, Token } from '@aws-cdk/core';
import * as cr from '@aws-cdk/custom-resources';
import { CfnRepository } from './ecr.generated';
import { LifecycleRule, TagStatus } from './lifecycle';

Expand Down Expand Up @@ -316,6 +317,13 @@ export interface RepositoryProps {
* @default RemovalPolicy.Retain
*/
readonly removalPolicy?: RemovalPolicy;

/**
* Enable the scan on push when creating the repository
*
* @default false
*/
readonly imageScanOnPush?: boolean;
}

export interface RepositoryAttributes {
Expand Down Expand Up @@ -422,6 +430,36 @@ export class Repository extends RepositoryBase {
resource: 'repository',
resourceName: this.physicalName,
});

// image scanOnPush
if (props.imageScanOnPush) {
new cr.AwsCustomResource(this, 'ImageScanOnPush', {
resourceType: 'Custom::ECRImageScanOnPush',
onUpdate: {
service: 'ECR',
action: 'putImageScanningConfiguration',
parameters: {
repositoryName: this.repositoryName,
imageScanningConfiguration: {
scanOnPush: props.imageScanOnPush,
},
},
physicalResourceId: cr.PhysicalResourceId.of(this.repositoryArn),
},
onDelete: {
service: 'ECR',
action: 'putImageScanningConfiguration',
parameters: {
repositoryName: this.repositoryName,
imageScanningConfiguration: {
scanOnPush: false,
},
},
physicalResourceId: cr.PhysicalResourceId.of(this.repositoryArn),
},
policy: cr.AwsCustomResourcePolicy.fromSdkCalls({ resources: [ this.repositoryArn ] }),
});
}
}

public addToResourcePolicy(statement: iam.PolicyStatement) {
Expand Down
8 changes: 5 additions & 3 deletions packages/@aws-cdk/aws-ecr/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -77,13 +77,15 @@
"dependencies": {
"@aws-cdk/aws-events": "0.0.0",
"@aws-cdk/aws-iam": "0.0.0",
"@aws-cdk/custom-resources": "0.0.0",
"@aws-cdk/core": "0.0.0",
"constructs": "^3.0.2"
},
"homepage": "https://github.com/aws/aws-cdk",
"peerDependencies": {
"@aws-cdk/aws-events": "0.0.0",
"@aws-cdk/aws-iam": "0.0.0",
"@aws-cdk/custom-resources": "0.0.0",
"@aws-cdk/core": "0.0.0",
"constructs": "^3.0.2"
},
Expand All @@ -106,8 +108,8 @@
]
},
"stability": "stable",
"maturity": "stable",
"awscdkio": {
"announce": false
},
"maturity": "stable"
}
}
}
204 changes: 202 additions & 2 deletions packages/@aws-cdk/aws-ecr/test/integ.imagescan.expected.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,81 @@
"Resources": {
"Repo02AC86CF": {
"Type": "AWS::ECR::Repository",
"UpdateReplacePolicy": "Retain",
"DeletionPolicy": "Retain"
"UpdateReplacePolicy": "Delete",
"DeletionPolicy": "Delete"
},
"RepoImageScanOnPush94CFD98F": {
"Type": "Custom::ECRImageScanOnPush",
"Properties": {
"ServiceToken": {
"Fn::GetAtt": [
"AWS679f53fac002430cb0da5b7982bd22872D164C4C",
"Arn"
]
},
"Create": {
"service": "ECR",
"action": "putImageScanningConfiguration",
"parameters": {
"repositoryName": {
"Ref": "Repo02AC86CF"
},
"imageScanningConfiguration": {
"scanOnPush": "TRUE:BOOLEAN"
}
},
"physicalResourceId": {
"id": {
"Fn::GetAtt": [
"Repo02AC86CF",
"Arn"
]
}
}
},
"Update": {
"service": "ECR",
"action": "putImageScanningConfiguration",
"parameters": {
"repositoryName": {
"Ref": "Repo02AC86CF"
},
"imageScanningConfiguration": {
"scanOnPush": "TRUE:BOOLEAN"
}
},
"physicalResourceId": {
"id": {
"Fn::GetAtt": [
"Repo02AC86CF",
"Arn"
]
}
}
},
"Delete": {
"service": "ECR",
"action": "putImageScanningConfiguration",
"parameters": {
"repositoryName": {
"Ref": "Repo02AC86CF"
},
"imageScanningConfiguration": {
"scanOnPush": "FALSE:BOOLEAN"
}
},
"physicalResourceId": {
"id": {
"Fn::GetAtt": [
"Repo02AC86CF",
"Arn"
]
}
}
}
},
"UpdateReplacePolicy": "Delete",
"DeletionPolicy": "Delete"
},
"RepoImageScanComplete7BC71935": {
"Type": "AWS::Events::Rule",
Expand All @@ -28,6 +101,133 @@
},
"State": "ENABLED"
}
},
"AWS679f53fac002430cb0da5b7982bd2287ServiceRoleC1EA0FF2": {
"Type": "AWS::IAM::Role",
"Properties": {
"AssumeRolePolicyDocument": {
"Statement": [
{
"Action": "sts:AssumeRole",
"Effect": "Allow",
"Principal": {
"Service": "lambda.amazonaws.com"
}
}
],
"Version": "2012-10-17"
},
"ManagedPolicyArns": [
{
"Fn::Join": [
"",
[
"arn:",
{
"Ref": "AWS::Partition"
},
":iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
]
]
}
]
}
},
"AWS679f53fac002430cb0da5b7982bd2287ServiceRoleDefaultPolicyD28E1A5E": {
"Type": "AWS::IAM::Policy",
"Properties": {
"PolicyDocument": {
"Statement": [
{
"Action": "ecr:PutImageScanningConfiguration",
"Effect": "Allow",
"Resource": {
"Fn::GetAtt": [
"Repo02AC86CF",
"Arn"
]
}
}
],
"Version": "2012-10-17"
},
"PolicyName": "AWS679f53fac002430cb0da5b7982bd2287ServiceRoleDefaultPolicyD28E1A5E",
"Roles": [
{
"Ref": "AWS679f53fac002430cb0da5b7982bd2287ServiceRoleC1EA0FF2"
}
]
}
},
"AWS679f53fac002430cb0da5b7982bd22872D164C4C": {
"Type": "AWS::Lambda::Function",
"Properties": {
"Code": {
"S3Bucket": {
"Ref": "AssetParameters0317970d7c7695dbb9076b70f5eaa0a840dabe9a56c3389439ae5018b5a4cc5bS3BucketA3488101"
},
"S3Key": {
"Fn::Join": [
"",
[
{
"Fn::Select": [
0,
{
"Fn::Split": [
"||",
{
"Ref": "AssetParameters0317970d7c7695dbb9076b70f5eaa0a840dabe9a56c3389439ae5018b5a4cc5bS3VersionKey23A2E46C"
}
]
}
]
},
{
"Fn::Select": [
1,
{
"Fn::Split": [
"||",
{
"Ref": "AssetParameters0317970d7c7695dbb9076b70f5eaa0a840dabe9a56c3389439ae5018b5a4cc5bS3VersionKey23A2E46C"
}
]
}
]
}
]
]
}
},
"Handler": "index.handler",
"Role": {
"Fn::GetAtt": [
"AWS679f53fac002430cb0da5b7982bd2287ServiceRoleC1EA0FF2",
"Arn"
]
},
"Runtime": "nodejs12.x",
"Timeout": 120
},
"DependsOn": [
"AWS679f53fac002430cb0da5b7982bd2287ServiceRoleDefaultPolicyD28E1A5E",
"AWS679f53fac002430cb0da5b7982bd2287ServiceRoleC1EA0FF2"
]
}
},
"Parameters": {
"AssetParameters0317970d7c7695dbb9076b70f5eaa0a840dabe9a56c3389439ae5018b5a4cc5bS3BucketA3488101": {
"Type": "String",
"Description": "S3 bucket for asset \"0317970d7c7695dbb9076b70f5eaa0a840dabe9a56c3389439ae5018b5a4cc5b\""
},
"AssetParameters0317970d7c7695dbb9076b70f5eaa0a840dabe9a56c3389439ae5018b5a4cc5bS3VersionKey23A2E46C": {
"Type": "String",
"Description": "S3 key for asset version \"0317970d7c7695dbb9076b70f5eaa0a840dabe9a56c3389439ae5018b5a4cc5b\""
},
"AssetParameters0317970d7c7695dbb9076b70f5eaa0a840dabe9a56c3389439ae5018b5a4cc5bArtifactHashF6409D44": {
"Type": "String",
"Description": "Artifact hash for asset \"0317970d7c7695dbb9076b70f5eaa0a840dabe9a56c3389439ae5018b5a4cc5b\""
}
},
"Outputs": {
Expand Down
7 changes: 5 additions & 2 deletions packages/@aws-cdk/aws-ecr/test/integ.imagescan.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,13 @@ import * as ecr from '../lib';
const app = new cdk.App();
const stack = new cdk.Stack(app, 'aws-ecr-integ-stack');

const repo = new ecr.Repository(stack, 'Repo');
repo.onImageScanCompleted('ImageScanComplete', {
const repo = new ecr.Repository(stack, 'Repo', {
imageScanOnPush: true,
removalPolicy: cdk.RemovalPolicy.DESTROY,
});

repo.onImageScanCompleted('ImageScanComplete');

new cdk.CfnOutput(stack, 'RepositoryURI', {
value: repo.repositoryUri,
});
Expand Down
12 changes: 12 additions & 0 deletions packages/@aws-cdk/aws-ecr/test/test.repository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,18 @@ export = {
test.done();
},

'repository creation with imageScanOnPush creates custom resource'(test: Test) {
// GIVEN
const stack = new cdk.Stack();

// WHEN
new ecr.Repository(stack, 'Repo', { imageScanOnPush: true });

// THEN
expect(stack).to(haveResource('Custom::ECRImageScanOnPush'));
test.done();
},

'tag-based lifecycle policy'(test: Test) {
// GIVEN
const stack = new cdk.Stack();
Expand Down

0 comments on commit 9df5486

Please sign in to comment.