Skip to content

Commit 6033c9a

Browse files
authored
feat(apprunner): make Service implement IGrantable (#26130)
Implementing `IGrantable` for cases when it's needed to grant permissions to a `Service` instance. For example: ``` declare const bucket: IBucket; const service = new apprunner.Service(this, 'Service', { source: apprunner.Source.fromEcrPublic({ imageConfiguration: { port: 8000 }, imageIdentifier: 'public.ecr.aws/aws-containers/hello-app-runner:latest', }), }); bucket.grantRead(service); ``` Closes #26089. ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
1 parent eea223b commit 6033c9a

File tree

7 files changed

+291
-18
lines changed

7 files changed

+291
-18
lines changed

packages/@aws-cdk/aws-apprunner-alpha/README.md

+23-1
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ The `Service` construct allows you to create AWS App Runner services with `ECR P
3535
- `Source.fromAsset()` - To define the source from local asset directory.
3636

3737

38+
The `Service` construct implements `IGrantable`.
39+
3840
## ECR Public
3941

4042
To create a `Service` with ECR Public:
@@ -124,7 +126,27 @@ new apprunner.Service(this, 'Service', {
124126
You are allowed to define `instanceRole` and `accessRole` for the `Service`.
125127

126128
`instanceRole` - The IAM role that provides permissions to your App Runner service. These are permissions that
127-
your code needs when it calls any AWS APIs.
129+
your code needs when it calls any AWS APIs. If not defined, a new instance role will be generated
130+
when required.
131+
132+
To add IAM policy statements to this role, use `addToRolePolicy()`:
133+
134+
```ts
135+
import * as iam from 'aws-cdk-lib/aws-iam';
136+
137+
const service = new apprunner.Service(this, 'Service', {
138+
source: apprunner.Source.fromEcrPublic({
139+
imageConfiguration: { port: 8000 },
140+
imageIdentifier: 'public.ecr.aws/aws-containers/hello-app-runner:latest',
141+
}),
142+
});
143+
144+
service.addToRolePolicy(new iam.PolicyStatement({
145+
effect: iam.Effect.ALLOW,
146+
actions: ['s3:GetObject'],
147+
resources: ['*'],
148+
}))
149+
```
128150

129151
`accessRole` - The IAM role that grants the App Runner service access to a source repository. It's required for
130152
ECR image repositories (but not for ECR Public repositories). If not defined, a new access role will be generated

packages/@aws-cdk/aws-apprunner-alpha/lib/service.ts

+13-7
Original file line numberDiff line numberDiff line change
@@ -689,7 +689,7 @@ export interface ServiceProps {
689689
*
690690
* @see https://docs.aws.amazon.com/apprunner/latest/dg/security_iam_service-with-iam.html#security_iam_service-with-iam-roles-service.instance
691691
*
692-
* @default - no instance role attached.
692+
* @default - generate a new instance role.
693693
*/
694694
readonly instanceRole?: iam.IRole;
695695

@@ -959,7 +959,7 @@ export abstract class Secret {
959959
/**
960960
* The App Runner Service.
961961
*/
962-
export class Service extends cdk.Resource {
962+
export class Service extends cdk.Resource implements iam.IGrantable {
963963
/**
964964
* Import from service name.
965965
*/
@@ -993,9 +993,10 @@ export class Service extends cdk.Resource {
993993

994994
return new Import(scope, id);
995995
}
996+
public readonly grantPrincipal: iam.IPrincipal;
996997
private readonly props: ServiceProps;
997998
private accessRole?: iam.IRole;
998-
private instanceRole?: iam.IRole;
999+
private instanceRole: iam.IRole;
9991000
private source: SourceConfig;
10001001

10011002
/**
@@ -1051,7 +1052,8 @@ export class Service extends cdk.Resource {
10511052
this.source = source;
10521053
this.props = props;
10531054

1054-
this.instanceRole = this.props.instanceRole;
1055+
this.instanceRole = this.props.instanceRole ?? this.createInstanceRole();
1056+
this.grantPrincipal = this.instanceRole;
10551057

10561058
const environmentVariables = this.getEnvironmentVariables();
10571059
const environmentSecrets = this.getEnvironmentSecrets();
@@ -1117,6 +1119,13 @@ export class Service extends cdk.Resource {
11171119
this.serviceName = cdk.Fn.select(1, cdk.Fn.split('/', resourceFullName));
11181120
}
11191121

1122+
/**
1123+
* Adds a statement to the instance role.
1124+
*/
1125+
public addToRolePolicy(statement: iam.PolicyStatement) {
1126+
this.instanceRole.addToPrincipalPolicy(statement);
1127+
}
1128+
11201129
/**
11211130
* This method adds an environment variable to the App Runner service.
11221131
*/
@@ -1134,9 +1143,6 @@ export class Service extends cdk.Resource {
11341143
if (name.startsWith('AWSAPPRUNNER')) {
11351144
throw new Error(`Environment secret key ${name} with a prefix of AWSAPPRUNNER is not allowed`);
11361145
}
1137-
if (!this.instanceRole) {
1138-
this.instanceRole = this.createInstanceRole();
1139-
}
11401146
secret.grantRead(this.instanceRole);
11411147
this.secrets.push({ name: name, value: secret.arn });
11421148
}

packages/@aws-cdk/aws-apprunner-alpha/test/integ.service-ecr-public.js.snapshot/integ-apprunner-ecr-public.template.json

+26-2
Original file line numberDiff line numberDiff line change
@@ -14,15 +14,39 @@
1414
"ImageRepositoryType": "ECR_PUBLIC"
1515
}
1616
},
17-
"InstanceConfiguration": {},
17+
"InstanceConfiguration": {
18+
"InstanceRoleArn": {
19+
"Fn::GetAtt": [
20+
"Service1InstanceRole8CBC81F1",
21+
"Arn"
22+
]
23+
}
24+
},
1825
"NetworkConfiguration": {
1926
"EgressConfiguration": {
2027
"EgressType": "DEFAULT"
2128
}
2229
},
2330
"ServiceName": "service1"
2431
}
25-
}
32+
},
33+
"Service1InstanceRole8CBC81F1": {
34+
"Type": "AWS::IAM::Role",
35+
"Properties": {
36+
"AssumeRolePolicyDocument": {
37+
"Statement": [
38+
{
39+
"Action": "sts:AssumeRole",
40+
"Effect": "Allow",
41+
"Principal": {
42+
"Service": "tasks.apprunner.amazonaws.com"
43+
}
44+
}
45+
],
46+
"Version": "2012-10-17"
47+
}
48+
}
49+
}
2650
},
2751
"Outputs": {
2852
"URL1": {

packages/@aws-cdk/aws-apprunner-alpha/test/integ.service-ecr.js.snapshot/integ-apprunner.template.json

+51-3
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,14 @@
9292
"ImageRepositoryType": "ECR"
9393
}
9494
},
95-
"InstanceConfiguration": {},
95+
"InstanceConfiguration": {
96+
"InstanceRoleArn": {
97+
"Fn::GetAtt": [
98+
"Service3InstanceRoleD40BEE82",
99+
"Arn"
100+
]
101+
}
102+
},
96103
"NetworkConfiguration": {
97104
"EgressConfiguration": {
98105
"EgressType": "DEFAULT"
@@ -211,14 +218,55 @@
211218
"ImageRepositoryType": "ECR"
212219
}
213220
},
214-
"InstanceConfiguration": {},
221+
"InstanceConfiguration": {
222+
"InstanceRoleArn": {
223+
"Fn::GetAtt": [
224+
"Service2InstanceRole3F57F2AA",
225+
"Arn"
226+
]
227+
}
228+
},
215229
"NetworkConfiguration": {
216230
"EgressConfiguration": {
217231
"EgressType": "DEFAULT"
218232
}
219233
}
220234
}
221-
}
235+
},
236+
"Service3InstanceRoleD40BEE82": {
237+
"Type": "AWS::IAM::Role",
238+
"Properties": {
239+
"AssumeRolePolicyDocument": {
240+
"Statement": [
241+
{
242+
"Action": "sts:AssumeRole",
243+
"Effect": "Allow",
244+
"Principal": {
245+
"Service": "tasks.apprunner.amazonaws.com"
246+
}
247+
}
248+
],
249+
"Version": "2012-10-17"
250+
}
251+
}
252+
},
253+
"Service2InstanceRole3F57F2AA": {
254+
"Type": "AWS::IAM::Role",
255+
"Properties": {
256+
"AssumeRolePolicyDocument": {
257+
"Statement": [
258+
{
259+
"Action": "sts:AssumeRole",
260+
"Effect": "Allow",
261+
"Principal": {
262+
"Service": "tasks.apprunner.amazonaws.com"
263+
}
264+
}
265+
],
266+
"Version": "2012-10-17"
267+
}
268+
}
269+
}
222270
},
223271
"Outputs": {
224272
"URL3": {

packages/@aws-cdk/aws-apprunner-alpha/test/integ.service-github.js.snapshot/integ-apprunner.template.json

+50-2
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,14 @@
1818
}
1919
}
2020
},
21-
"InstanceConfiguration": {},
21+
"InstanceConfiguration": {
22+
"InstanceRoleArn": {
23+
"Fn::GetAtt": [
24+
"Service4InstanceRole26B443A0",
25+
"Arn"
26+
]
27+
}
28+
},
2229
"NetworkConfiguration": {
2330
"EgressConfiguration": {
2431
"EgressType": "DEFAULT"
@@ -50,13 +57,54 @@
5057
}
5158
}
5259
},
53-
"InstanceConfiguration": {},
60+
"InstanceConfiguration": {
61+
"InstanceRoleArn": {
62+
"Fn::GetAtt": [
63+
"Service5InstanceRole94C07D84",
64+
"Arn"
65+
]
66+
}
67+
},
5468
"NetworkConfiguration": {
5569
"EgressConfiguration": {
5670
"EgressType": "DEFAULT"
5771
}
5872
}
5973
}
74+
},
75+
"Service4InstanceRole26B443A0": {
76+
"Type": "AWS::IAM::Role",
77+
"Properties": {
78+
"AssumeRolePolicyDocument": {
79+
"Statement": [
80+
{
81+
"Action": "sts:AssumeRole",
82+
"Effect": "Allow",
83+
"Principal": {
84+
"Service": "tasks.apprunner.amazonaws.com"
85+
}
86+
}
87+
],
88+
"Version": "2012-10-17"
89+
}
90+
}
91+
},
92+
"Service5InstanceRole94C07D84": {
93+
"Type": "AWS::IAM::Role",
94+
"Properties": {
95+
"AssumeRolePolicyDocument": {
96+
"Statement": [
97+
{
98+
"Action": "sts:AssumeRole",
99+
"Effect": "Allow",
100+
"Principal": {
101+
"Service": "tasks.apprunner.amazonaws.com"
102+
}
103+
}
104+
],
105+
"Version": "2012-10-17"
106+
}
107+
}
60108
}
61109
},
62110
"Outputs": {

packages/@aws-cdk/aws-apprunner-alpha/test/integ.service-vpc-connector.js.snapshot/integ-apprunner.template.json

+51-3
Original file line numberDiff line numberDiff line change
@@ -442,7 +442,14 @@
442442
"ImageRepositoryType": "ECR_PUBLIC"
443443
}
444444
},
445-
"InstanceConfiguration": {},
445+
"InstanceConfiguration": {
446+
"InstanceRoleArn": {
447+
"Fn::GetAtt": [
448+
"Service6InstanceRole7220D460",
449+
"Arn"
450+
]
451+
}
452+
},
446453
"NetworkConfiguration": {
447454
"EgressConfiguration": {
448455
"EgressType": "VPC",
@@ -469,7 +476,14 @@
469476
"ImageRepositoryType": "ECR_PUBLIC"
470477
}
471478
},
472-
"InstanceConfiguration": {},
479+
"InstanceConfiguration": {
480+
"InstanceRoleArn": {
481+
"Fn::GetAtt": [
482+
"Service7InstanceRoleFD40F312",
483+
"Arn"
484+
]
485+
}
486+
},
473487
"NetworkConfiguration": {
474488
"EgressConfiguration": {
475489
"EgressType": "VPC",
@@ -482,7 +496,41 @@
482496
}
483497
}
484498
}
485-
}
499+
},
500+
"Service6InstanceRole7220D460": {
501+
"Type": "AWS::IAM::Role",
502+
"Properties": {
503+
"AssumeRolePolicyDocument": {
504+
"Statement": [
505+
{
506+
"Action": "sts:AssumeRole",
507+
"Effect": "Allow",
508+
"Principal": {
509+
"Service": "tasks.apprunner.amazonaws.com"
510+
}
511+
}
512+
],
513+
"Version": "2012-10-17"
514+
}
515+
}
516+
},
517+
"Service7InstanceRoleFD40F312": {
518+
"Type": "AWS::IAM::Role",
519+
"Properties": {
520+
"AssumeRolePolicyDocument": {
521+
"Statement": [
522+
{
523+
"Action": "sts:AssumeRole",
524+
"Effect": "Allow",
525+
"Principal": {
526+
"Service": "tasks.apprunner.amazonaws.com"
527+
}
528+
}
529+
],
530+
"Version": "2012-10-17"
531+
}
532+
}
533+
}
486534
},
487535
"Outputs": {
488536
"URL6": {

0 commit comments

Comments
 (0)