如何在Serverless框架中为多个DynamoDB表定义IAM角色声明资源?

23

我希望在我的无服务器项目中使用多个DynamoDB表。如何在iamrolestatements中正确定义多个资源?

我有一个示例serverless.yml

service: serverless-expense-tracker
frameworkVersion: ">=1.1.0 <2.0.0"

provider:
  name: aws
  runtime: nodejs6.10
  environment:
    EXPENSES_TABLE: "${self:service}-${opt:stage, self:provider.stage}-expenses"
    BUDGETS_TABLE: "${self:service}-${opt:stage, self:provider.stage}-budgets"

  iamRoleStatements:
    - Effect: Allow
      Action:
        - dynamodb:Query
        - dynamodb:Scan
        - dynamodb:GetItem
        - dynamodb:PutItem
        - dynamodb:UpdateItem
        - dynamodb:DeleteItem
      Resource: "arn:aws:dynamodb:${opt:region, self:provider.region}:*:table/${self:provider.environment.EXPENSES_TABLE}"
      # what is the best way to add the other DB as a resource

functions:
  create:
    handler: expenseTracker/create.create
    events:
      - http:
          path: expenses
          method: post
          cors: true

  list:
    handler: expenseTracker/list.list
    events:
      - http:
          path: expenses
          method: get
          cors: true

  get:
    handler: expenseTracker/get.get
    events:
      - http:
          path: expenses/{id}
          method: get
          cors: true

  update:
    handler: expenseTracker/update.update
    events:
      - http:
          path: expenses/{id}
          method: put
          cors: true

  delete:
    handler: expenseTracker/delete.delete
    events:
      - http:
          path: expenses/{id}
          method: delete
          cors: true

resources:
  Resources:
    DynamoDbExpenses:
      Type: 'AWS::DynamoDB::Table'
      DeletionPolicy: Retain
      Properties:
        AttributeDefinitions:
          -
            AttributeName: id
            AttributeType: S
        KeySchema:
          -
            AttributeName: id
            KeyType: HASH
        ProvisionedThroughput:
          ReadCapacityUnits: 1
          WriteCapacityUnits: 1
        TableName: ${self:provider.environment.EXPENSES_TABLE}

    DynamoDbBudgets:
      Type: 'AWS::DynamoDB::Table'
      DeletionPolicy: Retain
      Properties:
        AttributeDefinitions:
          -
            AttributeName: id
            AttributeType: S
        KeySchema:
          -
            AttributeName: id
            KeyType: HASH
        ProvisionedThroughput:
          ReadCapacityUnits: 1
          WriteCapacityUnits: 1
        TableName: ${self:provider.environment.BUDGETS_TABLE}
你可以在那些评论中看到相关区域。

如果您有特定的问题(如何在IAM角色语句中正确定义多个资源),请提供一个仔细的示例,展示您尝试过的内容、任何错误以及详细解释您的意图。 - Vorsprung
谢谢@Vorsprung。我没有任何错误,但是我让上面链接的serverless.yml工作的唯一方法是使用通配符定义iam资源。这似乎是我们工程师所说的一个坏主意。你能帮我以更加封装的方式定义iamrolestatements中的多个资源吗? - jasongonzales
或许你的意思是我不应该将yml作为gist链接,而是直接在这里发布它? - jasongonzales
如果你需要帮助,那就提出具体的问题吧!请查看网站指南:https://stackoverflow.com/help/how-to-ask - Vorsprung
编辑过了,请告诉我是否不够明确。 - jasongonzales
3个回答

39

我懂了!

关键是在 - Resource 键下添加一个列表,但我也学到最好使用在表格供应时使用的 logicalIDs。下面是完整的示例:

service: serverless-expense-tracker

frameworkVersion: ">=1.1.0 <2.0.0"

