@@ -67,6 +67,296 @@ new cloudfront.Distribution(this, 'myDist', {
6767 defaultBehavior: { origin: new origins .HttpOrigin (' www.example.com' ) },
6868});
6969```
70+ ### CloudFront SaaS Manager resources
71+
72+ #### Multi-tenant distribution and tenant providing ACM certificates
73+ You can use Cloudfront to build multi-tenant distributions to house applications.
74+
75+ To create a multi-tenant distribution w/parameters, create a Distribution construct, and then update DistributionConfig in the CfnDistribution to use connectionMode: "tenant-only":
76+ ``` ts
77+ // Create the simple Origin
78+ const myBucket = new s3 .Bucket (this , ' myBucket' );
79+ const s3Origin = origins .S3BucketOrigin .withOriginAccessControl (myBucket , {
80+ originAccessLevels: [cloudfront .AccessLevel .READ , cloudfront .AccessLevel .LIST ],
81+ });
82+
83+ // Create the Distribution construct
84+ const myMultiTenantDistribution = new cloudfront .Distribution (this , ' distribution' , {
85+ defaultBehavior: {
86+ origin: s3Origin ,
87+ },
88+ defaultRootObject: ' index.html' , // recommended to specify
89+ });
90+
91+ // Access the underlying L1 CfnDistribution to configure SaaS Manager properties which are not yet available in the L2 Distribution construct
92+ const cfnDistribution = myMultiTenantDistribution .node .defaultChild as cloudfront .CfnDistribution ;
93+
94+ const defaultCacheBehavior: cloudfront .DefaultCacheBehaviorProperty = {
95+ targetOriginId: myBucket .bucketArn ,
96+ viewerProtocolPolicy: ' allow-all' ,
97+ compress: false ,
98+ allowedMethods: [' GET' , ' HEAD' ],
99+ cachePolicyId: cloudfront .CachePolicy .CACHING_OPTIMIZED .cachePolicyId
100+ };
101+ // Create the updated distributionConfig
102+ const distributionConfig: cloudfront .DistributionConfigProperty = {
103+ defaultCacheBehavior: defaultCacheBehavior ,
104+ enabled: true ,
105+ // the properties below are optional
106+ connectionMode: ' tenant-only' ,
107+ origins: [
108+ {
109+ id: myBucket .bucketArn ,
110+ domainName: myBucket .bucketDomainName ,
111+ s3OriginConfig: {},
112+ originPath: " /{{tenantName}}"
113+ },
114+ ],
115+ tenantConfig: {
116+ parameterDefinitions: [
117+ {
118+ definition: {
119+ stringSchema: {
120+ required: false ,
121+ // the properties below are optional
122+ comment: ' tenantName' ,
123+ defaultValue: ' root' ,
124+ },
125+ },
126+ name: ' tenantName' ,
127+ },
128+ ],
129+ },
130+ };
131+
132+ // Override the distribution configuration to enable multi-tenancy.
133+ cfnDistribution .distributionConfig = distributionConfig ;
134+ ```
135+
136+ Create a distribution tenant using an existing ACM certificate
137+ ``` ts
138+ const cfnDistributionTenant = new cloudfront .CfnDistributionTenant (this , ' distribution-tenant' , {
139+ distributionId: myMultiTenantDistribution .distributionId ,
140+ domains: [' my-tenant.my.domain.com' ],
141+ name: ' my-tenant' ,
142+ enabled: true ,
143+ parameters: [ // Override the default 'tenantName' parameter (root) defined in the multi-tenant distribution.
144+ {
145+ name: ' tenantName' ,
146+ value: ' app' ,
147+ },
148+ ],
149+ customizations: {
150+ certificate: {
151+ arn: ' REPLACE_WITH_ARN' , // Certificate must be in us-east-1 region and cover 'my-tenant.my.domain.com'
152+ },
153+ },
154+ });
155+ ```
156+
157+ #### Multi-tenant distribution and tenant with CloudFront-hosted certificate
158+ A distribution tenant with CloudFront-hosted domain validation is useful if you don't currently have traffic to the domain.
159+
160+ Start by creating a parent multi-tenant distribution
161+ ``` ts
162+ // Create the simple Origin
163+ const myBucket = new s3 .Bucket (this , ' myBucket' );
164+ const s3Origin = origins .S3BucketOrigin .withOriginAccessControl (myBucket , {
165+ originAccessLevels: [cloudfront .AccessLevel .READ , cloudfront .AccessLevel .LIST ],
166+ });
167+
168+ // Create the Distribution construct
169+ const myMultiTenantDistribution = new cloudfront .Distribution (this , ' cf-hosted-distribution' , {
170+ defaultBehavior: {
171+ origin: s3Origin ,
172+ },
173+ defaultRootObject: ' index.html' , // recommended to specify
174+ });
175+
176+ // Access the underlying L1 CfnDistribution to configure SaaS Manager properties which are not yet available in the L2 Distribution construct
177+ const cfnDistribution = myMultiTenantDistribution .node .defaultChild as cloudfront .CfnDistribution ;
178+
179+ const defaultCacheBehavior: cloudfront .DefaultCacheBehaviorProperty = {
180+ targetOriginId: myBucket .bucketArn ,
181+ viewerProtocolPolicy: ' allow-all' ,
182+ compress: false ,
183+ allowedMethods: [' GET' , ' HEAD' ],
184+ cachePolicyId: cloudfront .CachePolicy .CACHING_OPTIMIZED .cachePolicyId
185+ };
186+ // Create the updated distributionConfig
187+ const distributionConfig: cloudfront .DistributionConfigProperty = {
188+ defaultCacheBehavior: defaultCacheBehavior ,
189+ enabled: true ,
190+ // the properties below are optional
191+ connectionMode: ' tenant-only' ,
192+ origins: [
193+ {
194+ id: myBucket .bucketArn ,
195+ domainName: myBucket .bucketDomainName ,
196+ s3OriginConfig: {},
197+ originPath: " /{{tenantName}}"
198+ },
199+ ],
200+ tenantConfig: {
201+ parameterDefinitions: [
202+ {
203+ definition: {
204+ stringSchema: {
205+ required: false ,
206+ // the properties below are optional
207+ comment: ' tenantName' ,
208+ defaultValue: ' root' ,
209+ },
210+ },
211+ name: ' tenantName' ,
212+ },
213+ ],
214+ },
215+ };
216+
217+ // Override the distribution configuration to enable multi-tenancy.
218+ cfnDistribution .distributionConfig = distributionConfig ;
219+ ```
220+
221+ Create a connection group and a cname record in an existing hosted zone to validate domain ownership
222+ ``` ts
223+ const connectionGroup = new cloudfront .CfnConnectionGroup (this , ' cf-hosted-connection-group' , {
224+ enabled: true ,
225+ ipv6Enabled: true ,
226+ name: ' my-connection-group' ,
227+ });
228+
229+ // Import the existing hosted zone info, replacing with your hostedZoneId and zoneName
230+ const hostedZoneId = ' YOUR_HOSTED_ZONE_ID' ;
231+ const zoneName = ' my.domain.com' ;
232+ const hostedZone = HostedZone .fromHostedZoneAttributes (this , ' hosted-zone' , {
233+ hostedZoneId ,
234+ zoneName ,
235+ });
236+
237+ const record = new CnameRecord (this , ' cname-record' , {
238+ domainName: connectionGroup .attrRoutingEndpoint ,
239+ zone: hostedZone ,
240+ recordName: ' cf-hosted-tenant.my.domain.com' ,
241+ });
242+ ```
243+
244+ Create the cloudfront-hosted tenant, passing in the previously created connection group
245+ ``` ts
246+ const cloudfrontHostedTenant = new cloudfront .CfnDistributionTenant (this , ' cf-hosted-tenant' , {
247+ distributionId: myMultiTenantDistribution .distributionId ,
248+ name: ' cf-hosted-tenant' ,
249+ domains: [' cf-hosted-tenant.my.domain.com' ],
250+ connectionGroupId: connectionGroup .attrId ,
251+ enabled: true ,
252+ managedCertificateRequest: {
253+ validationTokenHost: ' cloudfront'
254+ },
255+ });
256+ ```
257+
258+ #### Multi-tenant distribution and tenant with self-hosted certificate
259+ A tenant with self-hosted domain validation is useful if you already have traffic to the domain and can't tolerate downtime during migration to multi-tenant architecture.
260+
261+ The tenant will be created, and the managed certificate will be awaiting validation of domain ownership. You can then validate domain ownership via http redirect or token file upload. [ More details here] ( https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/managed-cloudfront-certificates.html#complete-domain-ownership )
262+
263+ Traffic won't be migrated until you update your hosted zone to point the tenant domain to the CloudFront RoutingEndpoint.
264+
265+ Start by creating a parent multi-tenant distribution
266+ ``` ts
267+ // Create the simple Origin
268+ const myBucket = new s3 .Bucket (this , ' myBucket' );
269+ const s3Origin = origins .S3BucketOrigin .withOriginAccessControl (myBucket , {
270+ originAccessLevels: [cloudfront .AccessLevel .READ , cloudfront .AccessLevel .LIST ],
271+ });
272+
273+ // Create the Distribution construct
274+ const myMultiTenantDistribution = new cloudfront .Distribution (this , ' cf-hosted-distribution' , {
275+ defaultBehavior: {
276+ origin: s3Origin ,
277+ },
278+ defaultRootObject: ' index.html' , // recommended to specify
279+ });
280+
281+ // Access the underlying L1 CfnDistribution to configure SaaS Manager properties which are not yet available in the L2 Distribution construct
282+ const cfnDistribution = myMultiTenantDistribution .node .defaultChild as cloudfront .CfnDistribution ;
283+
284+ const defaultCacheBehavior: cloudfront .DefaultCacheBehaviorProperty = {
285+ targetOriginId: myBucket .bucketArn ,
286+ viewerProtocolPolicy: ' allow-all' ,
287+ compress: false ,
288+ allowedMethods: [' GET' , ' HEAD' ],
289+ cachePolicyId: cloudfront .CachePolicy .CACHING_OPTIMIZED .cachePolicyId
290+ };
291+ // Create the updated distributionConfig
292+ const distributionConfig: cloudfront .DistributionConfigProperty = {
293+ defaultCacheBehavior: defaultCacheBehavior ,
294+ enabled: true ,
295+ // the properties below are optional
296+ connectionMode: ' tenant-only' ,
297+ origins: [
298+ {
299+ id: myBucket .bucketArn ,
300+ domainName: myBucket .bucketDomainName ,
301+ s3OriginConfig: {},
302+ originPath: " /{{tenantName}}"
303+ },
304+ ],
305+ tenantConfig: {
306+ parameterDefinitions: [
307+ {
308+ definition: {
309+ stringSchema: {
310+ required: false ,
311+ // the properties below are optional
312+ comment: ' tenantName' ,
313+ defaultValue: ' root' ,
314+ },
315+ },
316+ name: ' tenantName' ,
317+ },
318+ ],
319+ },
320+ };
321+
322+ // Override the distribution configuration to enable multi-tenancy.
323+ cfnDistribution .distributionConfig = distributionConfig ;
324+ ```
325+
326+ Create a connection group so we have access to the RoutingEndpoint associated with the tenant we are about to create
327+ ``` ts
328+ const connectionGroup = new cloudfront .CfnConnectionGroup (this , ' self-hosted-connection-group' , {
329+ enabled: true ,
330+ ipv6Enabled: true ,
331+ name: ' self-hosted-connection-group' ,
332+ });
333+ ```
334+
335+ Create a distribution tenant with a self-hosted domain.
336+ ``` ts
337+ const selfHostedTenant = new cloudfront .CfnDistributionTenant (this , ' self-hosted-tenant' , {
338+ distributionId: myMultiTenantDistribution .distributionId ,
339+ connectionGroupId: connectionGroup .attrId ,
340+ name: ' self-hosted-tenant' ,
341+ domains: [' self-hosted-tenant.my.domain.com' ],
342+ enabled: true ,
343+ managedCertificateRequest: {
344+ primaryDomainName: ' self-hosted-tenant.my.domain.com' ,
345+ validationTokenHost: ' self-hosted' ,
346+ },
347+ });
348+
349+ // Export the RoutingEndpoint, skip this step if you'd prefer to fetch it from the CloudFront console or via Cloudfront.ListConnectionGroups API
350+ new cdk .CfnOutput (this , ' RoutingEndpoint' , {
351+ value: connectionGroup .attrRoutingEndpoint ,
352+ description: ' CloudFront Routing Endpoint to be added to my hosted zone CNAME records' ,
353+ });
354+ ```
355+ After this self-hosted tenant is created, [ follow the steps here] ( https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/managed-cloudfront-certificates.html#complete-domain-ownership ) to complete domain setup.
356+
357+ Validate domain ownership using the section "I have existing traffic"
358+
359+ Then, when you are ready to accept traffic, follow the steps [ here] ( https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/managed-cloudfront-certificates.html#point-domains-to-cloudfront ) using the RoutingEndpoint from above
70360
71361### VPC origins
72362
0 commit comments