Skip to content
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

Add support for multiple certificates #59

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 49 additions & 0 deletions Readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,8 @@ To remove the certificate and delete the CNAME recordsets from route53, run:

serverless remove-cert

It's also possible to define multiple certificates to be used, in such case the `customCertificate` should be an array of objects described above.

# Combine with serverless-domain-manager

If you combine this plugin with [serverless-domain-manager](https://github.com/amplify-education/serverless-domain-manager) you can automate the complete process of creating a custom domain with a certificate.
Expand Down Expand Up @@ -130,6 +132,40 @@ Open serverless.yml and add the following:
enabled: true // optional - default is true. For some stages you may not want to use certificates (and custom domains associated with it).
rewriteRecords: false

You can also modify serverless.yml like so, to support multiple certificates (possibly defined in different regions):

plugins:
- serverless-certificate-creator
- serverless-domain-manager

...

custom:
customDomain:
domainName: abc.somedomain.io
certificateName: 'abc.somedomain.io'
basePath: ''
stage: ${self:provider.stage}
createRoute53Record: true
customCertificate:
-
certificateName: 'abc.somedomain.io' //required
idempotencyToken: 'abcsomedomainio' //optional
hostedZoneNames: 'somedomain.io.' //required if hostedZoneIds is not set
hostedZoneIds: 'XXXXXXXXX' //required if hostedZoneNames is not set
region: eu-west-1 // optional - default is us-east-1 which is required for custom api gateway domains of Type Edge (default)
enabled: true // optional - default is true. For some stages you may not want to use certificates (and custom domains associated with it).
rewriteRecords: false
-
certificateName: 'abc.someseconddomain.io' //required
idempotencyToken: 'abcsomeseconddomainio' //optional
hostedZoneNames: 'someseconddomain.io.' //required if hostedZoneIds is not set
hostedZoneIds: 'XXXXXXXXX' //required if hostedZoneNames is not set
region: us-east-1 // optional - default is us-east-1 which is required for custom api gateway domains of Type Edge (default)
enabled: true // optional - default is true. For some stages you may not want to use certificates (and custom domains associated with it).
rewriteRecords: false


Now you can run:

serverless create-cert
Expand All @@ -155,6 +191,19 @@ The new supported syntax is:

see the serverless [docs](https://serverless.com/framework/docs/providers/aws/guide/plugins#custom-variable-types) for more information

Similarly, if you've defined multiple certificates, you have to add the array index to the variable.
For example, to obtain the certificate details about first certificate in an array you'd use (note the single quotes around indexes as a second param):

If you are on version >= 2.27.0 of serverless & have elected to use the variable resolver: `variablesResolutionMode: 20210219`.
You must use this supported syntax which is:

${certificate:${self:custom.customCertificate.0.certificateName, '0'}.CertificateArn}

For the new variable resolver: `variablesResolutionMode: 20210326`:
The new supported syntax is:

${certificate(${self:custom.customCertificate.0.certificateName}, '0'):CertificateArn}

### License

Copyright (c) 2018 Bastian Töpfer, contributors.
Expand Down
87 changes: 78 additions & 9 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,13 @@ class CreateCertificatePlugin {
this.initialized = false;
this.commands = {
'create-cert': {
usage: 'creates a certificate for an existing domain/hosted zone',
usage: 'creates a certificate(s) for an existing domain/hosted zone',
lifecycleEvents: [
'create'
]
},
'remove-cert': {
usage: 'removes the certificate previously created by create-cert command',
usage: 'removes the certificate(s) previously created by create-cert command',
lifecycleEvents: [
'remove'
]
Expand Down Expand Up @@ -57,6 +57,23 @@ class CreateCertificatePlugin {
};
}

/**
* Checks if the custom certificate entry in JSON is in fact an array.
*
* If no details have been set, `false` is returned.
*
* @return Boolean
*/
isCustomCertificateArray() {
const arr = (this.serverless.service.custom || {}).customCertificate || null;
if (Array.isArray(arr)) {
this.certificateArrayLength = arr.length;
return true;
} else {
return false;
}
}

/**
* Gets the details for the custom certificate from the service settings.
*
Expand All @@ -65,15 +82,18 @@ class CreateCertificatePlugin {
* @return {Object|null}
*/
getCustomCertificateDetails() {
return (this.serverless.service.custom || {}).customCertificate || null;
if (Number.isInteger(this.certificateIndex)) {
return (this.serverless.service.custom || {}).customCertificate[(this.certificateIndex)] || null;
} else {
return (this.serverless.service.custom || {}).customCertificate || null;
}
}

initializeVariables() {
if (!this.initialized) {
this.enabled = this.evaluateEnabled();
if (this.enabled) {
const customCertificate = this.getCustomCertificateDetails() || {};

const credentials = this.serverless.providers.aws.getCredentials();
this.route53 = new this.serverless.providers.aws.sdk.Route53(credentials);
this.region = customCertificate.region || 'us-east-1';
Expand Down Expand Up @@ -238,7 +258,21 @@ class CreateCertificatePlugin {
/**
* Creates a certificate for the given options set in serverless.yml under custom->customCertificate
*/
createCertificate() {
async createCertificate() {
if (this.isCustomCertificateArray()) {
for (var i = 0; i < this.certificateArrayLength; i++) {
this.certificateIndex = i;
this.initialized = false;
await this._createCertificate();
}
// TODO: should we care about the function returns?
}
else {
return this._createCertificate();
}
}

_createCertificate() {
this.initializeVariables();
if (!this.enabled) {
return this.reportDisabled();
Expand Down Expand Up @@ -301,7 +335,21 @@ class CreateCertificatePlugin {
* Deletes the certificate for the given options set in serverless.yml under custom->customCertificate
* (if it exists)
*/
deleteCertificate() {
async deleteCertificate() {
if (this.isCustomCertificateArray()) {
for (var i = 0; i < this.certificateArrayLength; i++) {
this.certificateIndex = i;
this.initialized = false;
await this._deleteCertificate();
// TODO: should we care about the function returns?
}
}
else {
return this._deleteCertificate();
}
}

_deleteCertificate() {
this.initializeVariables();
if (!this.enabled) {
return this.reportDisabled();
Expand Down Expand Up @@ -504,13 +552,28 @@ class CreateCertificatePlugin {
/**
* Prints out a summary of all certificate related info
*/
certificateSummary() {

async certificateSummary() {
this.serverless.cli.consoleLog(chalk.yellow.underline('Serverless Certificate Creator Summary'));
if (this.isCustomCertificateArray()) {
for (var i = 0; i < this.certificateArrayLength; i++) {
this.certificateIndex = i;
this.initialized = false;
await this._certificateSummary();
}
// TODO: should we care about the function returns?
}
else {
return this._certificateSummary();
}
}

_certificateSummary() {
this.initializeVariables();
if (!this.enabled) {
return this.reportDisabled();
}
return this.getExistingCertificate().then(existingCertificate => {
this.serverless.cli.consoleLog(chalk.yellow.underline('Serverless Certificate Creator Summary'));

this.serverless.cli.consoleLog(chalk.yellow('Certificate'));
this.serverless.cli.consoleLog(` ${existingCertificate.CertificateArn} => ${existingCertificate.DomainName}`);
Expand All @@ -519,9 +582,13 @@ class CreateCertificatePlugin {
}

async getCertificateProperty({address, params}) {

const property = address
const domainName = params[0]
if (params.length >= 2) {
this.certificateIndex = parseInt(params[1]);
this.initialized = false;
}


this.initializeVariables();
if (!this.enabled) {
Expand Down Expand Up @@ -550,6 +617,8 @@ class CreateCertificatePlugin {
}

getCertificatePropertyOld(src) {
// TODO: seems that this is not used anymore?
// How can I test it?
this.initializeVariables();
if (!this.enabled) {
return Promise.resolve('');
Expand Down