-
Notifications
You must be signed in to change notification settings - Fork 4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(certificatemanager): native CloudFormation DNS validated certificate #8552
Merged
Merged
Changes from all commits
Commits
Show all changes
16 commits
Select commit
Hold shift + click to select a range
adb63fc
feat(certificatemanager): native CloudFormation DNS validated certifi…
jogold fb22718
update ecs-patterns
jogold f1c3328
Merge branch 'master' into acm-dns-cf
jogold dab531c
Merge branch 'master' into acm-dns-cf
jogold d51738d
Merge branch 'master' into acm-dns-cf
jogold d3e9c4d
do not deprecate DnsValidatedCertificate
jogold fb792ff
README for DnsValidatedCertificate
jogold 0248018
README DNS
jogold 3b1ee06
fromDnsMultiZone
jogold 01b5941
JSDoc
jogold a7f1997
Merge branch 'master' into acm-dns-cf
jogold a371fbd
AWS Route 53 -> Amazon Route 53
jogold 538b658
revert delete example.dns-validated-request.lit.ts
jogold 785dff6
Merge branch 'master' into acm-dns-cf
jogold 2742524
Merge branch 'master' into acm-dns-cf
njlynch 8b9d7cb
Merge branch 'master' into acm-dns-cf
mergify[bot] File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,4 @@ | ||
import * as route53 from '@aws-cdk/aws-route53'; | ||
import { Construct, IResource, Resource, Token } from '@aws-cdk/core'; | ||
import { CfnCertificate } from './certificatemanager.generated'; | ||
import { apexDomain } from './util'; | ||
|
@@ -40,33 +41,132 @@ export interface CertificateProps { | |
* Has to be a superdomain of the requested domain. | ||
* | ||
* @default - Apex domain is used for every domain that's not overridden. | ||
* @deprecated use `validation` instead. | ||
*/ | ||
readonly validationDomains?: {[domainName: string]: string}; | ||
|
||
/** | ||
* Validation method used to assert domain ownership | ||
* | ||
* @default ValidationMethod.EMAIL | ||
* @deprecated use `validation` instead. | ||
*/ | ||
readonly validationMethod?: ValidationMethod; | ||
|
||
/** | ||
* How to validate this certifcate | ||
* | ||
* @default CertificateValidation.fromEmail() | ||
*/ | ||
readonly validation?: CertificateValidation; | ||
} | ||
|
||
/** | ||
* Properties for certificate validation | ||
*/ | ||
export interface CertificationValidationProps { | ||
/** | ||
* Validation method | ||
* | ||
* @default ValidationMethod.EMAIL | ||
*/ | ||
readonly method?: ValidationMethod; | ||
|
||
/** | ||
* Hosted zone to use for DNS validation | ||
* | ||
* @default - use email validation | ||
*/ | ||
readonly hostedZone?: route53.IHostedZone; | ||
|
||
/** | ||
* A map of hosted zones to use for DNS validation | ||
* | ||
* @default - use `hostedZone` | ||
*/ | ||
readonly hostedZones?: { [domainName: string]: route53.IHostedZone }; | ||
|
||
/** | ||
* Validation domains to use for email validation | ||
* | ||
* @default - Apex domain | ||
*/ | ||
readonly validationDomains?: { [domainName: string]: string }; | ||
} | ||
|
||
/** | ||
* How to validate a certificate | ||
*/ | ||
export class CertificateValidation { | ||
/** | ||
* Validate the certifcate with DNS | ||
* | ||
* IMPORTANT: If `hostedZone` is not specified, DNS records must be added | ||
* manually and the stack will not complete creating until the records are | ||
* added. | ||
* | ||
* @param hostedZone the hosted zone where DNS records must be created | ||
*/ | ||
public static fromDns(hostedZone?: route53.IHostedZone) { | ||
return new CertificateValidation({ | ||
method: ValidationMethod.DNS, | ||
hostedZone, | ||
}); | ||
} | ||
|
||
/** | ||
* Validate the certifcate with automatically created DNS records in multiple | ||
* Amazon Route 53 hosted zones. | ||
* | ||
* @param hostedZones a map of hosted zones where DNS records must be created | ||
* for the domains in the certificate | ||
*/ | ||
public static fromDnsMultiZone(hostedZones: { [domainName: string]: route53.IHostedZone }) { | ||
return new CertificateValidation({ | ||
method: ValidationMethod.DNS, | ||
hostedZones, | ||
}); | ||
} | ||
|
||
/** | ||
* Validate the certifcate with Email | ||
* | ||
* IMPORTANT: if you are creating a certificate as part of your stack, the stack | ||
* will not complete creating until you read and follow the instructions in the | ||
* email that you will receive. | ||
* | ||
* ACM will send validation emails to the following addresses: | ||
* | ||
* [email protected] | ||
* [email protected] | ||
* [email protected] | ||
* [email protected] | ||
* [email protected] | ||
* | ||
* For every domain that you register. | ||
* | ||
* @param validationDomains a map of validation domains to use for domains in the certificate | ||
*/ | ||
public static fromEmail(validationDomains?: { [domainName: string]: string }) { | ||
return new CertificateValidation({ | ||
method: ValidationMethod.EMAIL, | ||
validationDomains, | ||
}); | ||
} | ||
|
||
/** | ||
* The validation method | ||
*/ | ||
public readonly method: ValidationMethod; | ||
|
||
/** @param props Certification validation properties */ | ||
private constructor(public readonly props: CertificationValidationProps) { | ||
this.method = props.method ?? ValidationMethod.EMAIL; | ||
} | ||
} | ||
|
||
/** | ||
* A certificate managed by AWS Certificate Manager | ||
* | ||
* IMPORTANT: if you are creating a certificate as part of your stack, the stack | ||
* will not complete creating until you read and follow the instructions in the | ||
* email that you will receive. | ||
* | ||
* ACM will send validation emails to the following addresses: | ||
* | ||
* [email protected] | ||
* [email protected] | ||
* [email protected] | ||
* [email protected] | ||
* [email protected] | ||
* | ||
* For every domain that you register. | ||
*/ | ||
export class Certificate extends Resource implements ICertificate { | ||
|
||
|
@@ -89,33 +189,27 @@ export class Certificate extends Resource implements ICertificate { | |
constructor(scope: Construct, id: string, props: CertificateProps) { | ||
super(scope, id); | ||
|
||
let validation: CertificateValidation; | ||
if (props.validation) { | ||
validation = props.validation; | ||
} else { // Deprecated props | ||
if (props.validationMethod === ValidationMethod.DNS) { | ||
validation = CertificateValidation.fromDns(); | ||
} else { | ||
validation = CertificateValidation.fromEmail(props.validationDomains); | ||
} | ||
} | ||
|
||
const allDomainNames = [props.domainName].concat(props.subjectAlternativeNames || []); | ||
|
||
const cert = new CfnCertificate(this, 'Resource', { | ||
domainName: props.domainName, | ||
subjectAlternativeNames: props.subjectAlternativeNames, | ||
domainValidationOptions: allDomainNames.map(domainValidationOption), | ||
validationMethod: props.validationMethod, | ||
domainValidationOptions: renderDomainValidation(validation, allDomainNames), | ||
validationMethod: validation.method, | ||
}); | ||
|
||
this.certificateArn = cert.ref; | ||
|
||
/** | ||
* Return the domain validation options for the given domain | ||
* | ||
* Closes over props. | ||
*/ | ||
function domainValidationOption(domainName: string): CfnCertificate.DomainValidationOptionProperty { | ||
let validationDomain = props.validationDomains && props.validationDomains[domainName]; | ||
if (validationDomain === undefined) { | ||
if (Token.isUnresolved(domainName)) { | ||
throw new Error('When using Tokens for domain names, \'validationDomains\' needs to be supplied'); | ||
} | ||
validationDomain = apexDomain(domainName); | ||
} | ||
|
||
return { domainName, validationDomain }; | ||
} | ||
} | ||
} | ||
|
||
|
@@ -136,4 +230,33 @@ export enum ValidationMethod { | |
* @see https://docs.aws.amazon.com/acm/latest/userguide/gs-acm-validate-dns.html | ||
*/ | ||
DNS = 'DNS', | ||
} | ||
} | ||
|
||
// tslint:disable-next-line:max-line-length | ||
function renderDomainValidation(validation: CertificateValidation, domainNames: string[]): CfnCertificate.DomainValidationOptionProperty[] | undefined { | ||
const domainValidation: CfnCertificate.DomainValidationOptionProperty[] = []; | ||
|
||
switch (validation.method) { | ||
case ValidationMethod.DNS: | ||
for (const domainName of domainNames) { | ||
const hostedZone = validation.props.hostedZones?.[domainName] ?? validation.props.hostedZone; | ||
if (hostedZone) { | ||
domainValidation.push({ domainName, hostedZoneId: hostedZone.hostedZoneId }); | ||
} | ||
} | ||
break; | ||
case ValidationMethod.EMAIL: | ||
for (const domainName of domainNames) { | ||
const validationDomain = validation.props.validationDomains?.[domainName]; | ||
if (!validationDomain && Token.isUnresolved(domainName)) { | ||
throw new Error('When using Tokens for domain names, \'validationDomains\' needs to be supplied'); | ||
} | ||
domainValidation.push({ domainName, validationDomain: validationDomain ?? apexDomain(domainName) }); | ||
} | ||
break; | ||
default: | ||
throw new Error(`Unknown validation method ${validation.method}`); | ||
} | ||
|
||
return domainValidation.length !== 0 ? domainValidation : undefined; | ||
} |
36 changes: 36 additions & 0 deletions
36
packages/@aws-cdk/aws-certificatemanager/test/example.dns.lit.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
import * as route53 from '@aws-cdk/aws-route53'; | ||
import { App, CfnOutput, Construct, Stack } from '@aws-cdk/core'; | ||
import * as acm from '../lib'; | ||
|
||
class AcmStack extends Stack { | ||
constructor(scope: Construct, id: string) { | ||
super(scope, id); | ||
/// !show | ||
const exampleCom = new route53.HostedZone(this, 'ExampleCom', { | ||
zoneName: 'example.com', | ||
}); | ||
|
||
const exampleNet = new route53.HostedZone(this, 'ExampelNet', { | ||
zoneName: 'example.net', | ||
}); | ||
|
||
const cert = new acm.Certificate(this, 'Certificate', { | ||
domainName: 'test.example.com', | ||
subjectAlternativeNames: ['cool.example.com', 'test.example.net'], | ||
validation: acm.CertificateValidation.fromDnsMultiZone({ | ||
'text.example.com': exampleCom, | ||
'cool.example.com': exampleCom, | ||
'test.example.net': exampleNet, | ||
}), | ||
}); | ||
/// !hide | ||
|
||
new CfnOutput(this, 'Output', { | ||
value: cert.certificateArn, | ||
}); | ||
} | ||
} | ||
|
||
const app = new App(); | ||
new AcmStack(app, 'AcmStack'); | ||
app.synth(); |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we do:
to get a better naming?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think it's worth it, especially considering cross-region support is on the roadmap for CloudFormation. Let's just leave it as-is and deprecate it once we can.