sst 2.0.0-rc.64 → 2.0.0-rc.66

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -63,7 +63,7 @@ export const deploy = (program) => program.command("deploy [filter]", "Deploy yo
63
63
  process.exit(1);
64
64
  }
65
65
  const component = render(React.createElement(DeploymentUI, { assembly: assembly }));
66
- const results = await Stacks.deployMany(assembly.stacks);
66
+ const results = await Stacks.deployMany(target);
67
67
  component.clear();
68
68
  component.unmount();
69
69
  printDeploymentResults(assembly, results);
@@ -74,7 +74,7 @@ export const dev = (program) => program.command(["dev", "start"], "Work on your
74
74
  });
75
75
  bus.subscribe("function.build.failed", async (evt) => {
76
76
  const info = useFunctions().fromID(evt.properties.functionID);
77
- if (!info.enableLiveDev)
77
+ if (info.enableLiveDev === false)
78
78
  return;
79
79
  Colors.gap();
80
80
  Colors.line(Colors.danger("✖ "), "Build failed", info.handler);
@@ -153,6 +153,7 @@ export const dev = (program) => program.command(["dev", "start"], "Work on your
153
153
  deploy(assembly);
154
154
  }
155
155
  catch (ex) {
156
+ isWorking = false;
156
157
  spinner.fail();
157
158
  Colors.line(ex.stack
158
159
  .split("\n")
package/cli/sst.js CHANGED
@@ -38,8 +38,10 @@ consoleCommand(program);
38
38
  diff(program);
39
39
  version(program);
40
40
  telemetry(program);
41
- // @ts-expect-error
42
- process.setSourceMapsEnabled(true);
41
+ if ("setSourceMapsEnabled" in process) {
42
+ // @ts-expect-error
43
+ process.setSourceMapsEnabled(true);
44
+ }
43
45
  process.removeAllListeners("uncaughtException");
44
46
  process.on("uncaughtException", (err) => {
45
47
  Logger.debug(err);
package/cli/ui/deploy.js CHANGED
@@ -23,7 +23,7 @@ export const DeploymentUI = (props) => {
23
23
  ? Colors.dim(`${stackNameToId(event.StackName)} ${readable} ${event.ResourceType}`)
24
24
  : Colors.dim(`${stackNameToId(event.StackName)} ${event.ResourceType}`), Stacks.isFailed(event.ResourceStatus)
25
25
  ? Colors.danger(event.ResourceStatus)
26
- : Colors.dim(event.ResourceStatus));
26
+ : Colors.dim(event.ResourceStatus), Stacks.isFailed(event.ResourceStatus) && event.ResourceStatusReason);
27
27
  const { [event.LogicalResourceId]: _, ...next } = previous;
28
28
  return next;
29
29
  }
@@ -491,7 +491,7 @@ export declare class ApiGatewayV1Api<Authorizers extends Record<string, ApiGatew
491
491
  /**
492
492
  * The internally created certificate
493
493
  */
494
- certificate?: acm.Certificate | acm.DnsValidatedCertificate;
494
+ certificate?: acm.ICertificate;
495
495
  };
496
496
  private _deployment?;
497
497
  private _customDomainUrl?;
@@ -9,6 +9,7 @@ import * as apigV1AccessLog from "./util/apiGatewayV1AccessLog.js";
9
9
  import { Stack } from "./Stack.js";
10
10
  import { toCdkDuration } from "./util/duration.js";
11
11
  import { getFunctionRef, isCDKConstruct } from "./Construct.js";
12
+ import { DnsValidatedCertificate } from "./cdk/dns-validated-certificate.js";
12
13
  import { Function as Fn, } from "./Function.js";
13
14
  const allowedMethods = [
14
15
  "ANY",
@@ -447,7 +448,7 @@ export class ApiGatewayV1Api extends Construct {
447
448
  /////////////////////
448
449
  if (!apigDomainName && !certificate) {
449
450
  if (endpointType === "edge") {
450
- certificate = new acm.DnsValidatedCertificate(this, "CrossRegionCertificate", {
451
+ certificate = new DnsValidatedCertificate(this, "CrossRegionCertificate", {
451
452
  domainName: domainName,
452
453
  hostedZone: hostedZone,
453
454
  region: "us-east-1",
@@ -112,6 +112,7 @@ export class Auth extends Construct {
112
112
  [path]: {
113
113
  type: "function",
114
114
  function: this.authenticator,
115
+ authorizer: "none",
115
116
  },
116
117
  });
117
118
  // Auth construct has two types of Function bindinds:
@@ -1,6 +1,6 @@
1
- import * as route53 from "aws-cdk-lib/aws-route53";
2
- import * as cloudfront from "aws-cdk-lib/aws-cloudfront";
3
- import * as acm from "aws-cdk-lib/aws-certificatemanager";
1
+ import { IHostedZone } from "aws-cdk-lib/aws-route53";
2
+ import { ErrorResponse, DistributionProps, BehaviorOptions, IOrigin } from "aws-cdk-lib/aws-cloudfront";
3
+ import { ICertificate } from "aws-cdk-lib/aws-certificatemanager";
4
4
  /**
5
5
  * The customDomain for this website. SST supports domains that are hosted either on [Route 53](https://aws.amazon.com/route53/) or externally.
6
6
  *
@@ -61,13 +61,13 @@ export interface BaseSiteDomainProps {
61
61
  /**
62
62
  * Import the underlying Route 53 hosted zone.
63
63
  */
64
- hostedZone?: route53.IHostedZone;
64
+ hostedZone?: IHostedZone;
65
65
  /**
66
66
  * Import the certificate for the domain. By default, SST will create a certificate with the domain name. The certificate will be created in the `us-east-1`(N. Virginia) region as required by AWS CloudFront.
67
67
  *
68
68
  * Set this option if you have an existing certificate in the `us-east-1` region in AWS Certificate Manager you want to use.
69
69
  */
70
- certificate?: acm.ICertificate;
70
+ certificate?: ICertificate;
71
71
  };
72
72
  }
73
73
  export interface BaseSiteEnvironmentOutputsInfo {
@@ -82,11 +82,11 @@ export interface BaseSiteReplaceProps {
82
82
  search: string;
83
83
  replace: string;
84
84
  }
85
- export declare function buildErrorResponsesForRedirectToIndex(indexPage: string): cloudfront.ErrorResponse[];
86
- export declare function buildErrorResponsesFor404ErrorPage(errorPage: string): cloudfront.ErrorResponse[];
87
- export interface BaseSiteCdkDistributionProps extends Omit<cloudfront.DistributionProps, "defaultBehavior"> {
88
- defaultBehavior?: Omit<cloudfront.BehaviorOptions, "origin"> & {
89
- origin?: cloudfront.IOrigin;
85
+ export declare function buildErrorResponsesForRedirectToIndex(indexPage: string): ErrorResponse[];
86
+ export declare function buildErrorResponsesFor404ErrorPage(errorPage: string): ErrorResponse[];
87
+ export interface BaseSiteCdkDistributionProps extends Omit<DistributionProps, "defaultBehavior"> {
88
+ defaultBehavior?: Omit<BehaviorOptions, "origin"> & {
89
+ origin?: IOrigin;
90
90
  };
91
91
  }
92
92
  export declare function getBuildCmdEnvironment(siteEnvironment?: {
@@ -1,4 +1,4 @@
1
- import * as cdk from "aws-cdk-lib";
1
+ import { Token } from "aws-cdk-lib";
2
2
  export function buildErrorResponsesForRedirectToIndex(indexPage) {
3
3
  return [
4
4
  {
@@ -35,7 +35,7 @@ export function getBuildCmdEnvironment(siteEnvironment) {
35
35
  //
36
36
  const buildCmdEnvironment = {};
37
37
  Object.entries(siteEnvironment || {}).forEach(([key, value]) => {
38
- buildCmdEnvironment[key] = cdk.Token.isUnresolved(value)
38
+ buildCmdEnvironment[key] = Token.isUnresolved(value)
39
39
  ? `{{ ${key} }}`
40
40
  : value;
41
41
  });
@@ -183,7 +183,7 @@ export class EventBus extends Construct {
183
183
  eventBusName: this.cdk.eventBus.eventBusName,
184
184
  rules: Object.entries(this.targetsData).map(([ruleName, rule]) => ({
185
185
  key: ruleName,
186
- targets: Object.values(rule).map(getFunctionRef),
186
+ targets: Object.values(rule).map(getFunctionRef).filter(Boolean),
187
187
  targetNames: Object.keys(rule),
188
188
  })),
189
189
  },
@@ -112,8 +112,7 @@ export class NextjsSite extends SsrSite {
112
112
  allowedMethods: cloudfront.AllowedMethods.ALLOW_ALL,
113
113
  cachedMethods: cloudfront.CachedMethods.CACHE_GET_HEAD_OPTIONS,
114
114
  compress: true,
115
- cachePolicy: cdk?.cachePolicies?.serverRequests ??
116
- this.createCloudFrontServerCachePolicy(),
115
+ cachePolicy: cdk?.serverCachePolicy ?? this.createCloudFrontServerCachePolicy(),
117
116
  edgeLambdas: isMiddlewareEnabled
118
117
  ? [
119
118
  {
@@ -147,18 +147,13 @@ export interface SsrSiteProps {
147
147
  */
148
148
  distribution?: SsrCdkDistributionProps;
149
149
  /**
150
- * Override the default CloudFront cache policies created internally.
150
+ * Override the CloudFront cache policy properties for responses from the
151
+ * server rendering Lambda.
152
+ *
153
+ * @note The default cache policy that is used in the abscene of this property
154
+ * is one that performs no caching of the server response.
151
155
  */
152
- cachePolicies?: {
153
- /**
154
- * Override the CloudFront cache policy properties for responses from the
155
- * server rendering Lambda.
156
- *
157
- * @note The default cache policy that is used in the abscene of this property
158
- * is one that performs no caching of the server response.
159
- */
160
- serverRequests?: ICachePolicy;
161
- };
156
+ serverCachePolicy?: ICachePolicy;
162
157
  server?: Pick<FunctionProps, "vpc" | "vpcSubnets" | "securityGroups" | "allowAllOutbound" | "allowPublicSubnet" | "architecture">;
163
158
  };
164
159
  }
@@ -13,15 +13,15 @@ import { Function, Code, Runtime, FunctionUrlAuthType, } from "aws-cdk-lib/aws-l
13
13
  import { HostedZone, ARecord, AaaaRecord, RecordTarget, } from "aws-cdk-lib/aws-route53";
14
14
  import { Asset } from "aws-cdk-lib/aws-s3-assets";
15
15
  import { Distribution, ViewerProtocolPolicy, AllowedMethods, CachedMethods, LambdaEdgeEventType, CachePolicy, CacheQueryStringBehavior, CacheHeaderBehavior, CacheCookieBehavior, } from "aws-cdk-lib/aws-cloudfront";
16
- import { DnsValidatedCertificate, } from "aws-cdk-lib/aws-certificatemanager";
17
16
  import { AwsCliLayer } from "aws-cdk-lib/lambda-layer-awscli";
18
17
  import { S3Origin, HttpOrigin } from "aws-cdk-lib/aws-cloudfront-origins";
19
18
  import { CloudFrontTarget } from "aws-cdk-lib/aws-route53-targets";
20
- import { HttpsRedirect } from "aws-cdk-lib/aws-route53-patterns";
21
19
  import { Stack } from "./Stack.js";
22
20
  import { Logger } from "../logger.js";
23
21
  import { isCDKConstruct } from "./Construct.js";
24
22
  import { getBuildCmdEnvironment, } from "./BaseSite.js";
23
+ import { HttpsRedirect } from "./cdk/website-redirect.js";
24
+ import { DnsValidatedCertificate } from "./cdk/dns-validated-certificate.js";
25
25
  import { attachPermissionsToRole } from "./util/permission.js";
26
26
  import { ENVIRONMENT_PLACEHOLDER, getParameterPath, } from "./util/functionBinding.js";
27
27
  import { SiteEnv } from "../site-env.js";
@@ -483,8 +483,7 @@ export class SsrSite extends Construct {
483
483
  allowedMethods: AllowedMethods.ALLOW_ALL,
484
484
  cachedMethods: CachedMethods.CACHE_GET_HEAD_OPTIONS,
485
485
  compress: true,
486
- cachePolicy: cdk?.cachePolicies?.serverRequests ??
487
- this.createCloudFrontServerCachePolicy(),
486
+ cachePolicy: cdk?.serverCachePolicy ?? this.createCloudFrontServerCachePolicy(),
488
487
  ...(cfDistributionProps.defaultBehavior || {}),
489
488
  };
490
489
  }
@@ -497,8 +496,7 @@ export class SsrSite extends Construct {
497
496
  allowedMethods: AllowedMethods.ALLOW_ALL,
498
497
  cachedMethods: CachedMethods.CACHE_GET_HEAD_OPTIONS,
499
498
  compress: true,
500
- cachePolicy: cdk?.cachePolicies?.serverRequests ??
501
- this.createCloudFrontServerCachePolicy(),
499
+ cachePolicy: cdk?.serverCachePolicy ?? this.createCloudFrontServerCachePolicy(),
502
500
  ...(cfDistributionProps.defaultBehavior || {}),
503
501
  // concatenate edgeLambdas
504
502
  edgeLambdas: [
@@ -7,17 +7,17 @@ import { Construct } from "constructs";
7
7
  import { Token, Duration, CfnOutput, RemovalPolicy, CustomResource, } from "aws-cdk-lib";
8
8
  import * as s3 from "aws-cdk-lib/aws-s3";
9
9
  import * as s3Assets from "aws-cdk-lib/aws-s3-assets";
10
- import * as acm from "aws-cdk-lib/aws-certificatemanager";
11
10
  import * as iam from "aws-cdk-lib/aws-iam";
12
11
  import * as lambda from "aws-cdk-lib/aws-lambda";
13
12
  import * as route53 from "aws-cdk-lib/aws-route53";
14
- import * as route53Patterns from "aws-cdk-lib/aws-route53-patterns";
15
13
  import * as route53Targets from "aws-cdk-lib/aws-route53-targets";
16
14
  import * as cloudfront from "aws-cdk-lib/aws-cloudfront";
17
15
  import * as cfOrigins from "aws-cdk-lib/aws-cloudfront-origins";
18
16
  import { AwsCliLayer } from "aws-cdk-lib/lambda-layer-awscli";
19
17
  import { Stack } from "./Stack.js";
20
18
  import { getBuildCmdEnvironment, buildErrorResponsesFor404ErrorPage, buildErrorResponsesForRedirectToIndex, } from "./BaseSite.js";
19
+ import { HttpsRedirect } from "./cdk/website-redirect.js";
20
+ import { DnsValidatedCertificate } from "./cdk/dns-validated-certificate.js";
21
21
  import { isCDKConstruct } from "./Construct.js";
22
22
  import { ENVIRONMENT_PLACEHOLDER, getParameterPath, } from "./util/functionBinding.js";
23
23
  import { gray } from "colorette";
@@ -519,7 +519,7 @@ interface ImportMeta {
519
519
  // HostedZone is set for Route 53 domains
520
520
  if (this.hostedZone) {
521
521
  if (typeof customDomain === "string") {
522
- acmCertificate = new acm.DnsValidatedCertificate(this, "Certificate", {
522
+ acmCertificate = new DnsValidatedCertificate(this, "Certificate", {
523
523
  domainName: customDomain,
524
524
  hostedZone: this.hostedZone,
525
525
  region: "us-east-1",
@@ -529,7 +529,7 @@ interface ImportMeta {
529
529
  acmCertificate = customDomain.cdk.certificate;
530
530
  }
531
531
  else {
532
- acmCertificate = new acm.DnsValidatedCertificate(this, "Certificate", {
532
+ acmCertificate = new DnsValidatedCertificate(this, "Certificate", {
533
533
  domainName: customDomain.domainName,
534
534
  hostedZone: this.hostedZone,
535
535
  region: "us-east-1",
@@ -568,7 +568,7 @@ interface ImportMeta {
568
568
  new route53.AaaaRecord(this, "AliasRecordAAAA", recordProps);
569
569
  // Create Alias redirect record
570
570
  if (domainAlias) {
571
- new route53Patterns.HttpsRedirect(this, "Redirect", {
571
+ new HttpsRedirect(this, "Redirect", {
572
572
  zone: this.hostedZone,
573
573
  recordNames: [domainAlias],
574
574
  targetDomain: recordName,
@@ -0,0 +1,18 @@
1
+ import { Metric, MetricOptions } from "aws-cdk-lib/aws-cloudwatch";
2
+ import { Resource } from "aws-cdk-lib";
3
+ import { ICertificate } from "aws-cdk-lib/aws-certificatemanager";
4
+ /**
5
+ * Shared implementation details of ICertificate implementations.
6
+ *
7
+ * @internal
8
+ */
9
+ export declare abstract class CertificateBase extends Resource implements ICertificate {
10
+ abstract readonly certificateArn: string;
11
+ /**
12
+ * If the certificate is provisionned in a different region than the
13
+ * containing stack, this should be the region in which the certificate lives
14
+ * so we can correctly create `Metric` instances.
15
+ */
16
+ protected readonly region?: string;
17
+ metricDaysToExpiry(props?: MetricOptions): Metric;
18
+ }
@@ -0,0 +1,26 @@
1
+ import { Metric, Stats } from "aws-cdk-lib/aws-cloudwatch";
2
+ import { Duration, Resource } from "aws-cdk-lib";
3
+ /**
4
+ * Shared implementation details of ICertificate implementations.
5
+ *
6
+ * @internal
7
+ */
8
+ export class CertificateBase extends Resource {
9
+ /**
10
+ * If the certificate is provisionned in a different region than the
11
+ * containing stack, this should be the region in which the certificate lives
12
+ * so we can correctly create `Metric` instances.
13
+ */
14
+ region;
15
+ metricDaysToExpiry(props) {
16
+ return new Metric({
17
+ period: Duration.days(1),
18
+ ...props,
19
+ dimensionsMap: { CertificateArn: this.certificateArn },
20
+ metricName: "DaysToExpiry",
21
+ namespace: "AWS/CertificateManager",
22
+ region: this.region,
23
+ statistic: Stats.MINIMUM,
24
+ });
25
+ }
26
+ }
@@ -0,0 +1,77 @@
1
+ import { Construct } from "constructs";
2
+ import { ITaggable, TagManager, RemovalPolicy } from "aws-cdk-lib";
3
+ import { CertificateProps, ICertificate } from "aws-cdk-lib/aws-certificatemanager";
4
+ import * as iam from "aws-cdk-lib/aws-iam";
5
+ import * as route53 from "aws-cdk-lib/aws-route53";
6
+ import { CertificateBase } from "./certificate-base.js";
7
+ /**
8
+ * Properties to create a DNS validated certificate managed by AWS Certificate Manager
9
+ *
10
+ */
11
+ export interface DnsValidatedCertificateProps extends CertificateProps {
12
+ /**
13
+ * Route 53 Hosted Zone used to perform DNS validation of the request. The zone
14
+ * must be authoritative for the domain name specified in the Certificate Request.
15
+ */
16
+ readonly hostedZone: route53.IHostedZone;
17
+ /**
18
+ * AWS region that will host the certificate. This is needed especially
19
+ * for certificates used for CloudFront distributions, which require the region
20
+ * to be us-east-1.
21
+ *
22
+ * @default the region the stack is deployed in.
23
+ */
24
+ readonly region?: string;
25
+ /**
26
+ * An endpoint of Route53 service, which is not necessary as AWS SDK could figure
27
+ * out the right endpoints for most regions, but for some regions such as those in
28
+ * aws-cn partition, the default endpoint is not working now, hence the right endpoint
29
+ * need to be specified through this prop.
30
+ *
31
+ * Route53 is not been officially launched in China, it is only available for AWS
32
+ * internal accounts now. To make DnsValidatedCertificate work for internal accounts
33
+ * now, a special endpoint needs to be provided.
34
+ *
35
+ * @default - The AWS SDK will determine the Route53 endpoint to use based on region
36
+ */
37
+ readonly route53Endpoint?: string;
38
+ /**
39
+ * Role to use for the custom resource that creates the validated certificate
40
+ *
41
+ * @default - A new role will be created
42
+ */
43
+ readonly customResourceRole?: iam.IRole;
44
+ /**
45
+ * When set to true, when the DnsValidatedCertificate is deleted,
46
+ * the associated Route53 validation records are removed.
47
+ *
48
+ * CAUTION: If multiple certificates share the same domains (and same validation records),
49
+ * this can cause the other certificates to fail renewal and/or not validate.
50
+ * Not recommended for production use.
51
+ *
52
+ * @default false
53
+ */
54
+ readonly cleanupRoute53Records?: boolean;
55
+ }
56
+ /**
57
+ * A certificate managed by AWS Certificate Manager. Will be automatically
58
+ * validated using DNS validation against the specified Route 53 hosted zone.
59
+ *
60
+ * @resource AWS::CertificateManager::Certificate
61
+ */
62
+ export declare class DnsValidatedCertificate extends CertificateBase implements ICertificate, ITaggable {
63
+ readonly certificateArn: string;
64
+ /**
65
+ * Resource Tags.
66
+ * @see https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-certificatemanager-certificate.html#cfn-certificatemanager-certificate-tags
67
+ */
68
+ readonly tags: TagManager;
69
+ protected readonly region?: string;
70
+ private normalizedZoneName;
71
+ private hostedZoneId;
72
+ private domainName;
73
+ private _removalPolicy?;
74
+ constructor(scope: Construct, id: string, props: DnsValidatedCertificateProps);
75
+ applyRemovalPolicy(policy: RemovalPolicy): void;
76
+ private validateDnsValidatedCertificate;
77
+ }
@@ -0,0 +1,125 @@
1
+ import url from "url";
2
+ import * as path from "path";
3
+ import { Token, TagManager, TagType, Duration, Stack, CustomResource, Lazy, } from "aws-cdk-lib";
4
+ import * as iam from "aws-cdk-lib/aws-iam";
5
+ import * as lambda from "aws-cdk-lib/aws-lambda";
6
+ import { CertificateBase } from "./certificate-base.js";
7
+ const __dirname = url.fileURLToPath(new URL(".", import.meta.url));
8
+ /**
9
+ * A certificate managed by AWS Certificate Manager. Will be automatically
10
+ * validated using DNS validation against the specified Route 53 hosted zone.
11
+ *
12
+ * @resource AWS::CertificateManager::Certificate
13
+ */
14
+ export class DnsValidatedCertificate extends CertificateBase {
15
+ certificateArn;
16
+ /**
17
+ * Resource Tags.
18
+ * @see https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-certificatemanager-certificate.html#cfn-certificatemanager-certificate-tags
19
+ */
20
+ tags;
21
+ region;
22
+ normalizedZoneName;
23
+ hostedZoneId;
24
+ domainName;
25
+ _removalPolicy;
26
+ constructor(scope, id, props) {
27
+ super(scope, id);
28
+ this.region = props.region;
29
+ this.domainName = props.domainName;
30
+ // check if domain name is 64 characters or less
31
+ if (!Token.isUnresolved(props.domainName) && props.domainName.length > 64) {
32
+ throw new Error("Domain name must be 64 characters or less");
33
+ }
34
+ this.normalizedZoneName = props.hostedZone.zoneName;
35
+ // Remove trailing `.` from zone name
36
+ if (this.normalizedZoneName.endsWith(".")) {
37
+ this.normalizedZoneName = this.normalizedZoneName.substring(0, this.normalizedZoneName.length - 1);
38
+ }
39
+ // Remove any `/hostedzone/` prefix from the Hosted Zone ID
40
+ this.hostedZoneId = props.hostedZone.hostedZoneId.replace(/^\/hostedzone\//, "");
41
+ this.tags = new TagManager(TagType.MAP, "AWS::CertificateManager::Certificate");
42
+ let certificateTransparencyLoggingPreference;
43
+ if (props.transparencyLoggingEnabled !== undefined) {
44
+ certificateTransparencyLoggingPreference =
45
+ props.transparencyLoggingEnabled ? "ENABLED" : "DISABLED";
46
+ }
47
+ const requestorFunction = new lambda.Function(this, "CertificateRequestorFunction", {
48
+ code: lambda.Code.fromAsset(path.join(__dirname, "../../support/certificate-requestor")),
49
+ handler: "index.certificateRequestHandler",
50
+ runtime: lambda.Runtime.NODEJS_14_X,
51
+ timeout: Duration.minutes(15),
52
+ role: props.customResourceRole,
53
+ });
54
+ requestorFunction.addToRolePolicy(new iam.PolicyStatement({
55
+ actions: [
56
+ "acm:RequestCertificate",
57
+ "acm:DescribeCertificate",
58
+ "acm:DeleteCertificate",
59
+ "acm:AddTagsToCertificate",
60
+ ],
61
+ resources: ["*"],
62
+ }));
63
+ requestorFunction.addToRolePolicy(new iam.PolicyStatement({
64
+ actions: ["route53:GetChange"],
65
+ resources: ["*"],
66
+ }));
67
+ requestorFunction.addToRolePolicy(new iam.PolicyStatement({
68
+ actions: ["route53:changeResourceRecordSets"],
69
+ resources: [
70
+ `arn:${Stack.of(requestorFunction).partition}:route53:::hostedzone/${this.hostedZoneId}`,
71
+ ],
72
+ conditions: {
73
+ "ForAllValues:StringEquals": {
74
+ "route53:ChangeResourceRecordSetsRecordTypes": ["CNAME"],
75
+ "route53:ChangeResourceRecordSetsActions": props.cleanupRoute53Records ? ["UPSERT", "DELETE"] : ["UPSERT"],
76
+ },
77
+ "ForAllValues:StringLike": {
78
+ "route53:ChangeResourceRecordSetsNormalizedRecordNames": [
79
+ addWildcard(props.domainName),
80
+ ...(props.subjectAlternativeNames ?? []).map((d) => addWildcard(d)),
81
+ ],
82
+ },
83
+ },
84
+ }));
85
+ const certificate = new CustomResource(this, "CertificateRequestorResource", {
86
+ serviceToken: requestorFunction.functionArn,
87
+ properties: {
88
+ DomainName: props.domainName,
89
+ SubjectAlternativeNames: Lazy.list({ produce: () => props.subjectAlternativeNames }, { omitEmpty: true }),
90
+ CertificateTransparencyLoggingPreference: certificateTransparencyLoggingPreference,
91
+ HostedZoneId: this.hostedZoneId,
92
+ Region: props.region,
93
+ Route53Endpoint: props.route53Endpoint,
94
+ RemovalPolicy: Lazy.any({ produce: () => this._removalPolicy }),
95
+ // Custom resources properties are always converted to strings; might as well be explict here.
96
+ CleanupRecords: props.cleanupRoute53Records ? "true" : undefined,
97
+ Tags: Lazy.list({ produce: () => this.tags.renderTags() }),
98
+ },
99
+ });
100
+ this.certificateArn = certificate.getAtt("Arn").toString();
101
+ this.node.addValidation({
102
+ validate: () => this.validateDnsValidatedCertificate(),
103
+ });
104
+ }
105
+ applyRemovalPolicy(policy) {
106
+ this._removalPolicy = policy;
107
+ }
108
+ validateDnsValidatedCertificate() {
109
+ const errors = [];
110
+ // Ensure the zone name is a parent zone of the certificate domain name
111
+ if (!Token.isUnresolved(this.normalizedZoneName) &&
112
+ this.domainName !== this.normalizedZoneName &&
113
+ !this.domainName.endsWith("." + this.normalizedZoneName)) {
114
+ errors.push(`DNS zone ${this.normalizedZoneName} is not authoritative for certificate domain name ${this.domainName}`);
115
+ }
116
+ return errors;
117
+ }
118
+ }
119
+ // https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/specifying-rrset-conditions.html
120
+ function addWildcard(domainName) {
121
+ if (domainName.startsWith("*.")) {
122
+ return domainName;
123
+ }
124
+ return `*.${domainName}`;
125
+ }
@@ -0,0 +1,53 @@
1
+ import { ICertificate } from "aws-cdk-lib/aws-certificatemanager";
2
+ import { IHostedZone } from "aws-cdk-lib/aws-route53";
3
+ import { Construct } from "constructs";
4
+ /**
5
+ * Properties to configure an HTTPS Redirect
6
+ */
7
+ export interface HttpsRedirectProps {
8
+ /**
9
+ * Hosted zone of the domain which will be used to create alias record(s) from
10
+ * domain names in the hosted zone to the target domain. The hosted zone must
11
+ * contain entries for the domain name(s) supplied through `recordNames` that
12
+ * will redirect to the target domain.
13
+ *
14
+ * Domain names in the hosted zone can include a specific domain (example.com)
15
+ * and its subdomains (acme.example.com, zenith.example.com).
16
+ *
17
+ */
18
+ readonly zone: IHostedZone;
19
+ /**
20
+ * The redirect target fully qualified domain name (FQDN). An alias record
21
+ * will be created that points to your CloudFront distribution. Root domain
22
+ * or sub-domain can be supplied.
23
+ */
24
+ readonly targetDomain: string;
25
+ /**
26
+ * The domain names that will redirect to `targetDomain`
27
+ *
28
+ * @default - the domain name of the hosted zone
29
+ */
30
+ readonly recordNames?: string[];
31
+ /**
32
+ * The AWS Certificate Manager (ACM) certificate that will be associated with
33
+ * the CloudFront distribution that will be created. If provided, the certificate must be
34
+ * stored in us-east-1 (N. Virginia)
35
+ *
36
+ * @default - A new certificate is created in us-east-1 (N. Virginia)
37
+ */
38
+ readonly certificate?: ICertificate;
39
+ }
40
+ /**
41
+ * Allows creating a domainA -> domainB redirect using CloudFront and S3.
42
+ * You can specify multiple domains to be redirected.
43
+ */
44
+ export declare class HttpsRedirect extends Construct {
45
+ constructor(scope: Construct, id: string, props: HttpsRedirectProps);
46
+ /**
47
+ * Creates a certificate.
48
+ *
49
+ * This is also safe to upgrade since the new certificate will be created and updated
50
+ * on the CloudFront distribution before the old one is deleted.
51
+ */
52
+ private createCertificate;
53
+ }