Repository: serverless/serverless (JavaScript) — Bug Fix

Issue:
report error when we cannot delete a network interface
So I found out about this from our AWS account manager when I exceeded quota.

Running serverless deploy with the VPC, SecurityGroupIds, and SubnetIds defined seems to create a new network interface on each deploy, but doesn't clean up old, unused network interfaces.    

This is happening in 1.1.0.

Is this a known issue?  



Discussion:
Hi @brianbegygr, does your execution role have `ec2:DetachNetworkInterface` and `ec2:DeleteNetworkInterface` permissions?
Good catch, it did not.  Once it has permissions, it seems to clean up after itself.

However, I would have expected it to report an error when it couldn't do so.  

Is it appropriate to put a feature request in for "report error when we cannot delete a network interface?"


Resolving patch:
diff --git a/.gitignore b/.gitignore
index 266ff9083b4..132ba192e31 100755
--- a/.gitignore
+++ b/.gitignore
@@ -40,6 +40,7 @@ admin.env
 tmp
 .coveralls.yml
 tmpdirs-serverless
+*.zip
 
 # ESLint cache
 .eslintcache
diff --git a/docs/providers/aws/guide/functions.md b/docs/providers/aws/guide/functions.md
index 6c400310638..bb2892b9a4d 100644
--- a/docs/providers/aws/guide/functions.md
+++ b/docs/providers/aws/guide/functions.md
@@ -219,6 +219,10 @@ functions:
 
 Then, when you run `serverless deploy`, VPC configuration will be deployed along with your lambda function.
 
+**VPC IAM permissions**
+
+The Lambda function execution role must have permissions to create, describe and delete [Elastic Network Interfaces](http://docs.aws.amazon.com/AmazonVPC/latest/UserGuide/VPC_ElasticNetworkInterfaces.html) (ENI). When VPC configuration is provided the default AWS `AWSLambdaVPCAccessExecutionRole` will be associated with your Lambda execution role. In case custom roles are provided be sure to include the proper [ManagedPolicyArns](http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-iam-role.html#cfn-iam-role-managepolicyarns). For more information please check [configuring a Lambda Function for Amazon VPC Access](http://docs.aws.amazon.com/lambda/latest/dg/vpc.html)
+
 ## Environment Variables
 
 You can add Environment Variable configuration to a specific function in `serverless.yml` by adding an `environment` object property in the function configuration. This object should contain a key/value collection of string:
diff --git a/docs/providers/aws/guide/iam.md b/docs/providers/aws/guide/iam.md
index 05a74963e8c..d4aaed16460 100644
--- a/docs/providers/aws/guide/iam.md
+++ b/docs/providers/aws/guide/iam.md
@@ -16,7 +16,7 @@ Every AWS Lambda function needs permission to interact with other AWS infrastruc
 
 ## The Default IAM Role
 
-By default, one IAM Role is shared by all of the Lambda functions in your service. An IAM Policy is also created and is attached to that Role.  Also by default, your Lambda functions have permissions to create and write to CloudWatch logs, and if you have specified VPC security groups and subnets for your Functions to use, then the EC2 rights necessary to attach to the VPC via an ENI will be added into the default IAM Policy.
+By default, one IAM Role is shared by all of the Lambda functions in your service. Also by default, your Lambda functions have permission to create and write to CloudWatch logs. When VPC configuration is provided the default AWS `AWSLambdaVPCAccessExecutionRole` will be associated in order to communicate with your VPC resources.
 
 To add specific rights to this service-wide Role, define statements in `provider.iamRoleStatements` which will be merged into the generated policy.  As those statements will be merged into the CloudFormation template, you can use `Join`, `Ref` or any other CloudFormation method or feature.
 
@@ -95,6 +95,9 @@ resources:
                 Service:
                   - lambda.amazonaws.com
               Action: sts:AssumeRole
+        # note that these rights are needed if you want your function to be able to communicate with resources within your vpc
+        ManagedPolicyArns:
+          - arn:aws:iam::aws:policy/service-role/AWSLambdaVPCAccessExecutionRole
         Policies:
           - PolicyName: myPolicyName
             PolicyDocument:
diff --git a/docs/providers/aws/guide/resources.md b/docs/providers/aws/guide/resources.md
index fd221562866..e0466d0306d 100644
--- a/docs/providers/aws/guide/resources.md
+++ b/docs/providers/aws/guide/resources.md
@@ -67,7 +67,6 @@ We're also using the term `normalizedName` or similar terms in this guide. This
 |---                    |---                                                      | ---                           |
 | S3::Bucket            | S3Bucket{normalizedBucketName}                          | S3BucketMybucket              |
 |IAM::Role              | IamRoleLambdaExecution                                  | IamRoleLambdaExecution        |
-|IAM::Policy            | IamPolicyLambdaExecution                                | IamPolicyLambdaExecution      |
 |Lambda::Function       | {normalizedFunctionName}LambdaFunction                  | HelloLambdaFunction           |
 |Lambda::Version        | {normalizedFunctionName}LambdaVersion{sha256}                           | HelloLambdaVersionr3pgoTvv1xT4E4NiCL6JG02fl6vIyi7OS1aW0FwAI |
 |Logs::LogGroup         | {normalizedFunctionName}LogGroup                        | HelloLogGroup                 |
diff --git a/lib/plugins/aws/deploy/compile/events/stream/index.js b/lib/plugins/aws/deploy/compile/events/stream/index.js
index 4ab273e48ed..ec14f4ed1e9 100644
--- a/lib/plugins/aws/deploy/compile/events/stream/index.js
+++ b/lib/plugins/aws/deploy/compile/events/stream/index.js
@@ -87,7 +87,7 @@ class AwsCompileStreamEvents {
               .getStreamLogicalId(functionName, streamType, streamName);
 
             const funcRole = functionObj.role || this.serverless.service.provider.role;
-            let dependsOn = '"IamPolicyLambdaExecution"';
+            let dependsOn = '"IamRoleLambdaExecution"';
             if (funcRole) {
               if ( // check whether the custom role is an ARN
                 typeof funcRole === 'string' &&
@@ -137,6 +137,9 @@ class AwsCompileStreamEvents {
                 ' \'kinesis\'. Please check the docs for more info.',
               ].join('');
               throw new this.serverless.classes
+                .Properties
+                .Policies[0]
+                .PolicyDocument
                 .Error(errorMessage);
             }
 
@@ -151,11 +154,12 @@ class AwsCompileStreamEvents {
 
         // update the PolicyDocument statements (if default policy is used)
         if (this.serverless.service.provider.compiledCloudFormationTemplate
-          .Resources.IamPolicyLambdaExecution) {
+          .Resources.IamRoleLambdaExecution) {
           const statement = this.serverless.service.provider.compiledCloudFormationTemplate
             .Resources
-            .IamPolicyLambdaExecution
+            .IamRoleLambdaExecution
             .Properties
+            .Policies[0]
             .PolicyDocument
             .Statement;
           if (dynamodbStreamStatement.Resource.length) {
diff --git a/lib/plugins/aws/deploy/compile/functions/index.js b/lib/plugins/aws/deploy/compile/functions/index.js
index af68e37d211..e0b409efcfe 100644
--- a/lib/plugins/aws/deploy/compile/functions/index.js
+++ b/lib/plugins/aws/deploy/compile/functions/index.js
@@ -50,7 +50,6 @@ class AwsCompileFunctions {
           // role is the default role generated by the framework
           compiledFunction.Properties.Role = { 'Fn::GetAtt': [role, 'Arn'] };
           compiledFunction.DependsOn = [
-            'IamPolicyLambdaExecution',
             'IamRoleLambdaExecution',
           ];
         } else {
diff --git a/lib/plugins/aws/deploy/lib/iam-policy-lambda-execution-template.json b/lib/plugins/aws/deploy/lib/iam-policy-lambda-execution-template.json
deleted file mode 100644
index d5f9775e6d1..00000000000
--- a/lib/plugins/aws/deploy/lib/iam-policy-lambda-execution-template.json
+++ /dev/null
@@ -1,27 +0,0 @@
-{
-  "Type": "AWS::IAM::Policy",
-  "DependsOn": [
-    "IamRoleLambdaExecution"
-  ],
-  "Properties": {
-    "PolicyName": "",
-    "PolicyDocument": {
-      "Version": "2012-10-17",
-      "Statement": [
-        {
-          "Effect": "Allow",
-          "Action": [
-            "logs:CreateLogStream",
-            "logs:PutLogEvents"
-          ],
-          "Resource": []
-        }
-      ]
-    },
-    "Roles": [
-      {
-        "Ref": "[TO BE REPLACED]"
-      }
-    ]
-  }
-}
diff --git a/lib/plugins/aws/deploy/lib/iam-role-lambda-execution-template.json b/lib/plugins/aws/deploy/lib/iam-role-lambda-execution-template.json
index 40606fec533..d75bf0450f2 100644
--- a/lib/plugins/aws/deploy/lib/iam-role-lambda-execution-template.json
+++ b/lib/plugins/aws/deploy/lib/iam-role-lambda-execution-template.json
@@ -16,6 +16,30 @@
           ]
         }
       ]
-    }
+    },
+    "Policies": [
+      {
+        "PolicyName": "[TO BE REPLACED]",
+        "PolicyDocument": {
+          "Version": "2012-10-17",
+          "Statement": [
+            {
+              "Effect": "Allow",
+              "Action": [
+                "logs:CreateLogStream"
+              ],
+              "Resource": []
+            },
+            {
+              "Effect": "Allow",
+              "Action": [
+                "logs:PutLogEvents"
+              ],
+              "Resource": []
+            }
+          ]
+        }
+      }
+    ]
   }
 }
