diff --git a/CHANGELOG.md b/CHANGELOG.md index 4f38b8c87..7bf612b16 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,12 @@ +# 1.45.1 (2019-06-12) + +- [Fix IAM policies setup for functions with custom name](https://github.com/serverless/serverless/pull/6240) +- [Fix Travis CI deploy config](https://github.com/serverless/serverless/pull/6234) + +## Meta +- [Comparison since last release](https://github.com/serverless/serverless/compare/v1.45.0...v1.45.1) + + # 1.45.0 (2019-06-12) - [Add `--config` option](https://github.com/serverless/serverless/pull/6216) diff --git a/lib/plugins/aws/package/lib/mergeIamTemplates.js b/lib/plugins/aws/package/lib/mergeIamTemplates.js index 2698be83f..030751af7 100644 --- a/lib/plugins/aws/package/lib/mergeIamTemplates.js +++ b/lib/plugins/aws/package/lib/mergeIamTemplates.js @@ -83,33 +83,58 @@ module.exports = { } ); + const canonicalFunctionNamePrefix = + `${this.provider.serverless.service.service}-${this.provider.getStage()}`; const logGroupsPrefix = this.provider.naming - .getLogGroupName(`${this.provider.serverless.service.service}-${this.provider.getStage()}`); + .getLogGroupName(canonicalFunctionNamePrefix); - this.serverless.service.provider.compiledCloudFormationTemplate + const policyDocumentStatements = this.serverless.service.provider.compiledCloudFormationTemplate .Resources[this.provider.naming.getRoleLogicalId()] .Properties .Policies[0] .PolicyDocument - .Statement[0] + .Statement; + + // Ensure general polices for functions with default name resolution + policyDocumentStatements[0] .Resource .push({ 'Fn::Sub': 'arn:${AWS::Partition}:logs:${AWS::Region}:${AWS::AccountId}' + `:log-group:${logGroupsPrefix}*:*`, }); - this.serverless.service.provider.compiledCloudFormationTemplate - .Resources[this.provider.naming.getRoleLogicalId()] - .Properties - .Policies[0] - .PolicyDocument - .Statement[1] + policyDocumentStatements[1] .Resource .push({ 'Fn::Sub': 'arn:${AWS::Partition}:logs:${AWS::Region}:${AWS::AccountId}' + `:log-group:${logGroupsPrefix}*:*:*`, }); + // Ensure policies for functions with custom name resolution + this.serverless.service.getAllFunctions().forEach((functionName) => { + const { name: resolvedFunctionName } = this.serverless.service.getFunction(functionName); + if (!resolvedFunctionName || resolvedFunctionName.startsWith(canonicalFunctionNamePrefix)) { + return; + } + + const customFunctionNamelogGroupsPrefix = + this.provider.naming.getLogGroupName(resolvedFunctionName); + + policyDocumentStatements[0] + .Resource + .push({ + 'Fn::Sub': 'arn:${AWS::Partition}:logs:${AWS::Region}:${AWS::AccountId}' + + `:log-group:${customFunctionNamelogGroupsPrefix}:*`, + }); + + policyDocumentStatements[1] + .Resource + .push({ + 'Fn::Sub': 'arn:${AWS::Partition}:logs:${AWS::Region}:${AWS::AccountId}' + + `:log-group:${customFunctionNamelogGroupsPrefix}:*:*`, + }); + }); + if (this.serverless.service.provider.iamRoleStatements) { // add custom iam role statements this.serverless.service.provider.compiledCloudFormationTemplate @@ -117,12 +142,8 @@ module.exports = { .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); + .Statement = policyDocumentStatements + .concat(this.serverless.service.provider.iamRoleStatements); } if (this.serverless.service.provider.iamManagedPolicies) { diff --git a/lib/plugins/aws/package/lib/mergeIamTemplates.test.js b/lib/plugins/aws/package/lib/mergeIamTemplates.test.js index 59a41473c..c659ab00f 100644 --- a/lib/plugins/aws/package/lib/mergeIamTemplates.test.js +++ b/lib/plugins/aws/package/lib/mergeIamTemplates.test.js @@ -9,12 +9,15 @@ const AwsPackage = require('../index'); describe('#mergeIamTemplates()', () => { let awsPackage; let serverless; + const serviceName = 'new-service'; const functionName = 'test'; + const stage = 'dev'; + const resolvedFunctionName = `${serviceName}-${stage}-${functionName}`; beforeEach(() => { serverless = new Serverless(); const options = { - stage: 'dev', + stage, region: 'us-east-1', }; serverless.setProvider('aws', new AwsProvider(serverless, options)); @@ -23,14 +26,14 @@ describe('#mergeIamTemplates()', () => { awsPackage.serverless.service.provider.compiledCloudFormationTemplate = { Resources: {}, }; - awsPackage.serverless.service.service = 'new-service'; + awsPackage.serverless.service.service = serviceName; awsPackage.serverless.service.functions = { [functionName]: { - name: 'test', artifact: 'test.zip', handler: 'handler.hello', }, }; + serverless.service.setFunctionNames(); // Ensure to resolve function names }); it('should not merge if there are no functions', () => { @@ -136,6 +139,114 @@ describe('#mergeIamTemplates()', () => { }) ); + + it('should ensure IAM policies for custom named functions', () => { + const customFunctionName = 'foo-bar'; + awsPackage.serverless.service.functions = { + [functionName]: { + name: customFunctionName, + artifact: 'test.zip', + handler: 'handler.hello', + }, + }; + serverless.service.setFunctionNames(); // Ensure to resolve function names + + return awsPackage.mergeIamTemplates() + .then(() => { + const canonicalFunctionsPrefix = + `${awsPackage.serverless.service.service}-${awsPackage.provider.getStage()}`; + + expect(awsPackage.serverless.service.provider.compiledCloudFormationTemplate + .Resources[awsPackage.provider.naming.getRoleLogicalId()] + ).to.deep.equal({ + Type: 'AWS::IAM::Role', + Properties: { + AssumeRolePolicyDocument: { + Version: '2012-10-17', + Statement: [ + { + Effect: 'Allow', + Principal: { + Service: [ + 'lambda.amazonaws.com', + ], + }, + Action: [ + 'sts:AssumeRole', + ], + }, + ], + }, + Path: '/', + Policies: [ + { + PolicyName: { + 'Fn::Join': [ + '-', + [ + awsPackage.provider.getStage(), + awsPackage.serverless.service.service, + 'lambda', + ], + ], + }, + PolicyDocument: { + Version: '2012-10-17', + Statement: [ + { + Effect: 'Allow', + Action: [ + 'logs:CreateLogStream', + ], + Resource: [ + { + 'Fn::Sub': 'arn:${AWS::Partition}:logs:${AWS::Region}:${AWS::AccountId}:' + + `log-group:/aws/lambda/${canonicalFunctionsPrefix}*:*`, + }, + { + 'Fn::Sub': 'arn:${AWS::Partition}:logs:${AWS::Region}:${AWS::AccountId}:' + + `log-group:/aws/lambda/${customFunctionName}:*`, + }, + ], + }, + { + Effect: 'Allow', + Action: [ + 'logs:PutLogEvents', + ], + Resource: [ + { + 'Fn::Sub': 'arn:${AWS::Partition}:logs:${AWS::Region}:${AWS::AccountId}:' + + `log-group:/aws/lambda/${canonicalFunctionsPrefix}*:*:*`, + }, + { + 'Fn::Sub': 'arn:${AWS::Partition}:logs:${AWS::Region}:${AWS::AccountId}:' + + `log-group:/aws/lambda/${customFunctionName}:*:*`, + }, + ], + }, + ], + }, + }, + ], + RoleName: { + 'Fn::Join': [ + '-', + [ + awsPackage.serverless.service.service, + awsPackage.provider.getStage(), + { + Ref: 'AWS::Region', + }, + 'lambdaRole', + ], + ], + }, + }, + }); + }); + }); + it('should add custom IAM policy statements', () => { awsPackage.serverless.service.provider.iamRoleStatements = [ { @@ -291,7 +402,7 @@ describe('#mergeIamTemplates()', () => { { Type: 'AWS::Logs::LogGroup', Properties: { - LogGroupName: awsPackage.provider.naming.getLogGroupName(functionName), + LogGroupName: awsPackage.provider.naming.getLogGroupName(resolvedFunctionName), }, } ); @@ -310,7 +421,7 @@ describe('#mergeIamTemplates()', () => { { Type: 'AWS::Logs::LogGroup', Properties: { - LogGroupName: awsPackage.provider.naming.getLogGroupName(functionName), + LogGroupName: awsPackage.provider.naming.getLogGroupName(resolvedFunctionName), RetentionInDays: 5, }, } diff --git a/package-lock.json b/package-lock.json index cd27db017..0e7462712 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "serverless", - "version": "1.45.0", + "version": "1.45.1", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -7521,9 +7521,9 @@ } }, "resolve": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.11.1.tgz", - "integrity": "sha512-vIpgF6wfuJOZI7KKKSP+HmiKggadPQAdsp5HiC1mvqnfp0gF1vdwgBWZIdrVft9pgqoMFQN+R7BSWZiBxx+BBw==", + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.11.0.tgz", + "integrity": "sha512-WL2pBDjqT6pGUNSUzMw00o4T7If+z4H2x3Gz893WoUQ5KW8Vr9txp00ykiP16VBaZF5+j/OcXJHZ9+PCvdiDKw==", "dev": true, "requires": { "path-parse": "^1.0.6" diff --git a/package.json b/package.json index 7e682f05b..05497a91f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "serverless", - "version": "1.45.0", + "version": "1.45.1", "engines": { "node": ">=6.0" },