如何在无服务器框架中为多个 dynamodb 表定义 iamrolestatements 资源?

Posted

技术标签:

【中文标题】如何在无服务器框架中为多个 dynamodb 表定义 iamrolestatements 资源?【英文标题】:How do I define resources for iamrolestatements for multiple dynamodb tables in serverless framework? 【发布时间】:2018-03-05 18:31:20 【问题描述】:

我想在我的无服务器项目中使用多个 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

您可以在那里的 cmets 中看到相关区域。

【问题讨论】:

如果你有一个特定的问题(如何在 iam 角色声明中正确定义多个资源),然后做一个仔细的例子来展示你尝试过的东西,任何错误都不起作用,并准确解释你的意图 感谢@Vorsprung。我没有任何错误,但是让上面链接的 serverless.yml 工作的唯一方法是使用通配符定义 iam 资源。这似乎是我们工程师所说的坏主意。你能帮我以一种更加、呃、封装的方式在 iamrolestatements 中定义多个资源吗? 或者你的意思是我不应该链接到 yml 作为要点,而是直接在这里发布? 我的意思是,如果您需要某种帮助,请提出具体问题!请参阅网站指南***.com/help/how-to-ask 已编辑,如果不够具体,请告诉我。 【参考方案1】:

我明白了!

关键只是在- Resource 下添加一个列表,但我还了解到,最好只使用配置表时使用的逻辑 ID。完整示例:

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表时,如何让每个函数知道需要连接哪个数据库? 在这种情况下,您会注意到我在 provider.environment 中定义了 EXPENSES_TABLE 和 BUDGETS_TABLE。在您的 lambda 中,您只需像这样引用它:TableName: process.env.EXPENSES_TABLE 但是 lambda 是在 sls 中自动创建的,我在哪里可以设置 TableName?你的意思是在更硬的脚本中? 是的,在处理程序脚本中,很抱歉造成混淆。 未在处理程序脚本中设置。我现在知道该怎么做了:serverless.com/framework/docs/providers/aws/guide/…。似乎您当前的代码将不起作用,直到您为每个函数添加环境 TABLE_NAME【参考方案2】:

我想发布我的更新,因为我花时间从这个问题中学到了很多东西。当前接受的答案未完全发挥作用。

我添加了什么:

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

参考:

serverless environment variables

【讨论】:

【参考方案3】:

如果您的意图是提供对正在部署的堆栈中所有表的访问,您可以使用:

Resource: !Sub arn:aws:dynamodb:$AWS::Region:$AWS::AccountId:table/$AWS::StackName-*

这样,您的堆栈中的 lambdas 仅限于堆栈中的表,您不必在每次添加表时都更新它。

【讨论】:

以上是关于如何在无服务器框架中为多个 dynamodb 表定义 iamrolestatements 资源?的主要内容,如果未能解决你的问题,请参考以下文章

如何在无服务器框架中包含静态文件?

如何在无服务器框架或 AWS lambda 中启用节点 js 的实验性功能

在无服务器框架中禁用 SLS_DEBUG=*

如何在无服务器中允许 CORS 用于自定义标头?

如何在 DynamoDb 中加入两个表?

如何在 Go 中从 DynamoDB 表中删除多个项目