Skip to content

Commit 14204bc

Browse files
committed
feat: permit ApiGateway to skip domain record
1 parent 0956257 commit 14204bc

2 files changed

Lines changed: 86 additions & 31 deletions

File tree

packages/cdkConstructs/src/constructs/RestApiGateway.ts

Lines changed: 48 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import {
2828
ARecord,
2929
AaaaRecord,
3030
HostedZone,
31+
IHostedZone,
3132
RecordTarget
3233
} from "aws-cdk-lib/aws-route53"
3334
import {ApiGateway as ApiGatewayTarget} from "aws-cdk-lib/aws-route53-targets"
@@ -49,6 +50,10 @@ export interface RestApiGatewayProps {
4950
readonly csocApiGatewayDestination: string
5051
/** Managed policies attached to the API Gateway execution role. */
5152
readonly executionPolicies: Array<IManagedPolicy>
53+
/**
54+
* When true (default), creates the custom service domain, ACM certificate, and Route53 records.
55+
*/
56+
readonly enableServiceDomain?: boolean
5257
}
5358

5459
/** Creates a regional REST API with standard logging, DNS, and optional mTLS/CSOC integration. */
@@ -70,13 +75,16 @@ export class RestApiGateway extends Construct {
7075
* forwardCsocLogs: true,
7176
* csocApiGatewayDestination: "arn:aws:logs:eu-west-2:123456789012:destination:csoc",
7277
* executionPolicies: [myLambdaInvokePolicy]
78+
* enableServiceDomain: true
7379
* })
7480
* api.api.root.addResource("patients")
7581
* ```
7682
*/
7783
public constructor(scope: Construct, id: string, props: RestApiGatewayProps) {
7884
super(scope, id)
7985

86+
const enableServiceDomain = (props.enableServiceDomain ?? true)
87+
8088
if (props.forwardCsocLogs && props.csocApiGatewayDestination === "") {
8189
throw new Error("csocApiGatewayDestination must be provided when forwardCsocLogs is true")
8290
}
@@ -100,12 +108,17 @@ export class RestApiGateway extends Construct {
100108
const trustStoreBucketKmsKey = Key.fromKeyArn(
101109
this, "TrustStoreBucketKmsKey", ACCOUNT_RESOURCES.TrustStoreBucketKMSKey)
102110

103-
const epsDomainName: string = ACCOUNT_RESOURCES.EpsDomainName
104-
const hostedZone = HostedZone.fromHostedZoneAttributes(this, "HostedZone", {
105-
hostedZoneId: ACCOUNT_RESOURCES.EpsZoneId,
106-
zoneName: epsDomainName
107-
})
108-
const serviceDomainName = `${props.stackName}.${epsDomainName}`
111+
let hostedZone: IHostedZone | undefined
112+
let serviceDomainName: string | undefined
113+
114+
if (enableServiceDomain) {
115+
const epsDomainName: string = ACCOUNT_RESOURCES.EpsDomainName
116+
hostedZone = HostedZone.fromHostedZoneAttributes(this, "HostedZone", {
117+
hostedZoneId: ACCOUNT_RESOURCES.EpsZoneId,
118+
zoneName: epsDomainName
119+
})
120+
serviceDomainName = `${props.stackName}.${epsDomainName}`
121+
}
109122

110123
// Resources
111124
const logGroup = new LogGroup(this, "ApiGatewayAccessLogGroup", {
@@ -131,14 +144,16 @@ export class RestApiGateway extends Construct {
131144
})
132145
}
133146

134-
const certificate = new Certificate(this, "Certificate", {
135-
domainName: serviceDomainName,
136-
validation: CertificateValidation.fromDns(hostedZone)
137-
})
147+
const certificate = enableServiceDomain && hostedZone && serviceDomainName
148+
? new Certificate(this, "Certificate", {
149+
domainName: serviceDomainName,
150+
validation: CertificateValidation.fromDns(hostedZone)
151+
})
152+
: undefined
138153

139154
let mtlsConfig: MTLSConfig | undefined
140155

141-
if (props.mutualTlsTrustStoreKey) {
156+
if (enableServiceDomain && props.mutualTlsTrustStoreKey) {
142157
const trustStoreKeyPrefix = `cpt-api/${props.stackName}-truststore`
143158
const logGroup = new LogGroup(this, "LambdaLogGroup", {
144159
encryptionKey: cloudWatchLogsKmsKey,
@@ -220,13 +235,16 @@ export class RestApiGateway extends Construct {
220235

221236
const apiGateway = new RestApi(this, "ApiGateway", {
222237
restApiName: `${props.stackName}-apigw`,
223-
domainName: {
224-
domainName: serviceDomainName,
225-
certificate: certificate,
226-
securityPolicy: SecurityPolicy.TLS_1_2,
227-
endpointType: EndpointType.REGIONAL,
228-
mtls: mtlsConfig
229-
},
238+
...(enableServiceDomain
239+
? {
240+
domainName: {
241+
domainName: serviceDomainName!,
242+
certificate: certificate!,
243+
securityPolicy: SecurityPolicy.TLS_1_2,
244+
endpointType: EndpointType.REGIONAL,
245+
mtls: mtlsConfig
246+
}
247+
} : {}),
230248
disableExecuteApiEndpoint: mtlsConfig ? true : false, // NOSONAR
231249
endpointConfiguration: {
232250
types: [EndpointType.REGIONAL]
@@ -245,17 +263,19 @@ export class RestApiGateway extends Construct {
245263
managedPolicies: props.executionPolicies
246264
}).withoutPolicyUpdates()
247265

248-
new ARecord(this, "ARecord", {
249-
recordName: props.stackName,
250-
target: RecordTarget.fromAlias(new ApiGatewayTarget(apiGateway)),
251-
zone: hostedZone
252-
})
266+
if (enableServiceDomain && hostedZone) {
267+
new ARecord(this, "ARecord", {
268+
recordName: props.stackName,
269+
target: RecordTarget.fromAlias(new ApiGatewayTarget(apiGateway)),
270+
zone: hostedZone
271+
})
253272

254-
new AaaaRecord(this, "AaaaRecord", {
255-
recordName: props.stackName,
256-
target: RecordTarget.fromAlias(new ApiGatewayTarget(apiGateway)),
257-
zone: hostedZone
258-
})
273+
new AaaaRecord(this, "AaaaRecord", {
274+
recordName: props.stackName,
275+
target: RecordTarget.fromAlias(new ApiGatewayTarget(apiGateway)),
276+
zone: hostedZone
277+
})
278+
}
259279

260280
const cfnStage = apiGateway.deploymentStage.node.defaultChild as CfnStage
261281
addSuppressions([cfnStage], ["API_GW_CACHE_ENABLED_AND_ENCRYPTED"])

packages/cdkConstructs/tests/constructs/RestApiGateway.test.ts

Lines changed: 38 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,8 @@ describe("RestApiGateway without mTLS", () => {
3535
mutualTlsTrustStoreKey: undefined,
3636
forwardCsocLogs: false,
3737
csocApiGatewayDestination: "",
38-
executionPolicies: [testPolicy]
38+
executionPolicies: [testPolicy],
39+
enableServiceDomain: true
3940
})
4041

4142
// Add a dummy method to satisfy API Gateway validation
@@ -192,7 +193,8 @@ describe("RestApiGateway with CSOC logs", () => {
192193
mutualTlsTrustStoreKey: undefined,
193194
forwardCsocLogs: true,
194195
csocApiGatewayDestination: "arn:aws:logs:eu-west-2:123456789012:destination:csoc-destination",
195-
executionPolicies: [testPolicy]
196+
executionPolicies: [testPolicy],
197+
enableServiceDomain: true
196198
})
197199

198200
// Add a dummy method to satisfy API Gateway validation
@@ -240,7 +242,8 @@ describe("RestApiGateway with mTLS", () => {
240242
mutualTlsTrustStoreKey: "truststore.pem",
241243
forwardCsocLogs: false,
242244
csocApiGatewayDestination: "",
243-
executionPolicies: [testPolicy]
245+
executionPolicies: [testPolicy],
246+
enableServiceDomain: true
244247
})
245248

246249
// Add a dummy method to satisfy API Gateway validation
@@ -352,3 +355,35 @@ describe("RestApiGateway validation errors", () => {
352355
})).toThrow("csocApiGatewayDestination must be provided when forwardCsocLogs is true")
353356
})
354357
})
358+
359+
describe("RestApiGateway enableServiceDomain default behaviour", () => {
360+
test("creates custom domain resources when enableServiceDomain is omitted", () => {
361+
const app = new App()
362+
const stack = new Stack(app, "EnableServiceDomainDefaultStack")
363+
const testPolicy = new ManagedPolicy(stack, "TestPolicy", {
364+
description: "test execution policy",
365+
statements: [
366+
new PolicyStatement({
367+
actions: ["lambda:InvokeFunction"],
368+
resources: ["arn:aws:lambda:eu-west-2:123456789012:function:test-function"]
369+
})
370+
]
371+
})
372+
373+
const apiGateway = new RestApiGateway(stack, "TestApiGateway", {
374+
stackName: "test-stack",
375+
logRetentionInDays: 30,
376+
mutualTlsTrustStoreKey: undefined,
377+
forwardCsocLogs: false,
378+
csocApiGatewayDestination: "",
379+
executionPolicies: [testPolicy]
380+
})
381+
382+
apiGateway.api.root.addMethod("GET")
383+
384+
const template = Template.fromStack(stack)
385+
template.resourceCountIs("AWS::CertificateManager::Certificate", 1)
386+
template.resourceCountIs("AWS::ApiGateway::DomainName", 1)
387+
template.resourceCountIs("AWS::Route53::RecordSet", 2)
388+
})
389+
})

0 commit comments

Comments
 (0)