diff --git a/lib/plugins/aws/deploy/lib/mergeIamTemplates.js b/lib/plugins/aws/deploy/lib/mergeIamTemplates.js
index ec158b4f766..d7a564ec57a 100644
--- a/lib/plugins/aws/deploy/lib/mergeIamTemplates.js
+++ b/lib/plugins/aws/deploy/lib/mergeIamTemplates.js
@@ -11,6 +11,12 @@ module.exports = {
   },
 
   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
@@ -27,96 +33,97 @@ module.exports = {
         newLogGroup);
     });
 
-    if (!this.serverless.service.getAllFunctions().length) {
+    // resolve early if provider level role is provided
+    if ('role' in this.serverless.service.provider) {
       return BbPromise.resolve();
     }
 
-    let anyFunctionHasNoRole = false;
-    if (!('role' in this.serverless.service.provider)) {
-      this.serverless.service.getAllFunctions().forEach((functionName) => {
-        const functionObject = this.serverless.service.getFunction(functionName);
-        if (!('role' in functionObject)) {
-          anyFunctionHasNoRole = true;
-        }
-      });
+    // 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();
     }
-    if (!anyFunctionHasNoRole) return BbPromise.resolve();
-
-    if (typeof this.serverless.service.provider.role !== 'string') {
-      // merge in the iamRoleLambdaTemplate
-      const iamRoleLambdaExecutionTemplate = this.serverless.utils.readFileSync(
-        path.join(this.serverless.config.serverlessPath,
-          'plugins',
-          'aws',
-          'deploy',
-          'lib',
-          'iam-role-lambda-execution-template.json')
-      );
-      iamRoleLambdaExecutionTemplate.Properties.Path = this.provider.naming.getRolePath();
-      iamRoleLambdaExecutionTemplate.Properties.RoleName = this.provider.naming.getRoleName();
-
-      _.merge(
-        this.serverless.service.provider.compiledCloudFormationTemplate.Resources,
-        {
-          [this.provider.naming.getRoleLogicalId()]: iamRoleLambdaExecutionTemplate,
-        }
-      );
-
-      // merge in the iamPolicyLambdaTemplate
-      const iamPolicyLambdaExecutionTemplate = this.serverless.utils.readFileSync(
-        path.join(this.serverless.config.serverlessPath,
-          'plugins',
-          'aws',
-          'deploy',
-          'lib',
-          'iam-policy-lambda-execution-template.json')
-      );
-
-      // set the necessary variables for the IamPolicyLambda
-      iamPolicyLambdaExecutionTemplate.Properties.PolicyName = this.provider.naming.getPolicyName();
-      iamPolicyLambdaExecutionTemplate.Properties.Roles[0].Ref = this.provider.naming
-        .getRoleLogicalId();
-
-      _.merge(
-        this.serverless.service.provider.compiledCloudFormationTemplate.Resources,
-        {
-          [this.provider.naming.getPolicyLogicalId()]: iamPolicyLambdaExecutionTemplate,
-        }
-      );
-
-      this.serverless.service.getAllFunctions().forEach((functionName) => {
-        const logGroupLogicalId = this.provider.naming
-          .getLogGroupLogicalId(functionName);
-
-        this.serverless.service.provider.compiledCloudFormationTemplate
-          .Resources[this.provider.naming.getPolicyLogicalId()]
-          .Properties
-          .PolicyDocument
-          .Statement[0]
-          .Resource
-          .push({
-            'Fn::Join': [
-              ':',
-              [
-                { 'Fn::GetAtt': [`${logGroupLogicalId}`, 'Arn'] },
-                '*',
-              ],
-            ],
-          });
-      });
 
-      if (this.serverless.service.provider.iamRoleStatements) {
-        // add custom iam role statements
-        this.serverless.service.provider.compiledCloudFormationTemplate
-          .Resources[this.provider.naming.getPolicyLogicalId()]
-          .Properties
-          .PolicyDocument
-          .Statement = this.serverless.service.provider.compiledCloudFormationTemplate
-          .Resources[this.provider.naming.getPolicyLogicalId()]
+    // merge in the iamRoleLambdaTemplate
+    const iamRoleLambdaExecutionTemplate = this.serverless.utils.readFileSync(
+      path.join(this.serverless.config.serverlessPath,
+        'plugins',
+        'aws',
+        'deploy',
+        '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 logGroupLogicalId = this.provider.naming
+        .getLogGroupLogicalId(functionName);
+
+      this.serverless.service.provider.compiledCloudFormationTemplate
+        .Resources[this.provider.naming.getRoleLogicalId()]
+        .Properties
+        .Policies[0]
+        .PolicyDocument
+        .Statement[0]
+        .Resource
+        .push({ 'Fn::GetAtt': [logGroupLogicalId, 'Arn'] });
+
+      this.serverless.service.provider.compiledCloudFormationTemplate
+        .Resources[this.provider.naming.getRoleLogicalId()]
+        .Properties
+        .Policies[0]
+        .PolicyDocument
+        .Statement[1]
+        .Resource
+        .push({ 'Fn::Join': [':', [{ 'Fn::GetAtt': [logGroupLogicalId, 'Arn'] }, '*']] });
+    });
+
+    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();
@@ -154,4 +161,3 @@ module.exports = {
     }
   },
 };
-
diff --git a/lib/plugins/aws/lib/naming.js b/lib/plugins/aws/lib/naming.js
index 4a277944cf2..cb9b89d201e 100644
--- a/lib/plugins/aws/lib/naming.js
+++ b/lib/plugins/aws/lib/naming.js
@@ -84,9 +84,6 @@ module.exports = {
       ],
     };
   },
