mirror of
https://github.com/serverless/serverless.git
synced 2026-02-01 16:07:28 +00:00
165 lines
5.8 KiB
JavaScript
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);
|
|
}
|
|
},
|
|
};
|