Repository: serverless/serverless (JavaScript) — Bug Fix

Issue:
Iam Managed Policies not merging when VPC Config selected
<!--
1. If you have a question and not a bug/feature request please ask it at http://forum.serverless.com
2. Please check if an issue already exists so there are no duplicates
3. Check out and follow our Guidelines: https://github.com/serverless/serverless/blob/master/CONTRIBUTING.md
4. Fill out the whole template so we have a good overview on the issue
5. Do not remove any section of the template. If something is not applicable leave it empty but leave it in the Issue
6. Please follow the template, otherwise we'll have to ask you to update it
-->

# This is a (Bug Report / Feature Proposal)

## Description
For bug reports:
* What went wrong?
When providing an iamManagedPolicies under the provider section along with VPC config, the managed policy associated to setting VPC config overwrites any added iamManagedPolicies.
* What did you expect should have happened?
AWSLambdaVPCAccessExecutionRole should be merged into any added iamManagedPolicies
* What was the config you used?
VPCConfig
* What stacktrace or error message from your provider did you see?
N/A

Similar or dependent issues:


## Additional Data

* ***Serverless Framework Version you're using***:
1.26
* ***Operating System***:
ALL
* ***Stack Trace***:
N/A
* ***Provider Error messages***:
N/A
Iam Managed Policies not merging when VPC Config selected
<!--
1. If you have a question and not a bug/feature request please ask it at http://forum.serverless.com
2. Please check if an issue already exists so there are no duplicates
3. Check out and follow our Guidelines: https://github.com/serverless/serverless/blob/master/CONTRIBUTING.md
4. Fill out the whole template so we have a good overview on the issue
5. Do not remove any section of the template. If something is not applicable leave it empty but leave it in the Issue
6. Please follow the template, otherwise we'll have to ask you to update it
-->

# This is a (Bug Report / Feature Proposal)

## Description
For bug reports:
* What went wrong?
When providing an iamManagedPolicies under the provider section along with VPC config, the managed policy associated to setting VPC config overwrites any added iamManagedPolicies.
* What did you expect should have happened?
AWSLambdaVPCAccessExecutionRole should be merged into any added iamManagedPolicies
* What was the config you used?
VPCConfig
* What stacktrace or error message from your provider did you see?
N/A

Similar or dependent issues:


## Additional Data

* ***Serverless Framework Version you're using***:
1.26
* ***Operating System***:
ALL
* ***Stack Trace***:
N/A
* ***Provider Error messages***:
N/A


Discussion:

Resolving patch:
diff --git a/lib/plugins/aws/package/lib/mergeIamTemplates.js b/lib/plugins/aws/package/lib/mergeIamTemplates.js
index f6db9ab09f7..0f28c782619 100644
--- a/lib/plugins/aws/package/lib/mergeIamTemplates.js
+++ b/lib/plugins/aws/package/lib/mergeIamTemplates.js
@@ -128,14 +128,8 @@ module.exports = {
     if (this.serverless.service.provider.iamManagedPolicies) {
       // add iam managed policies
       const iamManagedPolicies = this.serverless.service.provider.iamManagedPolicies;
-      const resource = this.serverless.service.provider.compiledCloudFormationTemplate
-        .Resources[this.provider.naming.getRoleLogicalId()].Properties;
       if (iamManagedPolicies.length > 0) {
-        if (!_.has(resource, 'ManagedPolicyArns') || _.isEmpty(resource.ManagedPolicyArns)) {
-          resource.ManagedPolicyArns = [];
-        }
-        resource.ManagedPolicyArns = resource.ManagedPolicyArns
-          .concat(iamManagedPolicies);
+        this.mergeManagedPolicies(iamManagedPolicies);
       }
     }
 
@@ -150,23 +144,30 @@ module.exports = {
 
     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 = [{
-          'Fn::Join': ['',
-            [
-              'arn:',
-              { Ref: 'AWS::Partition' },
-              ':iam::aws:policy/service-role/AWSLambdaVPCAccessExecutionRole',
-            ],
+      this.mergeManagedPolicies([{
+        'Fn::Join': ['',
+          [
+            'arn:',
+            { Ref: 'AWS::Partition' },
+            ':iam::aws:policy/service-role/AWSLambdaVPCAccessExecutionRole',
           ],
-        }];
+        ],
+      }]);
     }
 
     return BbPromise.resolve();
   },
 
+  mergeManagedPolicies(managedPolicies) {
+    const resource = this.serverless.service.provider.compiledCloudFormationTemplate
+        .Resources[this.provider.naming.getRoleLogicalId()]
+        .Properties;
+    if (!_.has(resource, 'ManagedPolicyArns') || _.isEmpty(resource.ManagedPolicyArns)) {
+      resource.ManagedPolicyArns = [];
+    }
+    resource.ManagedPolicyArns = resource.ManagedPolicyArns.concat(managedPolicies);
+  },
+
   validateStatements(statements) {
     // Verify that iamRoleStatements (if present) is an array of { Effect: ...,
     // Action: ..., Resource: ... } objects.


Test patch:
diff --git a/lib/plugins/aws/package/lib/mergeIamTemplates.test.js b/lib/plugins/aws/package/lib/mergeIamTemplates.test.js
index 479efa3b24b..1a236a74311 100644
--- a/lib/plugins/aws/package/lib/mergeIamTemplates.test.js
+++ b/lib/plugins/aws/package/lib/mergeIamTemplates.test.js
@@ -171,6 +171,40 @@ describe('#mergeIamTemplates()', () => {
       });
   });
 
+  it('should merge managed policy arns when vpc config supplied', () => {
+    awsPackage.serverless.service.provider.vpc = {
+      securityGroupIds: ['xxx'],
+      subnetIds: ['xxx'],
+    };
+    const iamManagedPolicies = [
+      'some:aws:arn:xxx:*:*',
+      'someOther:aws:arn:xxx:*:*',
+      { 'Fn::Join': [':', ['arn:aws:iam:', { Ref: 'AWSAccountId' }, 'some/path']] },
+    ];
+    awsPackage.serverless.service.provider.iamManagedPolicies = iamManagedPolicies;
+    const expectedManagedPolicyArns = [
+      'some:aws:arn:xxx:*:*',
+      'someOther:aws:arn:xxx:*:*',
+      { 'Fn::Join': [':', ['arn:aws:iam:', { Ref: 'AWSAccountId' }, 'some/path']] },
+      { 'Fn::Join': ['',
+        [
+          'arn:',
+          { Ref: 'AWS::Partition' },
+          ':iam::aws:policy/service-role/AWSLambdaVPCAccessExecutionRole',
+        ],
+      ],
+      },
+    ];
+    return awsPackage.mergeIamTemplates()
+      .then(() => {
+        expect(awsPackage.serverless.service.provider.compiledCloudFormationTemplate
+          .Resources[awsPackage.provider.naming.getRoleLogicalId()]
+          .Properties
+          .ManagedPolicyArns
+        ).to.deep.equal(expectedManagedPolicyArns);
+      });
+  });
+
   it('should throw error if custom IAM policy statements is not an array', () => {
     awsPackage.serverless.service.provider.iamRoleStatements = {
       policy: 'some_value',