-  getPolicyLogicalId() {
-    return 'IamPolicyLambdaExecution';
-  },
 
   // Log Group
   getLogGroupLogicalId(functionName) {


Test patch:
diff --git a/lib/plugins/aws/deploy/compile/events/stream/index.test.js b/lib/plugins/aws/deploy/compile/events/stream/index.test.js
index de378c0f464..d6a6f38ef91 100644
--- a/lib/plugins/aws/deploy/compile/events/stream/index.test.js
+++ b/lib/plugins/aws/deploy/compile/events/stream/index.test.js
@@ -13,11 +13,15 @@ describe('AwsCompileStreamEvents', () => {
     serverless = new Serverless();
     serverless.service.provider.compiledCloudFormationTemplate = {
       Resources: {
-        IamPolicyLambdaExecution: {
+        IamRoleLambdaExecution: {
           Properties: {
-            PolicyDocument: {
-              Statement: [],
-            },
+            Policies: [
+              {
+                PolicyDocument: {
+                  Statement: [],
+                },
+              },
+            ],
           },
         },
       },
@@ -91,15 +95,15 @@ describe('AwsCompileStreamEvents', () => {
         },
       };
 
-      // pretend that the default IamPolicyLambdaExecution is not in place
+      // pretend that the default IamRoleLambdaExecution is not in place
       awsCompileStreamEvents.serverless.service.provider
         .compiledCloudFormationTemplate.Resources
-        .IamPolicyLambdaExecution = null;
+        .IamRoleLambdaExecution = null;
 
       expect(() => { awsCompileStreamEvents.compileStreamEvents(); }).to.not.throw(Error);
       expect(awsCompileStreamEvents.serverless.service.provider
         .compiledCloudFormationTemplate.Resources
-        .IamPolicyLambdaExecution
+        .IamRoleLambdaExecution
       ).to.equal(null);
     });
 
@@ -116,10 +120,10 @@ describe('AwsCompileStreamEvents', () => {
         },
       };
 
-      // pretend that the default IamPolicyLambdaExecution is not in place
+      // pretend that the default IamRoleLambdaExecution is not in place
       awsCompileStreamEvents.serverless.service.provider
         .compiledCloudFormationTemplate.Resources
-        .IamPolicyLambdaExecution = null;
+        .IamRoleLambdaExecution = null;
 
       expect(() => { awsCompileStreamEvents.compileStreamEvents(); }).to.not.throw(Error);
       expect(awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
@@ -128,7 +132,7 @@ describe('AwsCompileStreamEvents', () => {
         .Resources.FirstEventSourceMappingDynamodbFoo.DependsOn.length).to.equal(0);
       expect(awsCompileStreamEvents.serverless.service.provider
         .compiledCloudFormationTemplate.Resources
-        .IamPolicyLambdaExecution
+        .IamRoleLambdaExecution
       ).to.equal(null);
     });
 
@@ -146,15 +150,15 @@ describe('AwsCompileStreamEvents', () => {
         },
       };
 
-      // pretend that the default IamPolicyLambdaExecution is not in place
+      // pretend that the default IamRoleLambdaExecution is not in place
       awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
-        .Resources.IamPolicyLambdaExecution = null;
+        .Resources.IamRoleLambdaExecution = null;
 
       expect(() => { awsCompileStreamEvents.compileStreamEvents(); }).to.not.throw(Error);
       expect(awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
         .Resources.FirstEventSourceMappingDynamodbFoo.DependsOn).to.equal(roleLogicalId);
       expect(awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
-        .Resources.IamPolicyLambdaExecution).to.equal(null);
+        .Resources.IamRoleLambdaExecution).to.equal(null);
     });
 
     it('should not throw error if custom IAM role is set in provider', () => {
@@ -169,10 +173,10 @@ describe('AwsCompileStreamEvents', () => {
         },
       };
 
-      // pretend that the default IamPolicyLambdaExecution is not in place
+      // pretend that the default IamRoleLambdaExecution is not in place
       awsCompileStreamEvents.serverless.service.provider
         .compiledCloudFormationTemplate.Resources
-        .IamPolicyLambdaExecution = null;
+        .IamRoleLambdaExecution = null;
 
       awsCompileStreamEvents.serverless.service.provider
         .role = 'arn:aws:iam::account:role/foo';
@@ -184,7 +188,7 @@ describe('AwsCompileStreamEvents', () => {
         .Resources.FirstEventSourceMappingDynamodbFoo.DependsOn.length).to.equal(0);
       expect(awsCompileStreamEvents.serverless.service.provider
         .compiledCloudFormationTemplate.Resources
-        .IamPolicyLambdaExecution
+        .IamRoleLambdaExecution
       ).to.equal(null);
     });
 
@@ -201,9 +205,9 @@ describe('AwsCompileStreamEvents', () => {
         },
       };
 
-      // pretend that the default IamPolicyLambdaExecution is not in place
+      // pretend that the default IamRoleLambdaExecution is not in place
       awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
-        .Resources.IamPolicyLambdaExecution = null;
+        .Resources.IamRoleLambdaExecution = null;
 
       awsCompileStreamEvents.serverless.service.provider
         .role = { 'Fn::GetAtt': [roleLogicalId, 'Arn'] };
@@ -212,7 +216,7 @@ describe('AwsCompileStreamEvents', () => {
       expect(awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
         .Resources.FirstEventSourceMappingDynamodbFoo.DependsOn).to.equal(roleLogicalId);
       expect(awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
-        .Resources.IamPolicyLambdaExecution).to.equal(null);
+        .Resources.IamRoleLambdaExecution).to.equal(null);
     });
 
     describe('when a DynamoDB stream ARN is given', () => {
@@ -250,7 +254,7 @@ describe('AwsCompileStreamEvents', () => {
         expect(awsCompileStreamEvents.serverless.service
           .provider.compiledCloudFormationTemplate.Resources.FirstEventSourceMappingDynamodbFoo
           .DependsOn
-        ).to.equal('IamPolicyLambdaExecution');
+        ).to.equal('IamRoleLambdaExecution');
         expect(awsCompileStreamEvents.serverless.service
           .provider.compiledCloudFormationTemplate.Resources.FirstEventSourceMappingDynamodbFoo
           .Properties.EventSourceArn
@@ -285,7 +289,7 @@ describe('AwsCompileStreamEvents', () => {
         expect(awsCompileStreamEvents.serverless.service
           .provider.compiledCloudFormationTemplate.Resources.FirstEventSourceMappingDynamodbBar
           .DependsOn
-        ).to.equal('IamPolicyLambdaExecution');
+        ).to.equal('IamRoleLambdaExecution');
         expect(awsCompileStreamEvents.serverless.service
           .provider.compiledCloudFormationTemplate.Resources.FirstEventSourceMappingDynamodbBar
           .Properties.EventSourceArn
@@ -314,7 +318,7 @@ describe('AwsCompileStreamEvents', () => {
         expect(awsCompileStreamEvents.serverless.service
           .provider.compiledCloudFormationTemplate.Resources.FirstEventSourceMappingDynamodbBaz
           .DependsOn
-        ).to.equal('IamPolicyLambdaExecution');
+        ).to.equal('IamRoleLambdaExecution');
         expect(awsCompileStreamEvents.serverless.service
           .provider.compiledCloudFormationTemplate.Resources.FirstEventSourceMappingDynamodbBaz
           .Properties.EventSourceArn
@@ -370,7 +374,7 @@ describe('AwsCompileStreamEvents', () => {
 
         expect(awsCompileStreamEvents.serverless.service.provider
           .compiledCloudFormationTemplate.Resources
-          .IamPolicyLambdaExecution.Properties
+          .IamRoleLambdaExecution.Properties.Policies[0]
           .PolicyDocument.Statement
         ).to.deep.equal(iamRoleStatements);
       });
@@ -411,7 +415,7 @@ describe('AwsCompileStreamEvents', () => {
         expect(awsCompileStreamEvents.serverless.service
           .provider.compiledCloudFormationTemplate.Resources.FirstEventSourceMappingKinesisFoo
           .DependsOn
-        ).to.equal('IamPolicyLambdaExecution');
+        ).to.equal('IamRoleLambdaExecution');
         expect(awsCompileStreamEvents.serverless.service
           .provider.compiledCloudFormationTemplate.Resources.FirstEventSourceMappingKinesisFoo
           .Properties.EventSourceArn
@@ -446,7 +450,7 @@ describe('AwsCompileStreamEvents', () => {
         expect(awsCompileStreamEvents.serverless.service
           .provider.compiledCloudFormationTemplate.Resources.FirstEventSourceMappingKinesisBar
           .DependsOn
-        ).to.equal('IamPolicyLambdaExecution');
+        ).to.equal('IamRoleLambdaExecution');
         expect(awsCompileStreamEvents.serverless.service
           .provider.compiledCloudFormationTemplate.Resources.FirstEventSourceMappingKinesisBar
           .Properties.EventSourceArn
@@ -475,7 +479,7 @@ describe('AwsCompileStreamEvents', () => {
         expect(awsCompileStreamEvents.serverless.service
           .provider.compiledCloudFormationTemplate.Resources.FirstEventSourceMappingKinesisBaz
           .DependsOn
-        ).to.equal('IamPolicyLambdaExecution');
+        ).to.equal('IamRoleLambdaExecution');
         expect(awsCompileStreamEvents.serverless.service
           .provider.compiledCloudFormationTemplate.Resources.FirstEventSourceMappingKinesisBaz
           .Properties.EventSourceArn
@@ -531,7 +535,7 @@ describe('AwsCompileStreamEvents', () => {
 
         expect(awsCompileStreamEvents.serverless.service.provider
           .compiledCloudFormationTemplate.Resources
-          .IamPolicyLambdaExecution.Properties
+          .IamRoleLambdaExecution.Properties.Policies[0]
           .PolicyDocument.Statement
         ).to.deep.equal(iamRoleStatements);
       });
@@ -546,7 +550,7 @@ describe('AwsCompileStreamEvents', () => {
 
       awsCompileStreamEvents.compileStreamEvents();
 
-      // should be 1 because we've mocked the IamPolicyLambdaExecution above
+      // should be 1 because we've mocked the IamRoleLambdaExecution above
       expect(
         Object.keys(awsCompileStreamEvents.serverless.service.provider
           .compiledCloudFormationTemplate.Resources).length
@@ -565,7 +569,7 @@ describe('AwsCompileStreamEvents', () => {
       expect(
         awsCompileStreamEvents.serverless.service.provider
           .compiledCloudFormationTemplate.Resources
-          .IamPolicyLambdaExecution.Properties
+          .IamRoleLambdaExecution.Properties.Policies[0]
           .PolicyDocument.Statement.length
       ).to.equal(0);
     });
diff --git a/lib/plugins/aws/deploy/compile/functions/index.test.js b/lib/plugins/aws/deploy/compile/functions/index.test.js
index cb38684b667..fce0d6e1dda 100644
--- a/lib/plugins/aws/deploy/compile/functions/index.test.js
+++ b/lib/plugins/aws/deploy/compile/functions/index.test.js
@@ -348,7 +348,6 @@ describe('AwsCompileFunctions', () => {
       const compiledFunction = {
         Type: 'AWS::Lambda::Function',
         DependsOn: [
-          'IamPolicyLambdaExecution',
           'IamRoleLambdaExecution',
         ],
         Properties: {
@@ -374,21 +373,21 @@ describe('AwsCompileFunctions', () => {
       ).to.deep.equal(compiledFunction);
     });
 
-    it('should create a function resource with VPC config', () => {
+    it('should create a function resource with provider level vpc config', () => {
+      awsCompileFunctions.serverless.service.provider.vpc = {
+        securityGroupIds: ['xxx'],
+        subnetIds: ['xxx'],
+      };
+
       awsCompileFunctions.serverless.service.functions = {
         func: {
           handler: 'func.function.handler',
           name: 'new-service-dev-func',
-          vpc: {
-            securityGroupIds: ['xxx'],
-            subnetIds: ['xxx'],
-          },
         },
       };
-      const compliedFunction = {
+      const compiledFunction = {
         Type: 'AWS::Lambda::Function',
         DependsOn: [
-          'IamPolicyLambdaExecution',
           'IamRoleLambdaExecution',
         ],
         Properties: {
@@ -415,13 +414,50 @@ describe('AwsCompileFunctions', () => {
       expect(
         awsCompileFunctions.serverless.service.provider.compiledCloudFormationTemplate
           .Resources.FuncLambdaFunction
-      ).to.deep.equal(compliedFunction);
+      ).to.deep.equal(compiledFunction);
+    });
 
+    it('should create a function resource with function level vpc config', () => {
       awsCompileFunctions.serverless.service.functions = {
         func: {
           handler: 'func.function.handler',
+          name: 'new-service-dev-func',
+          vpc: {
+            securityGroupIds: ['xxx'],
+            subnetIds: ['xxx'],
+          },
         },
       };
+      const compiledFunction = {
+        Type: 'AWS::Lambda::Function',
+        DependsOn: [
+          'IamRoleLambdaExecution',
+        ],
+        Properties: {
+          Code: {
+            S3Key: `${awsCompileFunctions.serverless.service.package.artifactDirectoryName}/${
+              awsCompileFunctions.serverless.service.package.artifact}`,
+            S3Bucket: { Ref: 'ServerlessDeploymentBucket' },
+          },
+          FunctionName: 'new-service-dev-func',
+          Handler: 'func.function.handler',
+          MemorySize: 1024,
+          Role: { 'Fn::GetAtt': ['IamRoleLambdaExecution', 'Arn'] },
+          Runtime: 'nodejs4.3',
+          Timeout: 6,
+          VpcConfig: {
+            SecurityGroupIds: ['xxx'],
+            SubnetIds: ['xxx'],
+          },
+        },
+      };
+
+      awsCompileFunctions.compileFunctions();
+
+      expect(
+        awsCompileFunctions.serverless.service.provider.compiledCloudFormationTemplate
+          .Resources.FuncLambdaFunction
+      ).to.deep.equal(compiledFunction);
     });
 
     it('should create a function resource with environment config', () => {
@@ -440,10 +476,9 @@ describe('AwsCompileFunctions', () => {
         providerTest1: 'providerTest1',
       };
 
-      const compliedFunction = {
+      const compiledFunction = {
         Type: 'AWS::Lambda::Function',
         DependsOn: [
-          'IamPolicyLambdaExecution',
           'IamRoleLambdaExecution',
         ],
         Properties: {
@@ -473,13 +508,7 @@ describe('AwsCompileFunctions', () => {
       expect(
         awsCompileFunctions.serverless.service.provider.compiledCloudFormationTemplate
           .Resources.FuncLambdaFunction
-      ).to.deep.equal(compliedFunction);
-
-      awsCompileFunctions.serverless.service.functions = {
-        func: {
-          handler: 'func.function.handler',
-        },
-      };
+      ).to.deep.equal(compiledFunction);
     });
 
     it('should create a function resource with function level environment config', () => {
@@ -493,10 +522,9 @@ describe('AwsCompileFunctions', () => {
         },
       };
 
-      const compliedFunction = {
+      const compiledFunction = {
         Type: 'AWS::Lambda::Function',
         DependsOn: [
-          'IamPolicyLambdaExecution',
           'IamRoleLambdaExecution',
         ],
         Properties: {
@@ -524,13 +552,7 @@ describe('AwsCompileFunctions', () => {
       expect(
         awsCompileFunctions.serverless.service.provider.compiledCloudFormationTemplate
           .Resources.FuncLambdaFunction
-      ).to.deep.equal(compliedFunction);
-
-      awsCompileFunctions.serverless.service.functions = {
-        func: {
-          handler: 'func.function.handler',
-        },
-      };
+      ).to.deep.equal(compiledFunction);
     });
 
     it('should create a function resource with provider level environment config', () => {
@@ -545,10 +567,9 @@ describe('AwsCompileFunctions', () => {
         providerTest1: 'providerTest1',
       };
 
-      const compliedFunction = {
+      const compiledFunction = {
         Type: 'AWS::Lambda::Function',
         DependsOn: [
-          'IamPolicyLambdaExecution',
           'IamRoleLambdaExecution',
         ],
         Properties: {
@@ -576,13 +597,7 @@ describe('AwsCompileFunctions', () => {
       expect(
         awsCompileFunctions.serverless.service.provider.compiledCloudFormationTemplate
           .Resources.FuncLambdaFunction
-      ).to.deep.equal(compliedFunction);
-
-      awsCompileFunctions.serverless.service.functions = {
-        func: {
-          handler: 'func.function.handler',
-        },
-      };
+      ).to.deep.equal(compiledFunction);
     });
 
     it('should overwrite a provider level environment config when function config is given', () => {
@@ -600,10 +615,9 @@ describe('AwsCompileFunctions', () => {
         },
       };
 
-      const compliedFunction = {
+      const compiledFunction = {
         Type: 'AWS::Lambda::Function',
         DependsOn: [
-          'IamPolicyLambdaExecution',
           'IamRoleLambdaExecution',
         ],
         Properties: {
@@ -631,13 +645,7 @@ describe('AwsCompileFunctions', () => {
       expect(
         awsCompileFunctions.serverless.service.provider.compiledCloudFormationTemplate
           .Resources.FuncLambdaFunction
-      ).to.deep.equal(compliedFunction);
-
-      awsCompileFunctions.serverless.service.functions = {
-        func: {
-          handler: 'func.function.handler',
-        },
-      };
+      ).to.deep.equal(compiledFunction);
     });
 
     it('should throw an error if environment variable has invalid name', () => {
@@ -664,10 +672,9 @@ describe('AwsCompileFunctions', () => {
           timeout: 10,
         },
       };
-      const compliedFunction = {
+      const compiledFunction = {
         Type: 'AWS::Lambda::Function',
         DependsOn: [
-          'IamPolicyLambdaExecution',
           'IamRoleLambdaExecution',
         ],
         Properties: {
@@ -690,7 +697,7 @@ describe('AwsCompileFunctions', () => {
       expect(
         awsCompileFunctions.serverless.service.provider.compiledCloudFormationTemplate
           .Resources.FuncLambdaFunction
-      ).to.deep.equal(compliedFunction);
+      ).to.deep.equal(compiledFunction);
     });
 
     it('should allow functions to use a different runtime' +
@@ -708,7 +715,6 @@ describe('AwsCompileFunctions', () => {
       const compiledFunction = {
         Type: 'AWS::Lambda::Function',
         DependsOn: [
-          'IamPolicyLambdaExecution',
           'IamRoleLambdaExecution',
         ],
         Properties: {
@@ -743,7 +749,6 @@ describe('AwsCompileFunctions', () => {
       const compiledFunction = {
         Type: 'AWS::Lambda::Function',
         DependsOn: [
-          'IamPolicyLambdaExecution',
           'IamRoleLambdaExecution',
         ],
         Properties: {
@@ -782,7 +787,6 @@ describe('AwsCompileFunctions', () => {
       const compiledFunction = {
         Type: 'AWS::Lambda::Function',
         DependsOn: [
-          'IamPolicyLambdaExecution',
           'IamRoleLambdaExecution',
         ],
         Properties: {
@@ -823,7 +827,6 @@ describe('AwsCompileFunctions', () => {
       const compiledFunction = {
         Type: 'AWS::Lambda::Function',
         DependsOn: [
-          'IamPolicyLambdaExecution',
           'IamRoleLambdaExecution',
         ],
         Properties: {
@@ -941,7 +944,6 @@ describe('AwsCompileFunctions', () => {
 
       expect(resource).to.deep.equal({
         DependsOn: [
-          'IamPolicyLambdaExecution',
           role,
         ],
         Properties: {
diff --git a/lib/plugins/aws/deploy/lib/mergeIamTemplates.test.js b/lib/plugins/aws/deploy/lib/mergeIamTemplates.test.js
index 4c599c166f4..7e91610181d 100644
--- a/lib/plugins/aws/deploy/lib/mergeIamTemplates.test.js
+++ b/lib/plugins/aws/deploy/lib/mergeIamTemplates.test.js
@@ -1,6 +1,5 @@
 'use strict';
 
-const path = require('path');
 const expect = require('chai').expect;
 
 const Serverless = require('../../../../Serverless');
@@ -34,7 +33,7 @@ describe('#mergeIamTemplates()', () => {
     };
   });
 
-  it('should not merge there are no functions', () => {
+  it('should not merge if there are no functions', () => {
     awsDeploy.serverless.service.functions = {};
 
     return awsDeploy.mergeIamTemplates()
@@ -42,64 +41,98 @@ describe('#mergeIamTemplates()', () => {
         const resources = awsDeploy.serverless.service.provider
           .compiledCloudFormationTemplate.Resources;
 
-        expect(resources[awsDeploy.provider.naming.getRoleLogicalId()]).to.equal(undefined);
-        expect(resources[awsDeploy.provider.naming.getPolicyLogicalId()]).to.equal(undefined);
+        return expect(
+          resources[awsDeploy.provider.naming.getRoleLogicalId()]
+        ).to.not.exist;
       });
   });
 
-  it('should merge the IamRoleLambdaExecution template into the CloudFormation template', () => {
-    const IamRoleLambdaExecutionTemplate = awsDeploy.serverless.utils.readFileSync(
-      path.join(
-        __dirname,
-        '..',
-        'lib',
-        'iam-role-lambda-execution-template.json'
-      )
-    );
-    IamRoleLambdaExecutionTemplate.Properties.Path = awsDeploy.provider.naming.getRolePath();
-    IamRoleLambdaExecutionTemplate.Properties.RoleName = awsDeploy.provider.naming.getRoleName();
-
-    return awsDeploy.mergeIamTemplates()
-      .then(() => {
-        expect(awsDeploy.serverless.service.provider.compiledCloudFormationTemplate
-          .Resources[awsDeploy.provider.naming.getRoleLogicalId()]
-        ).to.deep.equal(
-          IamRoleLambdaExecutionTemplate
-        );
-      });
-  });
-
-  it('should merge IamPolicyLambdaExecution template into the CloudFormation template',
+  it('should merge the IamRoleLambdaExecution template into the CloudFormation template',
     () => awsDeploy.mergeIamTemplates()
       .then(() => {
-        // we check for the type here because a deep equality check will error out due to
-        // the updates which are made after the merge (they are tested in a separate test)
+        const normalizedName = awsDeploy.provider.naming.getLogGroupLogicalId(functionName);
         expect(awsDeploy.serverless.service.provider.compiledCloudFormationTemplate
-          .Resources[awsDeploy.provider.naming.getPolicyLogicalId()]
-          .Type
-        ).to.deep.equal('AWS::IAM::Policy');
-      })
-  );
-
-  it('should update the necessary variables for the IamPolicyLambdaExecution',
-    () => awsDeploy.mergeIamTemplates()
-      .then(() => {
-        expect(awsDeploy.serverless.service.provider.compiledCloudFormationTemplate
-          .Resources[awsDeploy.provider.naming.getPolicyLogicalId()]
-          .Properties
-          .PolicyName
-        ).to.eql(
-          {
-            'Fn::Join': [
-              '-',
-              [
-                awsDeploy.options.stage,
-                awsDeploy.serverless.service.service,
-                'lambda',
+          .Resources[awsDeploy.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': [
+                    '-',
+                    [
+                      awsDeploy.options.stage,
+                      awsDeploy.serverless.service.service,
+                      'lambda',
+                    ],
+                  ],
+                },
+                PolicyDocument: {
+                  Version: '2012-10-17',
+                  Statement: [
+                    {
+                      Effect: 'Allow',
+                      Action: [
+                        'logs:CreateLogStream',
+                      ],
+                      Resource: [
+                        {
+                          'Fn::GetAtt': [normalizedName, 'Arn'],
+                        },
+                      ],
+                    },
+                    {
+                      Effect: 'Allow',
+                      Action: [
+                        'logs:PutLogEvents',
+                      ],
+                      Resource: [
+                        {
+                          'Fn::Join': [
+                            ':',
+                            [
+                              { 'Fn::GetAtt': [normalizedName, 'Arn'] },
+                              '*',
+                            ],
+                          ],
+                        },
+                      ],
+                    },
+                  ],
+                },
+              },
             ],
-          }
-        );
+            RoleName: {
+              'Fn::Join': [
+                '-',
+                [
+                  awsDeploy.serverless.service.service,
+                  awsDeploy.options.stage,
+                  awsDeploy.options.region,
+                  'lambdaRole',
+                ],
+              ],
+            },
+          },
+        });
       })
   );
 
@@ -117,10 +150,11 @@ describe('#mergeIamTemplates()', () => {
     return awsDeploy.mergeIamTemplates()
       .then(() => {
         expect(awsDeploy.serverless.service.provider.compiledCloudFormationTemplate
-          .Resources[awsDeploy.provider.naming.getPolicyLogicalId()]
+          .Resources[awsDeploy.provider.naming.getRoleLogicalId()]
           .Properties
+          .Policies[0]
           .PolicyDocument
-          .Statement[1]
+          .Statement[2]
         ).to.deep.equal(awsDeploy.serverless.service.provider.iamRoleStatements[0]);
       });
   });
@@ -210,28 +244,6 @@ describe('#mergeIamTemplates()', () => {
 
   it('should add a CloudWatch LogGroup resource if all functions use custom roles', () => {
     awsDeploy.serverless.service.functions[functionName].role = 'something';
-    const normalizedName = awsDeploy.provider.naming.getLogGroupLogicalId(functionName);
-    return awsDeploy.mergeIamTemplates().then(() => {
-      expect(awsDeploy.serverless.service.provider.compiledCloudFormationTemplate
-        .Resources[normalizedName]
-      ).to.deep.equal(
-        {
-          Type: 'AWS::Logs::LogGroup',
-          Properties: {
-            LogGroupName: awsDeploy.provider.naming.getLogGroupName(functionName),
-          },
-        }
-      );
-
-      const roleLogicalId = awsDeploy.provider.naming.getRoleLogicalId();
-      expect(awsDeploy.serverless.service.provider.compiledCloudFormationTemplate
-        .Resources[roleLogicalId]
-      ).to.equal(undefined);
-      delete awsDeploy.serverless.service.functions[functionName].role;
-    });
-  });
-
-  it('should update IamPolicyLambdaExecution with a logging resource for the function', () => {
     awsDeploy.serverless.service.functions = {
       func0: {
         handler: 'func.function.handler',
@@ -271,20 +283,29 @@ describe('#mergeIamTemplates()', () => {
     });
   });
 
-  it('should update IamPolicyLambdaExecution with a logging resource for the function', () => {
+  it('should update IamRoleLambdaExecution with a logging resource for the function', () => {
     const normalizedName = awsDeploy.provider.naming.getLogGroupLogicalId(functionName);
     return awsDeploy.mergeIamTemplates().then(() => {
       expect(awsDeploy.serverless.service.provider.compiledCloudFormationTemplate
-        .Resources[awsDeploy.provider.naming.getPolicyLogicalId()]
+        .Resources[awsDeploy.provider.naming.getRoleLogicalId()]
         .Properties
+        .Policies[0]
         .PolicyDocument
         .Statement[0]
         .Resource
+      ).to.deep.equal([{ 'Fn::GetAtt': [normalizedName, 'Arn'] }]);
+      expect(awsDeploy.serverless.service.provider.compiledCloudFormationTemplate
+        .Resources[awsDeploy.provider.naming.getRoleLogicalId()]
+        .Properties
+        .Policies[0]
+        .PolicyDocument
+        .Statement[1]
+        .Resource
       ).to.deep.equal([{ 'Fn::Join': [':', [{ 'Fn::GetAtt': [normalizedName, 'Arn'] }, '*']] }]);
     });
   });
 
-  it('should update IamPolicyLambdaExecution with each function\'s logging resources', () => {
+  it('should update IamRoleLambdaExecution with each function\'s logging resources', () => {
     awsDeploy.serverless.service.functions = {
       func0: {
         handler: 'func.function.handler',
@@ -302,11 +323,25 @@ describe('#mergeIamTemplates()', () => {
     ];
     return awsDeploy.mergeIamTemplates().then(() => {
       expect(awsDeploy.serverless.service.provider.compiledCloudFormationTemplate
-        .Resources[awsDeploy.provider.naming.getPolicyLogicalId()]
+        .Resources[awsDeploy.provider.naming.getRoleLogicalId()]
         .Properties
+        .Policies[0]
         .PolicyDocument
         .Statement[0]
         .Resource
+      ).to.deep.equal(
+        [
+          { 'Fn::GetAtt': [normalizedNames[0], 'Arn'] },
+          { 'Fn::GetAtt': [normalizedNames[1], 'Arn'] },
+        ]
+      );
+      expect(awsDeploy.serverless.service.provider.compiledCloudFormationTemplate
+        .Resources[awsDeploy.provider.naming.getRoleLogicalId()]
+        .Properties
+        .Policies[0]
+        .PolicyDocument
+        .Statement[1]
+        .Resource
       ).to.deep.equal(
         [
           { 'Fn::Join': [':', [{ 'Fn::GetAtt': [normalizedNames[0], 'Arn'] }, '*']] },
@@ -316,12 +351,12 @@ describe('#mergeIamTemplates()', () => {
     });
   });
 
-  it('should not add the default role and policy if all functions have an ARN role', () => {
+  it('should add default role if one of the functions has an ARN role', () => {
     awsDeploy.serverless.service.functions = {
       func0: {
         handler: 'func.function.handler',
         name: 'new-service-dev-func0',
-        role: 'some:aws:arn:xx0:*:*',
+        // obtain role from provider
       },
       func1: {
         handler: 'func.function.handler',
@@ -330,57 +365,129 @@ describe('#mergeIamTemplates()', () => {
       },
     };
 
-    awsDeploy.mergeIamTemplates().then(() => {
-      expect(awsDeploy.serverless.service.provider.compiledCloudFormationTemplate
-        .Resources[awsDeploy.provider.naming.getPolicyLogicalId()]
-      ).to.equal(undefined);
-      expect(awsDeploy.serverless.service.provider.compiledCloudFormationTemplate
+    return awsDeploy.mergeIamTemplates()
+      .then(() => expect(awsDeploy.serverless.service.provider.compiledCloudFormationTemplate
         .Resources[awsDeploy.provider.naming.getRoleLogicalId()]
-      ).to.equal(undefined);
-    });
+      ).to.exist
+    );
   });
 
-  it('should not add default role / policy if all functions have an ARN role', () => {
+  it('should not add the default role if role is defined on a provider level', () => {
     awsDeploy.serverless.service.provider.role = 'some:aws:arn:xxx:*:*';
     awsDeploy.serverless.service.functions = {
       func0: {
         handler: 'func.function.handler',
         name: 'new-service-dev-func0',
-        // obtain role from provider
       },
       func1: {
         handler: 'func.function.handler',
         name: 'new-service-dev-func1',
-        role: 'some:aws:arn:xx1:*:*',
       },
     };
 
-    awsDeploy.mergeIamTemplates().then(() => {
-      expect(awsDeploy.serverless.service.provider.compiledCloudFormationTemplate
-        .Resources[awsDeploy.provider.naming.getPolicyLogicalId()]
-      ).to.equal(undefined);
-      expect(awsDeploy.serverless.service.provider.compiledCloudFormationTemplate
+    return awsDeploy.mergeIamTemplates()
+      .then(() => expect(awsDeploy.serverless.service.provider.compiledCloudFormationTemplate
         .Resources[awsDeploy.provider.naming.getRoleLogicalId()]
-      ).to.equal(undefined);
-    });
+      ).to.not.exist);
   });
 
-  it('should not add the IamPolicyLambdaExecution if role is defined on the provider level', () => {
-    awsDeploy.serverless.service.provider.role = 'some:aws:arn:xxx:*:*';
+  it('should not add the default role if all functions have an ARN role', () => {
+    awsDeploy.serverless.service.functions = {
+      func0: {
+        handler: 'func.function.handler',
+        name: 'new-service-dev-func0',
+        role: 'some:aws:arn:xx0:*:*',
+      },
+      func1: {
+        handler: 'func.function.handler',
+        name: 'new-service-dev-func1',
+        role: 'some:aws:arn:xx1:*:*',
+      },
+    };
 
     return awsDeploy.mergeIamTemplates()
       .then(() => expect(awsDeploy.serverless.service.provider.compiledCloudFormationTemplate
-        .Resources[awsDeploy.provider.naming.getPolicyLogicalId()]
-      ).to.not.exist);
+        .Resources[awsDeploy.provider.naming.getRoleLogicalId()]
+      ).to.not.exist
+    );
   });
 
+  describe('ManagedPolicyArns property', () => {
+    it('should not be added by default', () => {
+      awsDeploy.serverless.service.functions = {
+        func0: {
+          handler: 'func.function.handler',
+          name: 'new-service-dev-func0',
+        },
+      };
 
-  it('should not add the IamRoleLambdaExecution if role is defined on the provider level', () => {
-    awsDeploy.serverless.service.provider.role = 'some:aws:arn:xxx:*:*';
+      return awsDeploy.mergeIamTemplates()
+        .then(() => expect(awsDeploy.serverless.service.provider.compiledCloudFormationTemplate
+            .Resources[awsDeploy.provider.naming.getRoleLogicalId()].Properties.ManagedPolicyArns
+          ).to.not.exist
+        );
+    });
 
-    return awsDeploy.mergeIamTemplates()
-      .then(() => expect(awsDeploy.serverless.service.provider.compiledCloudFormationTemplate
-        .Resources[awsDeploy.provider.naming.getRoleLogicalId()]
-      ).to.not.exist);
+    it('should be added if vpc config is defined on a provider level', () => {
+      awsDeploy.serverless.service.provider.vpc = {
+        securityGroupIds: ['xxx'],
+        subnetIds: ['xxx'],
+      };
+
+      return awsDeploy.mergeIamTemplates()
+        .then(() => {
+          expect(awsDeploy.serverless.service.provider.compiledCloudFormationTemplate
+            .Resources[awsDeploy.provider.naming.getRoleLogicalId()].Properties.ManagedPolicyArns
+          ).to.deep.equal([
+            'arn:aws:iam::aws:policy/service-role/AWSLambdaVPCAccessExecutionRole',
+          ]);
+        });
+    });
+
+    it('should be added if vpc config is defined on function level', () => {
+      awsDeploy.serverless.service.functions = {
+        func0: {
+          handler: 'func.function.handler',
+          name: 'new-service-dev-func0',
+        },
+        func1: {
+          handler: 'func.function.handler',
+          name: 'new-service-dev-func1',
+          vpc: {
+            securityGroupIds: ['xxx'],
+            subnetIds: ['xxx'],
+          },
+        },
+      };
+
+      return awsDeploy.mergeIamTemplates()
+        .then(() => {
+          expect(awsDeploy.serverless.service.provider.compiledCloudFormationTemplate
+            .Resources[awsDeploy.provider.naming.getRoleLogicalId()].Properties.ManagedPolicyArns
+          ).to.deep.equal([
+            'arn:aws:iam::aws:policy/service-role/AWSLambdaVPCAccessExecutionRole',
+          ]);
+        });
+    });
+
+    it('should not be added if vpc config is defined with role on function level', () => {
+      awsDeploy.serverless.service.functions = {
+        func1: {
+          handler: 'func.function.handler',
+          name: 'new-service-dev-func1',
+          role: 'some:aws:arn:xx1:*:*',
+          vpc: {
+            securityGroupIds: ['xxx'],
+            subnetIds: ['xxx'],
+          },
+        },
+      };
+
+      return awsDeploy.mergeIamTemplates()
+        .then(() => expect(awsDeploy.serverless.service.provider.compiledCloudFormationTemplate
+            .Resources[awsDeploy.provider.naming.getRoleLogicalId()]
+          ).to.not.exist
+        );
+    });
   });
 });
diff --git a/lib/plugins/aws/lib/naming.test.js b/lib/plugins/aws/lib/naming.test.js
index 0e8fbf933b9..87c09ea7987 100644
--- a/lib/plugins/aws/lib/naming.test.js
+++ b/lib/plugins/aws/lib/naming.test.js
@@ -145,12 +145,6 @@ describe('#naming()', () => {
     });
   });
 
-  describe('#getPolicyLogicalId()', () => {
-    it('should return the expected policy name (IamPolicyLambdaExecution)', () => {
-      expect(sdk.naming.getPolicyLogicalId()).to.equal('IamPolicyLambdaExecution');
-    });
-  });
-
   describe('#getLogicalLogGroupName()', () => {
     it('should prefix the normalized function name to "LogGroup"', () => {
       expect(sdk.naming.getLogGroupLogicalId('functionName')).to.equal('FunctionNameLogGroup');