provider:
  name: aws
  runtime: nodejs6.10
  environment:
    EXPENSES_TABLE: { "Ref": "DynamoDbExpenses" } #DynamoDbExpenses is a logicalID also used when provisioning below
    BUDGETS_TABLE: { "Ref": "DynamoDbBudgets" }

  iamRoleStatements:
    - Effect: Allow
      Action:
        - dynamodb:DescribeTable
        - dynamodb:Query
        - dynamodb:Scan
        - dynamodb:GetItem
        - dynamodb:PutItem
        - dynamodb:UpdateItem
        - dynamodb:DeleteItem
      Resource:
        - { "Fn::GetAtt": ["DynamoDbExpenses", "Arn"] } #you will also see the logical IDs below where they are provisioned
        - { "Fn::GetAtt": ["DynamoDbBudgets", "Arn"] }
functions:
  create:
    handler: expenseTracker/create.create
    events:
      - http:
          path: expenses
          method: post
          cors: true

  createBudget:
    handler: expenseTracker/createBudget.createBudget
    events:
      - http:
          path: budgets
          method: post
          cors: true

  list:
    handler: expenseTracker/list.list
    events:
      - http:
          path: expenses
          method: get
          cors: true

  listBudgets:
    handler: expenseTracker/listBudgets.listBudgets
    events:
      - http:
          path: budgets
          method: get
          cors: true

  get:
    handler: expenseTracker/get.get
    events:
      - http:
          path: expenses/{id}
          method: get
          cors: true

  update:
    handler: expenseTracker/update.update
    events:
      - http:
          path: expenses/{id}
          method: put
          cors: true

  delete:
    handler: expenseTracker/delete.delete
    events:
      - http:
          path: expenses/{id}
          method: delete
          cors: true

resources:
  Resources:
    DynamoDbExpenses: #this is where the logicalID is defined
      Type: 'AWS::DynamoDB::Table'
      DeletionPolicy: Retain
      Properties:
        AttributeDefinitions:
          -
            AttributeName: id
            AttributeType: S
        KeySchema:
          -
            AttributeName: id
            KeyType: HASH
        ProvisionedThroughput:
          ReadCapacityUnits: 1
          WriteCapacityUnits: 1

    DynamoDbBudgets: #here too
      Type: 'AWS::DynamoDB::Table'
      DeletionPolicy: Retain
      Properties:
        AttributeDefinitions:
          -
            AttributeName: id
            AttributeType: S
        KeySchema:
          -
            AttributeName: id
            KeyType: HASH
        ProvisionedThroughput:
          ReadCapacityUnits: 1
          WriteCapacityUnits: 1

当您管理两个DynamoDB表时,您如何让每个函数知道需要连接哪个数据库? - BMW
在这种情况下,您会注意到我已经在提供程序环境中定义了 EXPENSES_TABLE 和 BUDGETS_TABLE。在您的 Lambda 函数中,您只需像这样引用它:TableName: process.env.EXPENSES_TABLE - jasongonzales
但是在sls中lambda是自动创建的,那我在哪里可以设置TableName呢?你是指在handler脚本中吗? - BMW
1
是的,在处理程序脚本中,抱歉让您感到困惑。 - jasongonzales
1
处理程序脚本中未设置。我现在知道该怎么做了:https://serverless.com/framework/docs/providers/aws/guide/functions#environment-variables。似乎您当前的代码将无法正常工作,除非为每个函数添加环境变量`TABLE_NAME`。 - BMW
抱歉我看错了你的问题,我以为你在问在哪里使用环境变量!是的,你可以按照上面所示设置名称。 - jasongonzales

7

我希望分享一下我的更新,因为我在这个问题上花费了很多时间并学到了很多。目前被接受的答案并不完全可用。

我添加的内容如下:

1) 确保在您的处理程序中,有一个环境变量TABLE_NAME(或其他名称,您可以相应调整),它指的是Lambda函数的环境变量。

  const params = {
    TableName: process.env.TABLE_NAME,
    Item: {
      ...
    }
  }

2) 更新 serverless.yml 文件,为每个函数指定表名。

environment:
  TABLE_NAME: { "Ref": "DynamoDbExpenses" }

或者

environment:
  TABLE_NAME: { "Ref": "DynamoDbBudgets" }

