serverless/lib/plugins/aws/package/lib/mergeIamTemplates.js
2017-04-06 18:28:59 +07:00

165 lines
5.8 KiB
JavaScript

'use strict';
const _ = require('lodash');
const BbPromise = require('bluebird');
const path = require('path');
module.exports = {
mergeIamTemplates() {
this.validateStatements(this.serverless.service.provider.iamRoleStatements);
return this.merge();
},
merge() {
// resolve early if no functions are provided
if (!this.serverless.service.getAllFunctions().length) {
return BbPromise.resolve();
}
// create log group resources
this.serverless.service.getAllFunctions().forEach((functionName) => {
const functionObject = this.serverless.service.getFunction(functionName);
const logGroupLogicalId = this.provider.naming
.getLogGroupLogicalId(functionName);
const newLogGroup = {
[logGroupLogicalId]: {
Type: 'AWS::Logs::LogGroup',
Properties: {
LogGroupName: this.provider.naming.getLogGroupName(functionObject.name),
},
},
};
_.merge(this.serverless.service.provider.compiledCloudFormationTemplate.Resources,
newLogGroup);
});
// resolve early if provider level role is provided
if ('role' in this.serverless.service.provider) {
return BbPromise.resolve();
}
// resolve early if all functions contain a custom role
const customRolesProvided = [];
this.serverless.service.getAllFunctions().forEach((functionName) => {
const functionObject = this.serverless.service.getFunction(functionName);
customRolesProvided.push('role' in functionObject);
});
if (_.isEqual(_.uniq(customRolesProvided), [true])) {
return BbPromise.resolve();
}
// merge in the iamRoleLambdaTemplate
const iamRoleLambdaExecutionTemplate = this.serverless.utils.readFileSync(
path.join(this.serverless.config.serverlessPath,
'plugins',
'aws',
'package',
'lib',
'iam-role-lambda-execution-template.json')
);
iamRoleLambdaExecutionTemplate.Properties.Path = this.provider.naming.getRolePath();
iamRoleLambdaExecutionTemplate.Properties.RoleName = this.provider.naming.getRoleName();
iamRoleLambdaExecutionTemplate.Properties.Policies[0]
.PolicyName = this.provider.naming.getPolicyName();
_.merge(
this.serverless.service.provider.compiledCloudFormationTemplate.Resources,
{
[this.provider.naming.getRoleLogicalId()]: iamRoleLambdaExecutionTemplate,
}
);
this.serverless.service.getAllFunctions().forEach((functionName) => {
const functionObject = this.serverless.service.getFunction(functionName);
this.serverless.service.provider.compiledCloudFormationTemplate
.Resources[this.provider.naming.getRoleLogicalId()]
.Properties
.Policies[0]
.PolicyDocument
.Statement[0]
.Resource
.push({ 'Fn::Sub': 'arn:aws:logs:${AWS::Region}:${AWS::AccountId}' +
`:log-group:${this.provider.naming.getLogGroupName(functionObject.name)}:*` });
this.serverless.service.provider.compiledCloudFormationTemplate
.Resources[this.provider.naming.getRoleLogicalId()]
.Properties
.Policies[0]
.PolicyDocument
.Statement[1]
.Resource
.push({ 'Fn::Sub': 'arn:aws:logs:${AWS::Region}:${AWS::AccountId}' +
`:log-group:${this.provider.naming.getLogGroupName(functionObject.name)}:*:*` });
});
if (this.serverless.service.provider.iamRoleStatements) {
// add custom iam role statements
this.serverless.service.provider.compiledCloudFormationTemplate
.Resources[this.provider.naming.getRoleLogicalId()]
.Properties
.Policies[0]
.PolicyDocument
.Statement = this.serverless.service.provider.compiledCloudFormationTemplate
.Resources[this.provider.naming.getRoleLogicalId()]
.Properties
.Policies[0]
.PolicyDocument
.Statement.concat(this.serverless.service.provider.iamRoleStatements);
}
// check if one of the functions contains vpc configuration
const vpcConfigProvided = [];
this.serverless.service.getAllFunctions().forEach((functionName) => {
const functionObject = this.serverless.service.getFunction(functionName);
if ('vpc' in functionObject) {
vpcConfigProvided.push(true);
}
});
if (_.includes(vpcConfigProvided, true) || this.serverless.service.provider.vpc) {
// add managed iam policy to allow ENI management
this.serverless.service.provider.compiledCloudFormationTemplate
.Resources[this.provider.naming.getRoleLogicalId()]
.Properties
.ManagedPolicyArns = [
'arn:aws:iam::aws:policy/service-role/AWSLambdaVPCAccessExecutionRole',
];
}
return BbPromise.resolve();
},
validateStatements(statements) {
// Verify that iamRoleStatements (if present) is an array of { Effect: ...,
// Action: ..., Resource: ... } objects.
if (!statements) {
return;
}
let violationsFound;
if (!(statements instanceof Array)) {
violationsFound = 'it is not an array';
} else {
const descriptions = statements.map((statement, i) => {
const missing = ['Effect', 'Action', 'Resource'].filter(
prop => statement[prop] === undefined);
return missing.length === 0 ? null :
`statement ${i} is missing the following properties: ${missing.join(', ')}`;
});
const flawed = descriptions.filter(curr => curr);
if (flawed.length) {
violationsFound = flawed.join('; ');
}
}
if (violationsFound) {
const errorMessage = [
'iamRoleStatements should be an array of objects,',
' where each object has Effect, Action, Resource fields.',
` Specifically, ${violationsFound}`,
].join('');
throw new this.serverless.classes.Error(errorMessage);
}
},
};