Skip to content

Commit

Permalink
Merge pull request #18 from schwamster/next
Browse files Browse the repository at this point in the history
PRs #15 #16 #17
  • Loading branch information
schwamster committed Sep 26, 2019
2 parents cfc1c9c + b2cd6f9 commit c16e8dd
Show file tree
Hide file tree
Showing 5 changed files with 98 additions and 55 deletions.
27 changes: 19 additions & 8 deletions Readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ Check out their getting started guide for more information [here](https://server
Make sure you have the following installed before starting:
* [nodejs](https://nodejs.org/en/download/)
* [npm](https://www.npmjs.com/get-npm?utm_source=house&utm_medium=homepage&utm_campaign=free%20orgs&utm_term=Install%20npm)
* [serverless](https://serverless.com/framework/docs/providers/aws/guide/installation/)
* [serverless](https://serverless.com/framework/docs/providers/aws/guide/installation/) >= v1.52.0

# Usage

Expand All @@ -62,11 +62,11 @@ open serverless.yml and add the following:
certificateName: 'abc.somedomain.io'
//optional
idempotencyToken: 'abcsomedomainio'
//required if hostedZoneId is not set
hostedZoneName: 'somedomain.io.'
//required if hostedZoneName is not set
hostedZoneId: 'XXXXXXXXX'
// optional default is false. if you set it to true you will get a new file (after executing serverless create-cert), that contains certificate info that you can use in your deploy pipeline
//required if hostedZoneIds is not set, alternativly as an array
hostedZoneNames: 'somedomain.io.'
//required if hostedZoneNames is not set
hostedZoneIds: 'XXXXXXXXX'
// optional default is false. if you set it to true you will get a new file (after executing serverless create-cert), that contains certificate info that you can use in your deploy pipeline, alternativly as an array
writeCertInfoToFile: false
// optional, only used when writeCertInfoToFile is set to true. It sets the name of the file containing the cert info
certInfoFileName: 'cert-info.yml'
Expand All @@ -81,6 +81,8 @@ open serverless.yml and add the following:
tags:
Name: 'somedomain.com'
Environment: 'prod'
//optional default false. this is useful if you managed to delete your certificate but the dns validation records still exist
rewriteRecords: false


now you can run:
Expand Down Expand Up @@ -118,10 +120,11 @@ Open serverless.yml and add the following:
customCertificate:
certificateName: 'abc.somedomain.io' //required
idempotencyToken: 'abcsomedomainio' //optional
hostedZoneName: 'somedomain.io.' //required if hostedZoneId is not set
hostedZoneId: 'XXXXXXXXX' //required if hostedZoneName is not set
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

Now you can run:

Expand All @@ -130,6 +133,14 @@ Now you can run:

Please make sure to check out the complete sample project [here](https://github.com/schwamster/serverless-certificate-creator/tree/master/examples/certificate-creator-example).

### Reference Certificate Arn via variableResolvers

Since version 1.2.0 of this plugin you can use the following syntax to access the certificates Arn in other plugins

${certificate:${self:custom.customCertificate.certificateName}:CertificateArn}

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

### License

Copyright (c) 2018 Bastian Töpfer, contributors.
Expand Down
2 changes: 1 addition & 1 deletion examples/certificate-creator-example/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
"author": "",
"license": "ISC",
"devDependencies": {
"serverless-certificate-creator": "^1.0.8",
"serverless-certificate-creator": "^1.2.0",
"serverless-domain-manager": "^3.2.6"
}
}
5 changes: 3 additions & 2 deletions examples/certificate-creator-example/serverless.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,11 @@ custom:
idempotencyToken: 'certcreatorsamplegreenelephantio'
writeCertInfoToFile: true
certInfoFileName: "certs/${self:provider.stage}/cert-info.yml"
hostedZoneName: 'greenelephant.io.'
hostedZoneNames: 'greenelephant.io.'
subjectAlternativeNames :
- 'certcreatorsample1.greenelephant.io'
- 'certcreatorsample2.greenelephant.io'
tags:
Name: 'somedomain.com'
Environment: 'prod'
Environment: 'prod'
rewriteRecords: false
117 changes: 74 additions & 43 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ const fs = require('fs');
const path = require('path');
const YAML = require('yamljs');
const mkdirp = require('mkdirp');
var packageJson = require('./package.json');

const unsupportedRegionPrefixes = ['cn-'];

Expand All @@ -13,7 +14,7 @@ class CreateCertificatePlugin {
this.serverless = serverless;
this.options = options;
this.initialized = false;

this.serverless.cli.log(`serverless-certificate-creator version ${packageJson.version} called`);
this.commands = {
'create-cert': {
usage: 'creates a certificate for an existing domain/hosted zone',
Expand All @@ -28,6 +29,14 @@ class CreateCertificatePlugin {
'after:deploy:deploy': this.certificateSummary.bind(this),
'after:info:info': this.certificateSummary.bind(this),
};

this.variableResolvers = {
certificate: {
resolver: this.getCertificateProperty.bind(this),
isDisabledAtPrepopulation: true,
serviceName: 'serverless-certificate-creator depends on AWS credentials.'
}
};
}

initializeVariables() {
Expand All @@ -38,12 +47,15 @@ class CreateCertificatePlugin {
this.route53 = new this.serverless.providers.aws.sdk.Route53(credentials);
this.region = this.serverless.service.custom.customCertificate.region || 'us-east-1';
this.domain = this.serverless.service.custom.customCertificate.certificateName;
this.hostedZoneId = this.serverless.service.custom.customCertificate.hostedZoneId;
this.hostedZoneName = this.serverless.service.custom.customCertificate.hostedZoneName;
//hostedZoneId is mapped for backwards compatibility
this.hostedZoneIds = this.serverless.service.custom.customCertificate.hostedZoneIds ? this.serverless.service.custom.customCertificate.hostedZoneIds : (this.serverless.service.custom.customCertificate.hostedZoneId) ? [].concat(this.serverless.service.custom.customCertificate.hostedZoneId) : [];
//hostedZoneName is mapped for backwards compatibility
this.hostedZoneNames = this.serverless.service.custom.customCertificate.hostedZoneNames ? this.serverless.service.custom.customCertificate.hostedZoneNames : (this.serverless.service.custom.customCertificate.hostedZoneName) ? [].concat(this.serverless.service.custom.customCertificate.hostedZoneName) : [];
const acmCredentials = Object.assign({}, credentials, { region: this.region });
this.acm = new this.serverless.providers.aws.sdk.ACM(acmCredentials);
this.idempotencyToken = this.serverless.service.custom.customCertificate.idempotencyToken;
this.writeCertInfoToFile = this.serverless.service.custom.customCertificate.writeCertInfoToFile || false;
this.rewriteRecords = this.serverless.service.custom.customCertificate.rewriteRecords || false;
this.certInfoFileName = this.serverless.service.custom.customCertificate.certInfoFileName || 'cert-info.yml';
this.subjectAlternativeNames = this.serverless.service.custom.customCertificate.subjectAlternativeNames || [];
this.tags = this.serverless.service.custom.customCertificate.tags || {};
Expand All @@ -55,7 +67,6 @@ class CreateCertificatePlugin {
}
})
}

this.initialized = true;
}
}
Expand Down Expand Up @@ -110,7 +121,7 @@ class CreateCertificatePlugin {
CertificateArn: certificateArn,
Tags: mappedTags
}

this.serverless.cli.log(`tagging certificate`);
return this.acm.addTagsToCertificate(params).promise().catch(error => {
this.serverless.cli.log('tagging certificate failed', error);
Expand Down Expand Up @@ -228,21 +239,19 @@ class CreateCertificatePlugin {
});
}

getHostedZoneId() {
getHostedZoneIds() {

return this.route53.listHostedZones({}).promise().then(data => {

if (this.hostedZoneId) {
return this.hostedZoneId;
}
let hostedZones = data.HostedZones.filter(x => this.hostedZoneIds.includes(x.Id.replace(/\/hostedzone\//g, '')) || this.hostedZoneNames.includes(x.Name));

let hostedZone = data.HostedZones.filter(x => x.Name == this.hostedZoneName);
if (hostedZone.length == 0) {
if (hostedZones.length == 0) {
throw "no hosted zone for domain found"
}

this.hostedZoneId = hostedZone[0].Id.replace(/\/hostedzone\//g, '');
return this.hostedZoneId;
return hostedZones.map(({ Id, Name }) => {
return { hostedZoneId: Id.replace(/\/hostedzone\//g, ''), Name: Name.substr(0, Name.length - 1) };
});
}).catch(error => {
this.serverless.cli.log('certificate validation failed', error);
console.log('problem', error);
Expand All @@ -255,38 +264,40 @@ class CreateCertificatePlugin {
* at least a short time after the cert has been created, thats why you should delay this call a bit after u created a new cert
*/
createRecordSetForDnsValidation(certificate) {
return this.getHostedZoneId().then((hostedZoneId) => {

let changes = certificate.Certificate.DomainValidationOptions.map((x) => {
return {
Action: "CREATE",
ResourceRecordSet: {
Name: x.ResourceRecord.Name,
ResourceRecords: [
{
Value: x.ResourceRecord.Value
}
],
TTL: 60,
Type: x.ResourceRecord.Type
return this.getHostedZoneIds().then((hostedZoneIds) => {

return Promise.all(hostedZoneIds.map(({ hostedZoneId, Name }) => {
let changes = certificate.Certificate.DomainValidationOptions.filter(({DomainName}) => DomainName.endsWith(Name)).map((x) => {
return {
Action: this.rewriteRecords ? "UPSERT" : "CREATE",
ResourceRecordSet: {
Name: x.ResourceRecord.Name,
ResourceRecords: [
{
Value: x.ResourceRecord.Value
}
],
TTL: 60,
Type: x.ResourceRecord.Type
}
}
}
});
});

var params = {
ChangeBatch: {
Changes: changes,
Comment: `DNS Validation for certificate ${certificate.Certificate.DomainValidationOptions[0].DomainName}`
},
HostedZoneId: hostedZoneId
};
return this.route53.changeResourceRecordSets(params).promise().then(recordSetResult => {
this.serverless.cli.log('dns validation record(s) created - certificate is ready for use after validation has gone through');
}).catch(error => {
this.serverless.cli.log('could not create record set for dns validation', error);
console.log('problem', error);
throw error;
});
var params = {
ChangeBatch: {
Changes: changes,
Comment: `DNS Validation for certificate ${Name}`
},
HostedZoneId: hostedZoneId
};
return this.route53.changeResourceRecordSets(params).promise().then(recordSetResult => {
this.serverless.cli.log('dns validation record(s) created - certificate is ready for use after validation has gone through');
}).catch(error => {
this.serverless.cli.log('could not create record set for dns validation', error);
console.log('problem', error);
throw error;
});
}));
});
}

Expand All @@ -306,6 +317,26 @@ class CreateCertificatePlugin {
return true;
});
}

getCertificateProperty(src) {
this.initializeVariables();
let [s, domainName, property] = src.split(':');
return this.listCertificates()
.then(({ CertificateSummaryList }) => {
let cert = CertificateSummaryList.filter(({ DomainName }) => DomainName == domainName)[0];
if (cert && cert[property]) {
return cert[property];
} else {
this.serverless.cli.consoleLog(chalk.yellow('Warning, certificate or certificate property was not found. Returning an empty string instead!'));
return '';
}
})
.catch(error => {
console.log(this.domain, this.region);
this.serverless.cli.log('Could not find certificate property attempting to create...');
throw error;
});
}
}


Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "serverless-certificate-creator",
"version": "1.1.0",
"version": "1.2.0",
"description": "creates a certificate that can be used for custom domains for your api gateway",
"main": "index.js",
"scripts": {
Expand Down

0 comments on commit c16e8dd

Please sign in to comment.