根据函数所针对的表格而定。

完整的serverless.yml已在此处更新:

service: serverless-expense-tracker

frameworkVersion: ">=1.1.0 <2.0.0"

provider:
  name: aws
  runtime: nodejs6.10
  environment:
    EXPENSES_TABLE: { "Ref": "DynamoDbExpenses" } #DynamoDbExpenses is a logicalID also used when provisioning below
    BUDGETS_TABLE: { "Ref": "DynamoDbBudgets" }

  iamRoleStatements:
    - Effect: Allow
      Action:
        - dynamodb:DescribeTable
        - dynamodb:Query
        - dynamodb:Scan
        - dynamodb:GetItem
        - dynamodb:PutItem
        - dynamodb:UpdateItem
        - dynamodb:DeleteItem
      Resource:
        - { "Fn::GetAtt": ["DynamoDbExpenses", "Arn"] } #you will also see the logical IDs below where they are provisioned
        - { "Fn::GetAtt": ["DynamoDbBudgets", "Arn"] }
functions:
  create:
    handler: expenseTracker/create.create
    environment:
      TABLE_NAME: { "Ref": "DynamoDbExpenses" }
    events:
      - http:
          path: expenses
          method: post
          cors: true

  createBudget:
    handler: expenseTracker/createBudget.createBudget
    environment:
      TABLE_NAME: { "Ref": "DynamoDbBudgets" }
    events:
      - http:
          path: budgets
          method: post
          cors: true

  list:
    handler: expenseTracker/list.list
    environment:
      TABLE_NAME: { "Ref": "DynamoDbExpenses" }
    events:
      - http:
          path: expenses
          method: get
          cors: true

  listBudgets:
    handler: expenseTracker/listBudgets.listBudgets
    environment:
      TABLE_NAME: { "Ref": "DynamoDbBudgets" }
    events:
      - http:
          path: budgets
          method: get
          cors: true

  get:
    handler: expenseTracker/get.get
    environment:
      TABLE_NAME: { "Ref": "DynamoDbExpenses" }
    events:
      - http:
          path: expenses/{id}
          method: get
          cors: true

  update:
    handler: expenseTracker/update.update
    environment:
      TABLE_NAME: { "Ref": "DynamoDbExpenses" }
    events:
      - http:
          path: expenses/{id}
          method: put
          cors: true

  delete:
    handler: expenseTracker/delete.delete
    environment:
      TABLE_NAME: { "Ref": "DynamoDbExpenses" }
    events:
      - http:
          path: expenses/{id}
          method: delete
          cors: true

resources:
  Resources:
    DynamoDbExpenses: #this is where the logicalID is defined
      Type: 'AWS::DynamoDB::Table'
      DeletionPolicy: Retain
      Properties:
        AttributeDefinitions:
          -
            AttributeName: id
            AttributeType: S
        KeySchema:
          -
            AttributeName: id
            KeyType: HASH
        ProvisionedThroughput:
          ReadCapacityUnits: 1
          WriteCapacityUnits: 1
        TableName: ${self:service}-${opt:stage, self:provider.stage}-expenses

    DynamoDbBudgets: #here too
      Type: 'AWS::DynamoDB::Table'
      DeletionPolicy: Retain
      Properties:
        AttributeDefinitions:
          -
            AttributeName: id
            AttributeType: S
        KeySchema:
          -
            AttributeName: id
            KeyType: HASH
        ProvisionedThroughput:
          ReadCapacityUnits: 1
          WriteCapacityUnits: 1
        TableName: ${self:service}-${opt:stage, self:provider.stage}-budgets

参考:

无服务器环境变量


该链接提供了有关如何在AWS上使用无服务器环境变量的指南。

0
如果您的意图是提供对正在部署的堆栈中所有表的访问权限,您可以使用以下方法:
Resource: !Sub arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/${AWS::StackName}-*

这样,您堆栈中的Lambda将仅限于堆栈中的表,而每次添加表时都不必更新此内容。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接