From 8d222390b1b7da7857c6dd825850b9e260663c79 Mon Sep 17 00:00:00 2001 From: Derek Ogi Date: Fri, 27 Jun 2025 20:22:13 +0000 Subject: [PATCH] Adding basic SaaS code samples --- .doc_gen/metadata/cloudfront_metadata.yaml | 50 +++++ javav2/example_code/cloudfront/README.md | 15 +- .../cloudfront/CreateDistributionTenant.java | 188 ++++++++++++++++++ .../CreateMultiTenantDistribution.java | 80 ++++++++ 4 files changed, 332 insertions(+), 1 deletion(-) create mode 100644 javav2/example_code/cloudfront/src/main/java/com/example/cloudfront/CreateDistributionTenant.java create mode 100644 javav2/example_code/cloudfront/src/main/java/com/example/cloudfront/CreateMultiTenantDistribution.java diff --git a/.doc_gen/metadata/cloudfront_metadata.yaml b/.doc_gen/metadata/cloudfront_metadata.yaml index 463ee7cc9b9..7f9cff19aaf 100644 --- a/.doc_gen/metadata/cloudfront_metadata.yaml +++ b/.doc_gen/metadata/cloudfront_metadata.yaml @@ -80,6 +80,56 @@ cloudfront_CreateDistribution: - cloudfront.java2.createdistribution.main services: cloudfront: {CreateDistribution} +cloudfront_CreateSaasResources: + title: Create SaaS manager resources &AWS; SDK + title_abbrev: Create a multi-tenant distribution and distribution tenant + synopsis: create a multi-tenant distribution and distribution tenant with various configurations. + category: Scenarios + languages: + Java: + versions: + - sdk_version: 2 + github: javav2/example_code/cloudfront + sdkguide: + excerpts: + - description: >- + The following example demonstrates how to create a multi-tenant distribution with parameters and wildcard certificate. + snippet_tags: + - cloudfront.java2.createmultitenantdistribution.import + - cloudfront.java2.createmultitenantdistribution.main + - description: >- + The following example demonstrates how to create a distribution tenant associated with that template, including utilizing the parameter we declared above. Note that we don't need to add certificate info here because our domain is already covered by the parent template. + snippet_tags: + - cloudfront.java2.createdistributiontenant.import + - cloudfront.java2.createdistributiontenant.title + - cloudfront.java2.createdistributiontenant.nocert + - cloudfront.java2.createdistributiontenant.closebrace + - description: >- + If the viewer certificate was omitted from the parent template, you would need to add certificate info on the tenant(s) associated with it instead. + The following example demonstrates how to do so via an ACM certificate arn that covers the necessary domain for the tenant. + snippet_tags: + - cloudfront.java2.createdistributiontenant.import + - cloudfront.java2.createdistributiontenant.title + - cloudfront.java2.createdistributiontenant.withcert + - cloudfront.java2.createdistributiontenant.closebrace + - description: >- + The following example demonstrates how to do so with a CloudFront-hosted managed certificate request. This is ideal if you don't already have traffic towards your domain. + In this case, we create a ConnectionGroup to generate a RoutingEndpoint. Then we use that RoutingEndpoint to create DNS records which verify domain ownership and point to CloudFront. CloudFront will then automatically serve a token to validate domain ownership and create a managed certificate. + snippet_tags: + - cloudfront.java2.createdistributiontenant.import + - cloudfront.java2.createdistributiontenant.title + - cloudfront.java2.createdistributiontenant.cfhosted + - cloudfront.java2.createdistributiontenant.closebrace + - description: >- + The following example demonstrates how to do so with a self-hosted managed certificate request. This is ideal if you have traffic towards your domain and can't tolerate downtime during a migration. + At the end of this example, the Tenant will be created in a state awaiting domain validation and DNS setup. Follow steps [here](https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/managed-cloudfront-certificates.html#complete-domain-ownership) to complete setup when you are ready to migrate traffic. + snippet_tags: + - cloudfront.java2.createdistributiontenant.import + - cloudfront.java2.createdistributiontenant.title + - cloudfront.java2.createdistributiontenant.selfhosted + - cloudfront.java2.createdistributiontenant.closebrace + services: + cloudfront: {CreateDistribution, CreateDistributionTenant} cloudfront_CreateKeyGroup: languages: Java: diff --git a/javav2/example_code/cloudfront/README.md b/javav2/example_code/cloudfront/README.md index 17f6b53ce1b..038e877c176 100644 --- a/javav2/example_code/cloudfront/README.md +++ b/javav2/example_code/cloudfront/README.md @@ -45,6 +45,7 @@ Code excerpts that show you how to call individual service functions. Code examples that show you how to accomplish a specific task by calling multiple functions within the same service. +- [Create a multi-tenant distribution and distribution tenant](src/main/java/com/example/cloudfront/CreateMultiTenantDistribution.java) - [Delete signing resources](src/main/java/com/example/cloudfront/DeleteSigningResources.java) - [Sign URLs and cookies](src/main/java/com/example/cloudfront/CreateCannedPolicyRequest.java) @@ -62,6 +63,18 @@ functions within the same service. +#### Create a multi-tenant distribution and distribution tenant + +This example shows you how to Create a multi-tenant distribution and distribution tenant with various configurations. + + + + + + + + + #### Delete signing resources This example shows you how to delete resources that are used to gain access to restricted content in an Amazon Simple Storage Service (Amazon S3) bucket. @@ -112,4 +125,4 @@ in the `javav2` folder. Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -SPDX-License-Identifier: Apache-2.0 \ No newline at end of file +SPDX-License-Identifier: Apache-2.0 diff --git a/javav2/example_code/cloudfront/src/main/java/com/example/cloudfront/CreateDistributionTenant.java b/javav2/example_code/cloudfront/src/main/java/com/example/cloudfront/CreateDistributionTenant.java new file mode 100644 index 00000000000..3a33799eb6b --- /dev/null +++ b/javav2/example_code/cloudfront/src/main/java/com/example/cloudfront/CreateDistributionTenant.java @@ -0,0 +1,188 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package com.example.cloudfront; + +// snippet-start:[cloudfront.java2.createdistributiontenant.import] +import software.amazon.awssdk.core.internal.waiters.ResponseOrException; +import software.amazon.awssdk.services.cloudfront.CloudFrontClient; +import software.amazon.awssdk.services.cloudfront.model.ConnectionMode; +import software.amazon.awssdk.services.cloudfront.model.CreateConnectionGroupResponse; +import software.amazon.awssdk.services.cloudfront.model.CreateDistributionResponse; +import software.amazon.awssdk.services.cloudfront.model.CreateDistributionTenantResponse; +import software.amazon.awssdk.services.cloudfront.model.Distribution; +import software.amazon.awssdk.services.cloudfront.model.DistributionTenant; +import software.amazon.awssdk.services.cloudfront.model.GetConnectionGroupResponse; +import software.amazon.awssdk.services.cloudfront.model.GetDistributionResponse; +import software.amazon.awssdk.services.cloudfront.model.HttpVersion; +import software.amazon.awssdk.services.cloudfront.model.Method; +import software.amazon.awssdk.services.cloudfront.model.SSLSupportMethod; +import software.amazon.awssdk.services.cloudfront.model.ValidationTokenHost; +import software.amazon.awssdk.services.cloudfront.model.ViewerProtocolPolicy; +import software.amazon.awssdk.services.cloudfront.waiters.CloudFrontWaiter; +import software.amazon.awssdk.services.route53.Route53Client; +import software.amazon.awssdk.services.route53.model.RRType; +import software.amazon.awssdk.services.s3.S3Client; + +import java.time.Instant; +// snippet-end:[cloudfront.java2.createdistributiontenant.import] + +// snippet-start:[cloudfront.java2.createdistributiontenant.title] +public class CreateMultiTenantDistribution { +// snippet-end:[cloudfront.java2.createdistributiontenant.title] +// snippet-start:[cloudfront.java2.createdistributiontenant.nocert] + public static DistributionTenant createDistributionTenantNoCert(CloudFrontClient cloudFrontClient, + Route53Client route53Client, + String distributionId, + String domain, + String hostedZoneId) { + CreateDistributionTenantResponse createResponse = cloudFrontClient.createDistributionTenant(builder -> builder + .distributionId(distributionId) + .domains(b1 -> b1 + .domain(domain)) + .parameters(b2 -> b2 + .name("tenantName") + .value("myTenant")) + .enabled(true) + .name("no-cert-tenant") + ); + + final DistributionTenant distributionTenant = createResponse.distributionTenant(); + + // Then update the Route53 hosted zone to point your domain at the distribution tenant + // We fetch the RoutingEndpoint to point to via the default connection group that was created for your tenant + final GetConnectionGroupResponse fetchedConnectionGroup = cloudFrontClient.getConnectionGroup(builder -> builder + .identifier(distributionTenant.connectionGroupId())); + + route53Client.changeResourceRecordSets(builder -> builder + .hostedZoneId(hostedZoneId) + .changeBatch(b1 -> b1 + .comment("ChangeBatch comment") + .changes(b2 -> b2 + .resourceRecordSet(b3 -> b3 + .name(domain) + .type("CNAME") + .ttl(300L) + .resourceRecords(b4 -> b4 + .value(fetchedConnectionGroup.connectionGroup().routingEndpoint()))) + .action("CREATE")) + )); + return distributionTenant; + } +// snippet-end:[cloudfront.java2.createdistributiontenant.nocert] + +// snippet-start:[cloudfront.java2.createdistributiontenant.withcert] + public static DistributionTenant createDistributionTenantWithCert(CloudFrontClient cloudFrontClient, + Route53Client route53Client, + String distributionId, + String domain, + String hostedZoneId, + String certificateArn) { + CreateDistributionTenantResponse createResponse = cloudFrontClient.createDistributionTenant(builder -> builder + .distributionId(distributionId) + .domains(b1 -> b1 + .domain(domain)) + .enabled(true) + .name("tenant-with-cert") + .parameters(b2 -> b2 + .name("tenantName") + .value("myTenant")) + .customizations(b3 -> b3 + .certificate(b4 -> b4 + .arn(certificateArn))) // NOTE: Cert must be in Us-East-1 and cover the domain provided in this request + + ); + + final DistributionTenant distributionTenant = createResponse.distributionTenant(); + + // Then update the Route53 hosted zone to point your domain at the distribution tenant + // We fetch the RoutingEndpoint to point to via the default connection group that was created for your tenant + final GetConnectionGroupResponse fetchedConnectionGroup = cloudFrontClient.getConnectionGroup(builder -> builder + .identifier(distributionTenant.connectionGroupId())); + + route53Client.changeResourceRecordSets(builder -> builder + .hostedZoneId(hostedZoneId) + .changeBatch(b1 -> b1 + .comment("ChangeBatch comment") + .changes(b2 -> b2 + .resourceRecordSet(b3 -> b3 + .name(domain) + .type("CNAME") + .ttl(300L) + .resourceRecords(b4 -> b4 + .value(fetchedConnectionGroup.connectionGroup().routingEndpoint()))) + .action("CREATE")) + )); + return distributionTenant; + } +// snippet-end:[cloudfront.java2.createdistributiontenant.withcert] + +// snippet-start:[cloudfront.java2.createdistributiontenant.cfhosted] + public static DistributionTenant createDistributionTenantCfHosted(CloudFrontClient cloudFrontClient, + Route53Client route53Client, + String distributionId, + String domain, + String hostedZoneId) { + CreateConnectionGroupResponse createConnectionGroupResponse = cloudFrontClient.createConnectionGroup(builder -> builder + .ipv6Enabled(true) + .name("cf-hosted-connection-group") + .enabled(true)); + + route53Client.changeResourceRecordSets(builder -> builder + .hostedZoneId(hostedZoneId) + .changeBatch(b1 -> b1 + .comment("cf-hosted domain validation record") + .changes(b2 -> b2 + .resourceRecordSet(b3 -> b3 + .name(domain) + .type(RRType.CNAME) + .ttl(300L) + .resourceRecords(b4 -> b4 + .value(createConnectionGroupResponse.connectionGroup().routingEndpoint()))) + .action("CREATE")) + )); + + // Give the R53 record time to propagate, if it isn't being returned by servers yet, the following call will fail + sleep(60000); + + CreateDistributionTenantResponse createResponse = cloudFrontClient.createDistributionTenant(builder -> builder + .distributionId(distributionId) + .domains(b1 -> b1 + .domain(domain)) + .enabled(true) + .name("cf-hosted-tenant") + .parameters(b2 -> b2 + .name("tenantName") + .value("myTenant")) + .managedCertificateRequest(b3 -> b3 + .validationTokenHost(ValidationTokenHost.SELF_HOSTED) + ); + + final DistributionTenant distributionTenant = createResponse.distributionTenant(); + } +// snippet-end:[cloudfront.java2.createdistributiontenant.cfhosted] + +// snippet-start:[cloudfront.java2.createdistributiontenant.selfhosted] + public static DistributionTenant createDistributionTenantSelfHosted(CloudFrontClient cloudFrontClient, + Route53Client route53Client, + String distributionId, + String domain, + String hostedZoneId) { + CreateDistributionTenantResponse createResponse = cloudFrontClient.createDistributionTenant(builder -> builder + .distributionId(distributionId) + .domains(b1 -> b1 + .domain(domain)) + .parameters(b2 -> b2 + .name("tenantName") + .value("myTenant")) + .enabled(true) + .name("self-hosted-tenant") + ); + + return createResponse.distributionTenant(); + } +// snippet-end:[cloudfront.java2.createdistributiontenant.selfhosted] + +// snippet-start:[cloudfront.java2.createdistributiontenant.closebrace] +} +// snippet-end:[cloudfront.java2.createdistributiontenant.closebrace] \ No newline at end of file diff --git a/javav2/example_code/cloudfront/src/main/java/com/example/cloudfront/CreateMultiTenantDistribution.java b/javav2/example_code/cloudfront/src/main/java/com/example/cloudfront/CreateMultiTenantDistribution.java new file mode 100644 index 00000000000..aa545f1c528 --- /dev/null +++ b/javav2/example_code/cloudfront/src/main/java/com/example/cloudfront/CreateMultiTenantDistribution.java @@ -0,0 +1,80 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package com.example.cloudfront; + +// snippet-start:[cloudfront.java2.createmultitenantdistribution.import] +import software.amazon.awssdk.core.internal.waiters.ResponseOrException; +import software.amazon.awssdk.services.cloudfront.CloudFrontClient; +import software.amazon.awssdk.services.cloudfront.model.CreateDistributionResponse; +import software.amazon.awssdk.services.cloudfront.model.Distribution; +import software.amazon.awssdk.services.cloudfront.model.GetDistributionResponse; +import software.amazon.awssdk.services.cloudfront.model.ItemSelection; +import software.amazon.awssdk.services.cloudfront.model.Method; +import software.amazon.awssdk.services.cloudfront.model.ViewerProtocolPolicy; +import software.amazon.awssdk.services.cloudfront.waiters.CloudFrontWaiter; +import software.amazon.awssdk.services.s3.S3Client; + +import java.time.Instant; +// snippet-end:[cloudfront.java2.createmultitenantdistribution.import] + +// snippet-start:[cloudfront.java2.createmultitenantdistribution.main] +public class CreateMultiTenantDistribution { + public static Distribution CreateMultiTenantDistribution(CloudFrontClient cloudFrontClient, + S3Client s3Client, + final String bucketName, + final String certificateArn) { + // fetch the origin info if necessary + final String region = s3Client.headBucket(b -> b.bucket(bucketName)).sdkHttpResponse().headers() + .get("x-amz-bucket-region").get(0); + final String originDomain = bucketName + ".s3." + region + ".amazonaws.com"; + String originId = originDomain; // Use the originDomain value for the originId. + + CreateDistributionResponse createDistResponse = cloudFrontClient.createDistribution(builder -> builder + .distributionConfig(b1 -> b1 + .httpVersion(HttpVersion.HTTP2) + .enabled(true) + .comment("Template Distribution with cert built with java") + .connectionMode(ConnectionMode.TENANT_ONLY) + .callerReference(Instant.now().toString()) + .viewerCertificate(certBuilder -> certBuilder + .acmCertificateArn(certificateArn) + .sslSupportMethod(SSLSupportMethod.SNI_ONLY)) + .origins(b2 -> b2 + .quantity(1) + .items(b3 -> b3 + .domainName(originDomain) + .id(originId) + .originPath("/{{tenantName}}") + .s3OriginConfig(builder4 -> builder4 + .originAccessIdentity( + "")))) + .tenantConfig(b5 -> b5 + .parameterDefinitions(b6 -> b6 + .name("tenantName") + .definition(b7 -> b7 + .stringSchema(b8 -> b8 + .comment("tenantName value") + .defaultValue("root") + .required(false))))) + .defaultCacheBehavior(b2 -> b2 + .viewerProtocolPolicy(ViewerProtocolPolicy.ALLOW_ALL) + .targetOriginId(originId) + .cachePolicyId("658327ea-f89d-4fab-a63d-7e88639e58f6") // CachingOptimized Policy + .allowedMethods(b4 -> b4 + .quantity(2) + .items(Method.HEAD, Method.GET))) + )); + + final Distribution distribution = createDistResponse.distribution(); + try (CloudFrontWaiter cfWaiter = CloudFrontWaiter.builder().client(cloudFrontClient).build()) { + ResponseOrException responseOrException = cfWaiter + .waitUntilDistributionDeployed(builder -> builder.id(distribution.id())) + .matched(); + responseOrException.response() + .orElseThrow(() -> new RuntimeException("Distribution not created")); + } + return distribution; + } +} +// snippet-end:[cloudfront.java2.createmultitenantdistribution.main] \ No newline at end of file