functionalone / serverless-iam-roles-per-function

Serverless Plugin for easily defining IAM roles per function via the use of iamRoleStatements at the function level.
MIT License
412 stars 58 forks source link

TypeError: Cannot read property 'Properties' of undefined #8

Closed PatNeedham closed 6 years ago

PatNeedham commented 6 years ago

In my serverless.yml I have a function that looks like this:

example_function:
    handler: handler.doStuff
    iamRoleStatementsInherit: true
    iamRoleStatements:
      - Effect: "Allow"        
        Action:
          - s3:PutObject
        Resource: arn:aws:s3:::bucket1/*
      - Effect: "Allow"        
        Action:
          - s3:PutObject
        Resource: arn:aws:s3:::bucket2/*
    events:
      - http:
          path: doStuff/{id}
          method: get
          cors: true
          request:
            parameters:
              paths:
                id: true

And plugins section like so:

plugins:
  - serverless-webpack
  - serverless-prune-plugin
  - serverless-iam-roles-per-function

After running SLS_DEBUG=* sls deploy, the output looks like:

Serverless: Invoke webpack:package
Serverless: Package lock found - Using locked versions
Serverless: Packing external modules: source-map-support@^0.5.5, aws-sdk@^2.233.1, babel-runtime@^6.26.0, request@^2.85.0, moment-timezone@^0.5.16, redis@^2.8.0
Serverless: Packaging service...
  Type Error ---------------------------------------------

  Cannot read property 'Properties' of undefined

     For debugging logs, run again after setting the "SLS_DEBUG=*" environment variable.

  Stack Trace --------------------------------------------

TypeError: Cannot read property 'Properties' of undefined
    at ServerlessIamPerFunctionPlugin.createRoleForFunction (/Users/patneedham/.../node_modules/serverless-iam-roles-per-function/dist/lib/index.js:146:25)
    at ServerlessIamPerFunctionPlugin.createRolesPerFunction (/Users/patneedham/.../node_modules/serverless-iam-roles-per-function/dist/lib/index.js:220:18)
    at BbPromise.reduce (/usr/local/lib/node_modules/serverless/lib/classes/PluginManager.js:372:55)
From previous event:
    at PluginManager.invoke (/usr/local/lib/node_modules/serverless/lib/classes/PluginManager.js:372:22)
    at PluginManager.spawn (/usr/local/lib/node_modules/serverless/lib/classes/PluginManager.js:390:17)
    at Deploy.BbPromise.bind.then.then (/usr/local/lib/node_modules/serverless/lib/plugins/deploy/deploy.js:123:50)
From previous event:
    at Object.before:deploy:deploy [as hook] (/usr/local/lib/node_modules/serverless/lib/plugins/deploy/deploy.js:113:10)
    at BbPromise.reduce (/usr/local/lib/node_modules/serverless/lib/classes/PluginManager.js:372:55)
From previous event:
    at PluginManager.invoke (/usr/local/lib/node_modules/serverless/lib/classes/PluginManager.js:372:22)
    at PluginManager.run (/usr/local/lib/node_modules/serverless/lib/classes/PluginManager.js:403:17)
    at variables.populateService.then (/usr/local/lib/node_modules/serverless/lib/Serverless.js:102:33)
    at runCallback (timers.js:757:18)
    at tryOnImmediate (timers.js:718:5)
    at processImmediate [as _immediateCallback] (timers.js:698:5)
    at process.topLevelDomainCallback (domain.js:101:23)
From previous event:
    at Serverless.run (/usr/local/lib/node_modules/serverless/lib/Serverless.js:89:74)
    at serverless.init.then (/usr/local/lib/node_modules/serverless/bin/serverless:42:50)
    at <anonymous>

  Get Support --------------------------------------------
     Docs:          docs.serverless.com
     Bugs:          github.com/serverless/serverless/issues
     Forums:        forum.serverless.com
     Chat:          gitter.im/serverless/serverless

  Your Environment Information -----------------------------
     OS:                     darwin
     Node Version:           9.6.1
     Serverless Version:     1.26.1

Line 151 of dist/lib/index.js is

functionIamRole.Properties.Policies[0].PolicyDocument.Statement = policyStatements;

functionIamRole is being defined a few lines before as

const functionIamRole = lodash_1.default.cloneDeep(globalIamRole);

I added some console.log statements to see the value of globalRoleName (it is IamRoleLambdaExecution) and the keys inside this.serverless.service.provider.compiledCloudFormationTemplate.Resources, which is:

[ 'ServerlessDeploymentBucket',
  'GetUnderscoreitemUnderscorecurrentUnderscorepriceLogGroup',
  'SetUnderscoreitemUnderscorecurrentUnderscorepriceLogGroup',
  'GetUnderscorerestaurantUnderscoreitemsLogGroup',
  'SetUnderscorerestaurantUnderscoreitemsLogGroup',
  'GetUnderscoreitemUnderscorepriceUnderscorehistoryLogGroup',
  'SetUnderscoreitemUnderscorepriceUnderscorehistoryLogGroup',
  'SaveUnderscorepriceUnderscorehistoriesLogGroup',
  'MigrateUnderscorefromUnderscorefirebaseLogGroup',
  'GetUnderscoreitemUnderscorecurrentUnderscorepriceLambdaFunction',
  'GetUnderscoreitemUnderscorecurrentUnderscorepriceLambdaVersionPJd606YxdqlYrJY7maZxj7odx9UyOqdZBkIjXh2IrQ',
  'SetUnderscoreitemUnderscorecurrentUnderscorepriceLambdaFunction',
  'SetUnderscoreitemUnderscorecurrentUnderscorepriceLambdaVersionKY2N6rkRD6JWHIz3bi3WJUQg8iuVtEQgnBB0dLHAEx4',
  'GetUnderscorerestaurantUnderscoreitemsLambdaFunction',
  'GetUnderscorerestaurantUnderscoreitemsLambdaVersioncgZ0uIwf3U6MtYcx0WLUQzerNSpaYsgpZ9veQuhQ',
  'SetUnderscorerestaurantUnderscoreitemsLambdaFunction',
  'SetUnderscorerestaurantUnderscoreitemsLambdaVersion4sXQxEOWZfrjXBgMK9RycnkkNQWOenqFQq9l8lY',
  'GetUnderscoreitemUnderscorepriceUnderscorehistoryLambdaFunction',
  'GetUnderscoreitemUnderscorepriceUnderscorehistoryLambdaVersion8nFoEKThFPH9v2SUt9gTb2495K5tRdTPFVGbfK3lWIg',
  'SetUnderscoreitemUnderscorepriceUnderscorehistoryLambdaFunction',
  'SetUnderscoreitemUnderscorepriceUnderscorehistoryLambdaVersionOvpjQc0zsuXM5heiI5KWmL2ShLq3INL4Bmg69Lw9Qo',
  'SaveUnderscorepriceUnderscorehistoriesLambdaFunction',
  'SaveUnderscorepriceUnderscorehistoriesLambdaVersion8VqpFN3TuXXebCfp4F1WoIkqBDt1hgh6fyegyLDlO1Q',
  'MigrateUnderscorefromUnderscorefirebaseLambdaFunction',
  'MigrateUnderscorefromUnderscorefirebaseLambdaVersionTThMQo8dCKgLGj3WhpLka7w0zpSMfBBxvpBhv75WEO8' ]

which explains why globalIamRole and subsequently functionIamRole ended up as null. The question is, why did that property not appear as a key inside the Resources object? Does the stack trace I provided give any clues as to what might have gone wrong?

I tried adding

custom:
  serverless-iam-roles-per-function:
    defaultInherit: true

to serverless.yml but ended up with the same result.

glicht commented 6 years ago

Thanks for reporting this. I haven't managed to reproduce this with the yaml file you provided.

Are you able to deploy when using the same setup without the plugin (with a global role defined in the top level)?

PatNeedham commented 6 years ago

Yes I was able to deploy when removing the plugin and defining a global role within provider section instead.

I tried with a new serverless project that follows the same format (two functions, each with iamRoleStatements field, iamRoleStatements field within provider, defaultInherit set to true within custom, and including the two additional plugins). Deploying that worked without any error, so I'll close this issue because it probably had something to do with my code and not this library.

Here are the relevant files for this new project I started to test with: serverless.yml:

service: example-role-per-function

custom:
  serverless-iam-roles-per-function:
    defaultInherit: true

provider:
  name: aws
  runtime: nodejs6.10
  iamRoleStatements:
    - Effect: "Allow"
      Action:
        - xray:PutTelemetryRecords
        - xray:PutTraceSegments
      Resource: "*"

plugins:
  - serverless-webpack
  - serverless-prune-plugin
  - serverless-iam-roles-per-function

functions:
  hello:
    handler: handler.hello
    iamRoleStatements:
      - Effect: "Allow"
        Action:
          - s3:PutObject
        Resource: "arn:aws:s3:::my-bucket/*"
  hello2:
    handler: handler.hello2
    iamRoleStatements:
      - Effect: "Allow"
        Action:
          - s3:GetObject
        Resource: "arn:aws:s3:::my-bucket/*"

package.json:

{
  "name": "example-role-per-function",
  "version": "1.0.0",
  "description": "",
  "main": "handler.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "babel-core": "^6.26.3",
    "babel-loader": "^7.1.4",
    "serverless-iam-roles-per-function": "^0.1.5",
    "serverless-prune-plugin": "^1.3.0",
    "serverless-webpack": "^5.1.5",
    "webpack": "^4.8.1",
    "webpack-node-externals": "^1.7.2"
  }
}

handler.js:

'use strict';

module.exports.hello = (event, context, callback) => {
  const response = {
    statusCode: 200,
    body: JSON.stringify({
      message: 'Go Serverless v1.0! Your function executed successfully!',
      input: event,
    }),
  };

  callback(null, response);
};

module.exports.hello2 = (event, context, callback) => {
  const response = {
    statusCode: 200,
    body: JSON.stringify({
      message: 'Gooooooooo Serverless v1.0! Your function executed successfully!',
      input: event,
    }),
  };

  callback(null, response);
};

webpack.config.js:

const slsw = require("serverless-webpack");
const nodeExternals = require("webpack-node-externals");

module.exports = {
  entry: slsw.lib.entries,
  target: "node",
  // Generate sourcemaps for proper error messages
  devtool: 'source-map',
  // Since 'aws-sdk' is not compatible with webpack,
  // we exclude all node dependencies
  externals: [nodeExternals()],
  mode: slsw.lib.webpack.isLocal ? "development" : "production",
  optimization: {
    // We no not want to minimize our code.
    minimize: false
  },
  performance: {
    // Turn off size warnings for entry points
    hints: false
  },
  // Run babel on all .js files and skip those in node_modules
  module: {
    rules: [
      {
        test: /\.js$/,
        loader: "babel-loader",
        include: __dirname,
        exclude: /node_modules/
      }
    ]
  }
};
glicht commented 6 years ago

Thanks for the update. If you do manage to figure out what setup was causing this error, feel free to update this issue or open a new one.

ankkho commented 5 years ago

@glicht I am facing the above issue. Tried @PatNeedham approach but still received same error.

glicht commented 5 years ago

@ankkho I can try to help but this is a weird one as it didn't reproduce. Please post your yaml file and the full output you receive when running sls deploy.

ankkho commented 5 years ago

@glicht Below is my serverless.yml and error log output.

serverless.yml

service: service-name

provider:
  name: aws
  runtime: nodejs8.10
  stage: staging
  profile: some-profile-name
  region: ap-south-1 #Mumbai
  memorySize: 128
  timeout: 60
  cfLogs: true
  tracing: true
  role: arn-for-deployment-role'
  iamRoleStatements:
    - Effect: "Allow"
      Action:
        - xray:PutTelemetryRecords
        - xray:PutTraceSegments
      Resource: "*"
  deploymentBucket:
    serverSideEncryption: AES256

  vpc:
    securityGroupIds:
      - VPCSecurityGroupArn
    subnetIds:
      - PrivateSubnetA_Arn
      - PrivateSubnetB_Arn

package:
  individually: false
  exclude:
    - ./node_modules/**
    - ./scripts/**
    - ./test/**/**
    - ./.eslintrc.json
    - ./.gitlab-ci.yml
    - ./testSchema.js
    - ./cf-table-resource.json
    - ./aws/tmp/**
  include:
    - ./meta.json

plugins:
  - serverless-plugin-tracing
  - serverless-prune-plugin
  - serverless-plugin-scripts
  - serverless-webpack
  - serverless-dynamodb-local
  - serverless-plugin-warmup
  - serverless-offline
  - serverless-iam-roles-per-function

custom:
  serverless-iam-roles-per-function:
    defaultInherit: true

functions:
  functionA:
    warmup: false
    handler: handler.functionA
    iamRoleStatements:
      - Effect: "Allow"
        Action:
          - dynamodb:GetItem
          - dynamodb:PutItem
          - dynamodb:BatchWriteItem
        Resource: "arn:aws:dynamodb:ap-south-1:*:table/table-name"

Error Output from sls deploy:

  Type Error ---------------------------------------------

  Cannot read property 'Properties' of undefined

     For debugging logs, run again after setting the "SLS_DEBUG=*" environment variable.

  Stack Trace --------------------------------------------

TypeError: Cannot read property 'Properties' of undefined
    at ServerlessIamPerFunctionPlugin.createRoleForFunction (/Users/Ankit/projects/personal/sample-project/node_modules/serverless-iam-roles-per-function/src/lib/index.ts:186:21)
    at ServerlessIamPerFunctionPlugin.createRolesPerFunction (/Users/Ankit/projects/personal/sample-project/node_modules/serverless-iam-roles-per-function/src/lib/index.ts:266:12)
    at BbPromise.reduce (/Users/Ankit/.nvm-fish/v8.10.0/lib/node_modules/serverless/lib/classes/PluginManager.js:391:55)
From previous event:
    at PluginManager.invoke (/Users/Ankit/.nvm-fish/v8.10.0/lib/node_modules/serverless/lib/classes/PluginManager.js:391:22)
    at PluginManager.spawn (/Users/Ankit/.nvm-fish/v8.10.0/lib/node_modules/serverless/lib/classes/PluginManager.js:409:17)
    at Deploy.BbPromise.bind.then (/Users/Ankit/.nvm-fish/v8.10.0/lib/node_modules/serverless/lib/plugins/deploy/deploy.js:117:50)
From previous event:
    at Object.before:deploy:deploy [as hook] (/Users/Ankit/.nvm-fish/v8.10.0/lib/node_modules/serverless/lib/plugins/deploy/deploy.js:107:10)
    at BbPromise.reduce (/Users/Ankit/.nvm-fish/v8.10.0/lib/node_modules/serverless/lib/classes/PluginManager.js:391:55)
From previous event:
    at PluginManager.invoke (/Users/Ankit/.nvm-fish/v8.10.0/lib/node_modules/serverless/lib/classes/PluginManager.js:391:22)
    at PluginManager.run (/Users/Ankit/.nvm-fish/v8.10.0/lib/node_modules/serverless/lib/classes/PluginManager.js:422:17)
    at variables.populateService.then.then (/Users/Ankit/.nvm-fish/v8.10.0/lib/node_modules/serverless/lib/Serverless.js:157:33)
    at runCallback (timers.js:794:20)
    at tryOnImmediate (timers.js:752:5)
    at processImmediate [as _immediateCallback] (timers.js:729:5)
From previous event:
    at Serverless.run (/Users/Ankit/.nvm-fish/v8.10.0/lib/node_modules/serverless/lib/Serverless.js:144:8)
    at serverless.init.then (/Users/Ankit/.nvm-fish/v8.10.0/lib/node_modules/serverless/bin/serverless:44:28)
    at <anonymous>

  Get Support --------------------------------------------
     Docs:          docs.serverless.com
     Bugs:          github.com/serverless/serverless/issues
     Issues:        forum.serverless.com

  Your Environment Information -----------------------------
     OS:                     darwin
     Node Version:           8.10.0
     Serverless Version:     1.35.1
glicht commented 5 years ago

The plugin doesn't support working with the role property. You have the following in your provider sections: role: arn-for-deployment-role'. Try removing this.

More info here: https://github.com/functionalone/serverless-iam-roles-per-function#more-info

ankkho commented 5 years ago

@glicht My bad! I've removed role: arn-for-deployment-role it's working, Thanks!