diff --git a/packages/@aws-cdk/aws-apprunner-alpha/lib/service.ts b/packages/@aws-cdk/aws-apprunner-alpha/lib/service.ts index a93e8bcff5602..b974e568d7c44 100644 --- a/packages/@aws-cdk/aws-apprunner-alpha/lib/service.ts +++ b/packages/@aws-cdk/aws-apprunner-alpha/lib/service.ts @@ -1311,9 +1311,11 @@ export class Service extends cdk.Resource implements iam.IGrantable { } : undefined, }); - // grant required privileges for the role + // grant required privileges for the role to access an image in Amazon ECR + // See https://docs.aws.amazon.com/apprunner/latest/dg/security_iam_service-with-iam.html#security_iam_service-with-iam-roles if (this.source.ecrRepository && this.accessRole) { this.source.ecrRepository.grantPull(this.accessRole); + this.source.ecrRepository.grant(this.accessRole, 'ecr:DescribeImages'); } this.serviceArn = resource.attrServiceArn; diff --git a/packages/@aws-cdk/aws-apprunner-alpha/test/integ.service-ecr.js.snapshot/cdk.out b/packages/@aws-cdk/aws-apprunner-alpha/test/integ.service-ecr.js.snapshot/cdk.out index 7925065efbcc4..1f0068d32659a 100644 --- a/packages/@aws-cdk/aws-apprunner-alpha/test/integ.service-ecr.js.snapshot/cdk.out +++ b/packages/@aws-cdk/aws-apprunner-alpha/test/integ.service-ecr.js.snapshot/cdk.out @@ -1 +1 @@ -{"version":"31.0.0"} \ No newline at end of file +{"version":"36.0.0"} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-apprunner-alpha/test/integ.service-ecr.js.snapshot/integ-apprunner.assets.json b/packages/@aws-cdk/aws-apprunner-alpha/test/integ.service-ecr.js.snapshot/integ-apprunner.assets.json index b3e241bcf0cf4..b1fedc2c8dc21 100644 --- a/packages/@aws-cdk/aws-apprunner-alpha/test/integ.service-ecr.js.snapshot/integ-apprunner.assets.json +++ b/packages/@aws-cdk/aws-apprunner-alpha/test/integ.service-ecr.js.snapshot/integ-apprunner.assets.json @@ -1,7 +1,7 @@ { - "version": "31.0.0", + "version": "36.0.0", "files": { - "3f9deb43f8f02da8be7c2aa727a501250dbddc1fd19e848d488e7071380ce060": { + "95d77364316b51a2e7967588a396f49e865192168822e58a293f02a3ea2abeac": { "source": { "path": "integ-apprunner.template.json", "packaging": "file" @@ -9,7 +9,7 @@ "destinations": { "current_account-current_region": { "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", - "objectKey": "3f9deb43f8f02da8be7c2aa727a501250dbddc1fd19e848d488e7071380ce060.json", + "objectKey": "95d77364316b51a2e7967588a396f49e865192168822e58a293f02a3ea2abeac.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-apprunner-alpha/test/integ.service-ecr.js.snapshot/integ-apprunner.template.json b/packages/@aws-cdk/aws-apprunner-alpha/test/integ.service-ecr.js.snapshot/integ-apprunner.template.json index f3c01fbb4534b..869667f2df4dd 100644 --- a/packages/@aws-cdk/aws-apprunner-alpha/test/integ.service-ecr.js.snapshot/integ-apprunner.template.json +++ b/packages/@aws-cdk/aws-apprunner-alpha/test/integ.service-ecr.js.snapshot/integ-apprunner.template.json @@ -1,5 +1,22 @@ { "Resources": { + "Service3InstanceRoleD40BEE82": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "tasks.apprunner.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + } + } + }, "Service3AccessRole3ACBAAA0": { "Type": "AWS::IAM::Role", "Properties": { @@ -31,6 +48,7 @@ "Action": [ "ecr:BatchCheckLayerAvailability", "ecr:BatchGetImage", + "ecr:DescribeImages", "ecr:GetDownloadUrlForLayer" ], "Effect": "Allow", @@ -72,6 +90,19 @@ "Service342D067F2": { "Type": "AWS::AppRunner::Service", "Properties": { + "InstanceConfiguration": { + "InstanceRoleArn": { + "Fn::GetAtt": [ + "Service3InstanceRoleD40BEE82", + "Arn" + ] + } + }, + "NetworkConfiguration": { + "EgressConfiguration": { + "EgressType": "DEFAULT" + } + }, "SourceConfiguration": { "AuthenticationConfiguration": { "AccessRoleArn": { @@ -91,19 +122,23 @@ }, "ImageRepositoryType": "ECR" } - }, - "InstanceConfiguration": { - "InstanceRoleArn": { - "Fn::GetAtt": [ - "Service3InstanceRoleD40BEE82", - "Arn" - ] + } + } + }, + "Service2InstanceRole3F57F2AA": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "tasks.apprunner.amazonaws.com" + } } - }, - "NetworkConfiguration": { - "EgressConfiguration": { - "EgressType": "DEFAULT" - } + ], + "Version": "2012-10-17" } } }, @@ -138,6 +173,7 @@ "Action": [ "ecr:BatchCheckLayerAvailability", "ecr:BatchGetImage", + "ecr:DescribeImages", "ecr:GetDownloadUrlForLayer" ], "Effect": "Allow", @@ -179,6 +215,19 @@ "Service2AB4D14D8": { "Type": "AWS::AppRunner::Service", "Properties": { + "InstanceConfiguration": { + "InstanceRoleArn": { + "Fn::GetAtt": [ + "Service2InstanceRole3F57F2AA", + "Arn" + ] + } + }, + "NetworkConfiguration": { + "EgressConfiguration": { + "EgressType": "DEFAULT" + } + }, "SourceConfiguration": { "AuthenticationConfiguration": { "AccessRoleArn": { @@ -217,56 +266,9 @@ }, "ImageRepositoryType": "ECR" } - }, - "InstanceConfiguration": { - "InstanceRoleArn": { - "Fn::GetAtt": [ - "Service2InstanceRole3F57F2AA", - "Arn" - ] - } - }, - "NetworkConfiguration": { - "EgressConfiguration": { - "EgressType": "DEFAULT" - } - } - } - }, - "Service3InstanceRoleD40BEE82": { - "Type": "AWS::IAM::Role", - "Properties": { - "AssumeRolePolicyDocument": { - "Statement": [ - { - "Action": "sts:AssumeRole", - "Effect": "Allow", - "Principal": { - "Service": "tasks.apprunner.amazonaws.com" - } - } - ], - "Version": "2012-10-17" - } - } - }, - "Service2InstanceRole3F57F2AA": { - "Type": "AWS::IAM::Role", - "Properties": { - "AssumeRolePolicyDocument": { - "Statement": [ - { - "Action": "sts:AssumeRole", - "Effect": "Allow", - "Principal": { - "Service": "tasks.apprunner.amazonaws.com" - } - } - ], - "Version": "2012-10-17" - } } } + } }, "Outputs": { "URL3": { diff --git a/packages/@aws-cdk/aws-apprunner-alpha/test/integ.service-ecr.js.snapshot/integ.json b/packages/@aws-cdk/aws-apprunner-alpha/test/integ.service-ecr.js.snapshot/integ.json index c68d4b1a9161b..24ddb00cd6868 100644 --- a/packages/@aws-cdk/aws-apprunner-alpha/test/integ.service-ecr.js.snapshot/integ.json +++ b/packages/@aws-cdk/aws-apprunner-alpha/test/integ.service-ecr.js.snapshot/integ.json @@ -1,5 +1,5 @@ { - "version": "31.0.0", + "version": "36.0.0", "testCases": { "integ.service-ecr": { "stacks": [ diff --git a/packages/@aws-cdk/aws-apprunner-alpha/test/integ.service-ecr.js.snapshot/manifest.json b/packages/@aws-cdk/aws-apprunner-alpha/test/integ.service-ecr.js.snapshot/manifest.json index e682f36481528..549a3c46bbb59 100644 --- a/packages/@aws-cdk/aws-apprunner-alpha/test/integ.service-ecr.js.snapshot/manifest.json +++ b/packages/@aws-cdk/aws-apprunner-alpha/test/integ.service-ecr.js.snapshot/manifest.json @@ -1,5 +1,5 @@ { - "version": "31.0.0", + "version": "36.0.0", "artifacts": { "integ-apprunner.assets": { "type": "cdk:asset-manifest", @@ -14,10 +14,11 @@ "environment": "aws://unknown-account/unknown-region", "properties": { "templateFile": "integ-apprunner.template.json", + "terminationProtection": false, "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}/3f9deb43f8f02da8be7c2aa727a501250dbddc1fd19e848d488e7071380ce060.json", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/95d77364316b51a2e7967588a396f49e865192168822e58a293f02a3ea2abeac.json", "requiresBootstrapStackVersion": 6, "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", "additionalDependencies": [ @@ -33,6 +34,12 @@ "integ-apprunner.assets" ], "metadata": { + "/integ-apprunner/Service3/InstanceRole/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "Service3InstanceRoleD40BEE82" + } + ], "/integ-apprunner/Service3/AccessRole/Resource": [ { "type": "aws:cdk:logicalId", @@ -57,6 +64,12 @@ "data": "URL3" } ], + "/integ-apprunner/Service2/InstanceRole/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "Service2InstanceRole3F57F2AA" + } + ], "/integ-apprunner/Service2/AccessRole/Resource": [ { "type": "aws:cdk:logicalId", diff --git a/packages/@aws-cdk/aws-apprunner-alpha/test/integ.service-ecr.js.snapshot/tree.json b/packages/@aws-cdk/aws-apprunner-alpha/test/integ.service-ecr.js.snapshot/tree.json index d9b8bfd8ede9c..efabe12e0f449 100644 --- a/packages/@aws-cdk/aws-apprunner-alpha/test/integ.service-ecr.js.snapshot/tree.json +++ b/packages/@aws-cdk/aws-apprunner-alpha/test/integ.service-ecr.js.snapshot/tree.json @@ -16,7 +16,7 @@ "id": "Staging", "path": "integ-apprunner/ImageAssets/Staging", "constructInfo": { - "fqn": "@aws-cdk/core.AssetStaging", + "fqn": "aws-cdk-lib.AssetStaging", "version": "0.0.0" } }, @@ -24,13 +24,13 @@ "id": "Repository", "path": "integ-apprunner/ImageAssets/Repository", "constructInfo": { - "fqn": "@aws-cdk/aws-ecr.RepositoryBase", + "fqn": "aws-cdk-lib.aws_ecr.RepositoryBase", "version": "0.0.0" } } }, "constructInfo": { - "fqn": "@aws-cdk/aws-ecr-assets.DockerImageAsset", + "fqn": "aws-cdk-lib.aws_ecr_assets.DockerImageAsset", "version": "0.0.0" } }, @@ -38,6 +38,49 @@ "id": "Service3", "path": "integ-apprunner/Service3", "children": { + "InstanceRole": { + "id": "InstanceRole", + "path": "integ-apprunner/Service3/InstanceRole", + "children": { + "ImportInstanceRole": { + "id": "ImportInstanceRole", + "path": "integ-apprunner/Service3/InstanceRole/ImportInstanceRole", + "constructInfo": { + "fqn": "aws-cdk-lib.Resource", + "version": "0.0.0" + } + }, + "Resource": { + "id": "Resource", + "path": "integ-apprunner/Service3/InstanceRole/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::IAM::Role", + "aws:cdk:cloudformation:props": { + "assumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "tasks.apprunner.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_iam.CfnRole", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_iam.Role", + "version": "0.0.0" + } + }, "AccessRole": { "id": "AccessRole", "path": "integ-apprunner/Service3/AccessRole", @@ -46,7 +89,7 @@ "id": "ImportAccessRole", "path": "integ-apprunner/Service3/AccessRole/ImportAccessRole", "constructInfo": { - "fqn": "@aws-cdk/core.Resource", + "fqn": "aws-cdk-lib.Resource", "version": "0.0.0" } }, @@ -71,7 +114,7 @@ } }, "constructInfo": { - "fqn": "@aws-cdk/aws-iam.CfnRole", + "fqn": "aws-cdk-lib.aws_iam.CfnRole", "version": "0.0.0" } }, @@ -96,6 +139,7 @@ "Action": [ "ecr:BatchCheckLayerAvailability", "ecr:BatchGetImage", + "ecr:DescribeImages", "ecr:GetDownloadUrlForLayer" ], "Effect": "Allow", @@ -135,19 +179,19 @@ } }, "constructInfo": { - "fqn": "@aws-cdk/aws-iam.CfnPolicy", + "fqn": "aws-cdk-lib.aws_iam.CfnPolicy", "version": "0.0.0" } } }, "constructInfo": { - "fqn": "@aws-cdk/aws-iam.Policy", + "fqn": "aws-cdk-lib.aws_iam.Policy", "version": "0.0.0" } } }, "constructInfo": { - "fqn": "@aws-cdk/aws-iam.Role", + "fqn": "aws-cdk-lib.aws_iam.Role", "version": "0.0.0" } }, @@ -157,6 +201,19 @@ "attributes": { "aws:cdk:cloudformation:type": "AWS::AppRunner::Service", "aws:cdk:cloudformation:props": { + "instanceConfiguration": { + "instanceRoleArn": { + "Fn::GetAtt": [ + "Service3InstanceRoleD40BEE82", + "Arn" + ] + } + }, + "networkConfiguration": { + "egressConfiguration": { + "egressType": "DEFAULT" + } + }, "sourceConfiguration": { "authenticationConfiguration": { "accessRoleArn": { @@ -176,23 +233,17 @@ }, "imageRepositoryType": "ECR" } - }, - "instanceConfiguration": {}, - "networkConfiguration": { - "egressConfiguration": { - "egressType": "DEFAULT" - } } } }, "constructInfo": { - "fqn": "@aws-cdk/aws-apprunner.CfnService", + "fqn": "aws-cdk-lib.aws_apprunner.CfnService", "version": "0.0.0" } } }, "constructInfo": { - "fqn": "@aws-cdk/aws-apprunner.Service", + "fqn": "aws-cdk-lib.Resource", "version": "0.0.0" } }, @@ -200,7 +251,7 @@ "id": "URL3", "path": "integ-apprunner/URL3", "constructInfo": { - "fqn": "@aws-cdk/core.CfnOutput", + "fqn": "aws-cdk-lib.CfnOutput", "version": "0.0.0" } }, @@ -208,6 +259,49 @@ "id": "Service2", "path": "integ-apprunner/Service2", "children": { + "InstanceRole": { + "id": "InstanceRole", + "path": "integ-apprunner/Service2/InstanceRole", + "children": { + "ImportInstanceRole": { + "id": "ImportInstanceRole", + "path": "integ-apprunner/Service2/InstanceRole/ImportInstanceRole", + "constructInfo": { + "fqn": "aws-cdk-lib.Resource", + "version": "0.0.0" + } + }, + "Resource": { + "id": "Resource", + "path": "integ-apprunner/Service2/InstanceRole/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::IAM::Role", + "aws:cdk:cloudformation:props": { + "assumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "tasks.apprunner.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_iam.CfnRole", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_iam.Role", + "version": "0.0.0" + } + }, "AccessRole": { "id": "AccessRole", "path": "integ-apprunner/Service2/AccessRole", @@ -216,7 +310,7 @@ "id": "ImportAccessRole", "path": "integ-apprunner/Service2/AccessRole/ImportAccessRole", "constructInfo": { - "fqn": "@aws-cdk/core.Resource", + "fqn": "aws-cdk-lib.Resource", "version": "0.0.0" } }, @@ -241,7 +335,7 @@ } }, "constructInfo": { - "fqn": "@aws-cdk/aws-iam.CfnRole", + "fqn": "aws-cdk-lib.aws_iam.CfnRole", "version": "0.0.0" } }, @@ -266,6 +360,7 @@ "Action": [ "ecr:BatchCheckLayerAvailability", "ecr:BatchGetImage", + "ecr:DescribeImages", "ecr:GetDownloadUrlForLayer" ], "Effect": "Allow", @@ -305,19 +400,19 @@ } }, "constructInfo": { - "fqn": "@aws-cdk/aws-iam.CfnPolicy", + "fqn": "aws-cdk-lib.aws_iam.CfnPolicy", "version": "0.0.0" } } }, "constructInfo": { - "fqn": "@aws-cdk/aws-iam.Policy", + "fqn": "aws-cdk-lib.aws_iam.Policy", "version": "0.0.0" } } }, "constructInfo": { - "fqn": "@aws-cdk/aws-iam.Role", + "fqn": "aws-cdk-lib.aws_iam.Role", "version": "0.0.0" } }, @@ -327,6 +422,19 @@ "attributes": { "aws:cdk:cloudformation:type": "AWS::AppRunner::Service", "aws:cdk:cloudformation:props": { + "instanceConfiguration": { + "instanceRoleArn": { + "Fn::GetAtt": [ + "Service2InstanceRole3F57F2AA", + "Arn" + ] + } + }, + "networkConfiguration": { + "egressConfiguration": { + "egressType": "DEFAULT" + } + }, "sourceConfiguration": { "authenticationConfiguration": { "accessRoleArn": { @@ -365,23 +473,17 @@ }, "imageRepositoryType": "ECR" } - }, - "instanceConfiguration": {}, - "networkConfiguration": { - "egressConfiguration": { - "egressType": "DEFAULT" - } } } }, "constructInfo": { - "fqn": "@aws-cdk/aws-apprunner.CfnService", + "fqn": "aws-cdk-lib.aws_apprunner.CfnService", "version": "0.0.0" } } }, "constructInfo": { - "fqn": "@aws-cdk/aws-apprunner.Service", + "fqn": "aws-cdk-lib.Resource", "version": "0.0.0" } }, @@ -389,7 +491,7 @@ "id": "URL2", "path": "integ-apprunner/URL2", "constructInfo": { - "fqn": "@aws-cdk/core.CfnOutput", + "fqn": "aws-cdk-lib.CfnOutput", "version": "0.0.0" } }, @@ -397,7 +499,7 @@ "id": "BootstrapVersion", "path": "integ-apprunner/BootstrapVersion", "constructInfo": { - "fqn": "@aws-cdk/core.CfnParameter", + "fqn": "aws-cdk-lib.CfnParameter", "version": "0.0.0" } }, @@ -405,13 +507,13 @@ "id": "CheckBootstrapVersion", "path": "integ-apprunner/CheckBootstrapVersion", "constructInfo": { - "fqn": "@aws-cdk/core.CfnRule", + "fqn": "aws-cdk-lib.CfnRule", "version": "0.0.0" } } }, "constructInfo": { - "fqn": "@aws-cdk/core.Stack", + "fqn": "aws-cdk-lib.Stack", "version": "0.0.0" } }, @@ -420,12 +522,12 @@ "path": "Tree", "constructInfo": { "fqn": "constructs.Construct", - "version": "10.1.252" + "version": "10.3.0" } } }, "constructInfo": { - "fqn": "@aws-cdk/core.App", + "fqn": "aws-cdk-lib.App", "version": "0.0.0" } } diff --git a/packages/@aws-cdk/aws-apprunner-alpha/test/integ.service-ecr.ts b/packages/@aws-cdk/aws-apprunner-alpha/test/integ.service-ecr.ts index 012132a75e2e0..8a1952b5329e7 100644 --- a/packages/@aws-cdk/aws-apprunner-alpha/test/integ.service-ecr.ts +++ b/packages/@aws-cdk/aws-apprunner-alpha/test/integ.service-ecr.ts @@ -2,6 +2,7 @@ import * as path from 'path'; import * as assets from 'aws-cdk-lib/aws-ecr-assets'; import * as cdk from 'aws-cdk-lib'; import { Service, Source } from '../lib'; +import * as integ from '@aws-cdk/integ-tests-alpha'; const app = new cdk.App(); @@ -29,3 +30,7 @@ const service2 = new Service(stack, 'Service2', { }), }); new cdk.CfnOutput(stack, 'URL2', { value: `https://${service2.serviceUrl}` }); + +new integ.IntegTest(app, 'AppRunnerEcr', { + testCases: [stack], +}); diff --git a/packages/@aws-cdk/aws-apprunner-alpha/test/service.test.ts b/packages/@aws-cdk/aws-apprunner-alpha/test/service.test.ts index 56eb9d9218ed3..ba1b641910fa6 100644 --- a/packages/@aws-cdk/aws-apprunner-alpha/test/service.test.ts +++ b/packages/@aws-cdk/aws-apprunner-alpha/test/service.test.ts @@ -524,6 +524,56 @@ test('create a service from existing ECR repository(image repository type: ECR)' Version: '2012-10-17', }, }); + // we should have a following IAM Policy + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { + PolicyDocument: { + Statement: [ + { + Effect: 'Allow', + Action: 'ecr:GetAuthorizationToken', + Resource: '*', + }, + { + Effect: 'Allow', + Action: [ + 'ecr:BatchCheckLayerAvailability', + 'ecr:GetDownloadUrlForLayer', + 'ecr:BatchGetImage', + ], + Resource: { + 'Fn::Join': ['', [ + 'arn:', + { Ref: 'AWS::Partition' }, + ':ecr:', + { Ref: 'AWS::Region' }, + ':', + { Ref: 'AWS::AccountId' }, + ':repository/nginx', + ]], + }, + }, + { + Effect: 'Allow', + Action: 'ecr:DescribeImages', + Resource: { + 'Fn::Join': ['', [ + 'arn:', + { Ref: 'AWS::Partition' }, + ':ecr:', + { Ref: 'AWS::Region' }, + ':', + { Ref: 'AWS::AccountId' }, + ':repository/nginx', + ]], + }, + }, + ], + }, + PolicyName: 'ServiceAccessRoleDefaultPolicy9C214812', + Roles: [ + { Ref: 'ServiceAccessRole4763579D' }, + ], + }); // we should have the service Template.fromStack(stack).hasResourceProperties('AWS::AppRunner::Service', { SourceConfiguration: {