@lumigo/serverless-crossaccount-ssm
Version:
Serverless framework plugin to access the system and secrets managers at isolated account
101 lines (85 loc) • 2.75 kB
JavaScript
const _ = require("lodash");
const AWS = require("aws-sdk");
const util = require("util");
const PromiseAny = require("promise.any");
class CrossaccountSSM {
constructor(serverless, options) {
this.serverless = serverless;
this.options = options;
this.log = msg => this.serverless.cli.log(`crossaccount-ssm: ${msg}`);
this.verboseLog = msg => {
if (process.env.SLS_DEBUG) {
this.log(msg);
}
};
// NOTE: config load is deferred to resolver due to the late embedded variables evaluation
this.config = null;
this.ssmResolver = serverless.variables.variableResolvers.find(
({ serviceName }) => serviceName === "SSM"
);
if (this.ssmResolver) {
this.ssmResolver.resolver = this.resolver.bind(this);
this.verboseLog(
"Default SSM resolver was replaced with the crossaccount one"
);
}
}
async resolver(name) {
const [, , key] = name.match(this.ssmResolver.regex) || [];
if (!key) return Promise.resolve();
this.verboseLog(`Resolving ${key}`);
if (!this.config) this.config = this.getConfig();
const validTrues = new Set(["True", "true", "Yes", "yes", true]);
const enabled = validTrues.has(_.get(this.config, "enable", true));
// NOTE: the reason it exists is that I currently cant understand the evaluation order
// FIXME: this N/A-workaround should be wiped-up
const nonAvailableMarker = "NA";
if (key.includes(nonAvailableMarker)) {
this.verboseLog(
`Resolving skipped due to the resolution disabled (secret name contains '${nonAvailableMarker}' marker: ${key})`
);
return key;
}
if (enabled) {
AWS.config.credentials = new AWS.SharedIniFileCredentials({
profile: this.config.profile
});
const secretParam = { Name: key, WithDecryption: true };
let replicas = _.chain(this.config.regions).map(r =>
new AWS.SSM({ region: r })
.getParameter(secretParam)
.promise()
.then(s => s.Parameter.Value)
);
return PromiseAny(replicas.value()).catch(error => {
this.log(
"multi-regional failure (all the regions requested were rejected)"
);
throw error;
});
} else {
this.verboseLog(
`Resolving skipped due to the resolution disabled (enable: ${enabled})`
);
return key;
}
}
getConfig() {
const configKey = "custom.crossaccount-ssm";
const currentConfig = _.get(this.serverless.service, configKey, null);
const defaultConfig = {
profile: "default",
regions: ["us-east-1"]
};
if (!currentConfig) {
this.verboseLog(
`config wasn't found. Defaulted to: ${util.inspect(defaultConfig)}`
);
return defaultConfig;
} else {
this.verboseLog(`config found: ${util.inspect(currentConfig)}`);
return currentConfig;
}
}
}
module.exports = CrossaccountSSM;