Skip to content

Commit 048e753

Browse files
authored
feat(apprunner): support vpc ingress connection (#30623)
### Issue # (if applicable) Closes #22850. ### Reason for this change To support VPC Ingress Connection for making App Runner Service private and only accessible from within a VPC. ### Description of changes * Add `isPubliclyAccessible` property to the `Service` class * Add `VpcIngressConnection` class ### Description of how you validated changes Add unit tests and integ tests ### Checklist - [x] My code adheres to the [CONTRIBUTING GUIDE](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md) and [DESIGN GUIDELINES](https://github.com/aws/aws-cdk/blob/main/docs/DESIGN_GUIDELINES.md) ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
1 parent 579041e commit 048e753

17 files changed

+2857
-0
lines changed

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

+37
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,43 @@ new apprunner.Service(this, 'Service', {
201201
});
202202
```
203203

204+
## VPC Ingress Connection
205+
206+
To make your App Runner service private and only accessible from within a VPC use the `isPubliclyAccessible` property and associate it to a `VpcIngressConnection` resource.
207+
208+
To set up a `VpcIngressConnection`, specify a VPC, a VPC Interface Endpoint, and the App Runner service.
209+
Also you must set `isPubliclyAccessible` property in ther `Service` to `false`.
210+
211+
For more information, see [Enabling Private endpoint for incoming traffic](https://docs.aws.amazon.com/apprunner/latest/dg/network-pl.html).
212+
213+
```typescript
214+
import * as ec2 from 'aws-cdk-lib/aws-ec2';
215+
216+
declare const vpc: ec2.Vpc;
217+
218+
const interfaceVpcEndpoint = new ec2.InterfaceVpcEndpoint(this, 'MyVpcEndpoint', {
219+
vpc,
220+
service: ec2.InterfaceVpcEndpointAwsService.APP_RUNNER_REQUESTS,
221+
privateDnsEnabled: false,
222+
});
223+
224+
const service = new apprunner.Service(this, 'Service', {
225+
source: apprunner.Source.fromEcrPublic({
226+
imageConfiguration: {
227+
port: 8000,
228+
},
229+
imageIdentifier: 'public.ecr.aws/aws-containers/hello-app-runner:latest',
230+
}),
231+
isPubliclyAccessible: false, // set false
232+
});
233+
234+
new apprunner.VpcIngressConnection(this, 'VpcIngressConnection', {
235+
vpc,
236+
interfaceVpcEndpoint,
237+
service,
238+
});
239+
```
240+
204241
## Dual Stack
205242

206243
To use dual stack (IPv4 and IPv6) for your incoming public network configuration, set `ipAddressType` to `IpAddressType.DUAL_STACK`.

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

+1
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,4 @@ export * from './auto-scaling-configuration';
33
export * from './observability-configuration';
44
export * from './service';
55
export * from './vpc-connector';
6+
export * from './vpc-ingress-connection';

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

+10
Original file line numberDiff line numberDiff line change
@@ -732,6 +732,15 @@ export interface ServiceProps {
732732
*/
733733
readonly vpcConnector?: IVpcConnector;
734734

735+
/**
736+
* Specifies whether your App Runner service is publicly accessible.
737+
*
738+
* If you use `VpcIngressConnection`, you must set this property to `false`.
739+
*
740+
* @default true
741+
*/
742+
readonly isPubliclyAccessible?: boolean;
743+
735744
/**
736745
* Settings for the health check that AWS App Runner performs to monitor the health of a service.
737746
*
@@ -1310,6 +1319,7 @@ export class Service extends cdk.Resource implements iam.IGrantable {
13101319
egressType: this.props.vpcConnector ? 'VPC' : 'DEFAULT',
13111320
vpcConnectorArn: this.props.vpcConnector?.vpcConnectorArn,
13121321
},
1322+
ingressConfiguration: props.isPubliclyAccessible !== undefined ? { isPubliclyAccessible: props.isPubliclyAccessible } : undefined,
13131323
ipAddressType: this.props.ipAddressType,
13141324
},
13151325
healthCheckConfiguration: this.props.healthCheck ?
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
import * as ec2 from 'aws-cdk-lib/aws-ec2';
2+
import * as cdk from 'aws-cdk-lib/core';
3+
import { Construct } from 'constructs';
4+
import { IService } from './service';
5+
import { CfnVpcIngressConnection } from 'aws-cdk-lib/aws-apprunner';
6+
7+
/**
8+
* Properties of the AppRunner VPC Ingress Connection
9+
*/
10+
export interface VpcIngressConnectionProps {
11+
/**
12+
* The name for the VPC Ingress Connection.
13+
*
14+
* @default - a name generated by CloudFormation
15+
*/
16+
readonly vpcIngressConnectionName?: string;
17+
18+
/**
19+
* The service to connect.
20+
*/
21+
readonly service: IService;
22+
23+
/**
24+
* The VPC for the VPC Ingress Connection.
25+
*/
26+
readonly vpc: ec2.IVpc;
27+
28+
/**
29+
* The VPC Interface Endpoint for the VPC Ingress Connection.
30+
*/
31+
readonly interfaceVpcEndpoint: ec2.IInterfaceVpcEndpoint;
32+
}
33+
34+
/**
35+
* Attributes for the App Runner VPC Ingress Connection
36+
*/
37+
export interface VpcIngressConnectionAttributes {
38+
/**
39+
* The Amazon Resource Name (ARN) of the VPC Ingress Connection.
40+
*/
41+
readonly vpcIngressConnectionArn: string;
42+
43+
/**
44+
* The name of the VPC Ingress Connection.
45+
*/
46+
readonly vpcIngressConnectionName: string;
47+
48+
/**
49+
* The domain name associated with the VPC Ingress Connection resource.
50+
*/
51+
readonly domainName: string;
52+
53+
/**
54+
* The current status of the VPC Ingress Connection.
55+
*/
56+
readonly status: string;
57+
}
58+
59+
/**
60+
* Represents the App Runner VPC Ingress Connection.
61+
*/
62+
export interface IVpcIngressConnection extends cdk.IResource {
63+
/**
64+
* The Amazon Resource Name (ARN) of the VPC Ingress Connection.
65+
* @attribute
66+
*/
67+
readonly vpcIngressConnectionArn: string;
68+
69+
/**
70+
* The name of the VPC Ingress Connection.
71+
* @attribute
72+
*/
73+
readonly vpcIngressConnectionName: string;
74+
}
75+
76+
/**
77+
* The App Runner VPC Ingress Connection
78+
*
79+
* @resource AWS::AppRunner::VpcIngressConnection
80+
*/
81+
export class VpcIngressConnection extends cdk.Resource implements IVpcIngressConnection {
82+
/**
83+
* Import from VPC Ingress Connection from attributes.
84+
*/
85+
public static fromVpcIngressConnectionAttributes(scope: Construct, id: string, attrs: VpcIngressConnectionAttributes): IVpcIngressConnection {
86+
const vpcIngressConnectionArn = attrs.vpcIngressConnectionArn;
87+
const domainName = attrs.domainName;
88+
const status = attrs.status;
89+
const vpcIngressConnectionName = attrs.vpcIngressConnectionName;
90+
91+
class Import extends cdk.Resource implements IVpcIngressConnection {
92+
public readonly vpcIngressConnectionArn = vpcIngressConnectionArn;
93+
public readonly domainName = domainName;
94+
public readonly status = status;
95+
public readonly vpcIngressConnectionName = vpcIngressConnectionName;
96+
}
97+
98+
return new Import(scope, id);
99+
}
100+
101+
/**
102+
* Imports an App Runner VPC Ingress Connection from its ARN
103+
*/
104+
public static fromArn(scope: Construct, id: string, vpcIngressConnectionArn: string): IVpcIngressConnection {
105+
const resourceParts = cdk.Fn.split('/', vpcIngressConnectionArn);
106+
107+
const vpcIngressConnectionName = cdk.Fn.select(0, resourceParts);
108+
109+
class Import extends cdk.Resource implements IVpcIngressConnection {
110+
public readonly vpcIngressConnectionName = vpcIngressConnectionName;
111+
public readonly vpcIngressConnectionArn = vpcIngressConnectionArn;
112+
}
113+
114+
return new Import(scope, id);
115+
}
116+
117+
/**
118+
* The ARN of the VPC Ingress Connection.
119+
* @attribute
120+
*/
121+
readonly vpcIngressConnectionArn: string;
122+
123+
/**
124+
* The domain name associated with the VPC Ingress Connection resource.
125+
* @attribute
126+
*/
127+
readonly domainName: string;
128+
129+
/**
130+
* The current status of the VPC Ingress Connection.
131+
* @attribute
132+
*/
133+
readonly status: string;
134+
135+
/**
136+
* The name of the VPC Ingress Connection.
137+
* @attribute
138+
*/
139+
readonly vpcIngressConnectionName: string;
140+
141+
public constructor(scope: Construct, id: string, props: VpcIngressConnectionProps) {
142+
super(scope, id, {
143+
physicalName: props.vpcIngressConnectionName,
144+
});
145+
146+
if (
147+
props.vpcIngressConnectionName !== undefined &&
148+
!cdk.Token.isUnresolved(props.vpcIngressConnectionName) &&
149+
!/^[A-Za-z0-9][A-Za-z0-9\-_]{3,39}$/.test(props.vpcIngressConnectionName)
150+
) {
151+
throw new Error(`vpcIngressConnectionName must match the \`^[A-Za-z0-9][A-Za-z0-9\-_]{3,39}\` pattern, got ${props.vpcIngressConnectionName}`);
152+
}
153+
154+
const resource = new CfnVpcIngressConnection(this, 'Resource', {
155+
ingressVpcConfiguration: {
156+
vpcEndpointId: props.interfaceVpcEndpoint.vpcEndpointId,
157+
vpcId: props.vpc.vpcId,
158+
},
159+
serviceArn: props.service.serviceArn,
160+
vpcIngressConnectionName: this.physicalName,
161+
});
162+
163+
this.vpcIngressConnectionArn = resource.attrVpcIngressConnectionArn;
164+
this.vpcIngressConnectionName = resource.ref;
165+
this.domainName = resource.attrDomainName;
166+
this.status = resource.attrStatus;
167+
}
168+
}

packages/@aws-cdk/aws-apprunner-alpha/test/integ.service-vpc-ingress-connection.js.snapshot/AppRunnerVpcIngressConnectionDefaultTestDeployAssertC725050C.assets.json

+19
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

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

+36
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)