使用Terraform和Node.js掌握跨学科DynamoDB访问:逐步指南!
#aws #node #dynamodb #terraform

介绍

建立DynamoDB的交叉算法访问可能是一个复杂的任务,尤其是在浏览配置多个AWS帐户的复杂性时。但是,通过采用Terraform和Node.js的功能,您可以克服这一挑战并创建强大而有效的多学院设置。在本文中,我们将指导您使用Terraform和Node.js设置DynamoDB的交叉算法访问的过程,使您可以轻松地在两个单独的AWS帐户上管理和获取数据。

此设置的好处很多。它不仅简化了您的项目资源管理,而且通过隔离帐户之间的资源来大大提高安全性。此外,它允许更好的可扩展性,使您的基础架构随着项目的需求而增长和发展。因此,让我们踏上这一激动人心的旅程,探索跨学院DynamoDB访问的世界!

先决条件

在开始之前,至关重要的是要确保您拥有必要的工具和服务。对于本教程,您将需要以下内容:

  1. 两个AWS帐户:带有DDB表和消费者的主要AWS帐户可以空白。

  2. Terraform:我们将使用Terraform自动化AWS资源的创建和管理。要安装Terraform,请按照official documentation中的说明。

  3. node.js:我们将使用节点构建我们从DynamoDB获取数据的示例应用程序。另外,serverless将用于部署应用程序。

本指南假定您对AWS组件,Terraform和JavaScript语法有基本的了解。

现在您拥有所有必要的工具和服务,让我们向前迈进并设置我们的AWS环境,以供交叉插件dynamodb访问。

AWS设置概述

让我们在详细信息之前先审查AWS设置:

AWS Setup Diagram

主帐户帐户保留DDB表,我们需要访问

消费者帐户应该从主帐户中授予对DDB表的访问。

  1. main aws帐户创建一个角色(consumer_account_role),该角色将允许消费者帐户为 main main 帐户提供凭证/strong>帐户。

  2. main 帐户中创建一个策略(main_account_ddb_policies_fell),该策略将允许访问DDB表。

  3. Main 帐户附加ain_account_ddb_policies_full to compution_account_role。

  4. 使用1个步骤,我们创建了一个信任策略规则,该规则允许消费者帐户使用commuter_account_role。

  5. 消费者帐户中,我们创建了一项策略,该策略将允许消费者lambda使用commuter_account_role。

应用层概述

Application Layer Diagram

  1. 由于示例消费者lambda可以通过随附的consumer_account_access_policy访问consumer_role,我们需要访问issue temporary credentials using Security Token Service (STS)访问ddb表。

  2. 必须使用从STS呼叫返回的已发行凭据来实例化DDB客户端和查询数据。

现在我们已经熟悉所有移动的零件,最有趣的部分留给了所有这些代码!

Terraform配置

使用Terraform为Main帐户创建3件事:

  1. aws_iam_role,消费者帐户将使用

  2. aws_iam_policy,它将允许下面的角色访问DDB表来执行特定的操作。

  3. aws_iam_policy_attachment - 将角色和政策链接在一起。

此模块可以具有3个参数以输入:

  • 消费者帐户ID。

  • ddb表名。

  • 环境名称(以防万一)。

一次设置看起来很好。 1策略 - > 1个附件 - > 1个角色。但是,如果我们需要从一个表格上阅读并完全访问另一个表怎么办?

我们应该再复制此设置,但对于另一个表格?

让我们创建一个可以处理这些情况的模块:

# main.tf

resource "aws_iam_role" "ddb_access_role" {
  name = "${var.env}-ddb_cross_acc_access_role-${var.consumer_account_id}"
  description = "Role that provides access to DynamoDB tables for account ${var.consumer_account_id} on ${var.env} env"

  assume_role_policy = jsonencode({
    Version   = "2012-10-17"
    Statement = [
      {
        Action    = "sts:AssumeRole"
        Effect    = "Allow"
        Principal = {
          AWS = "arn:aws:iam::${var.consumer_account_id}:root"
        }
      }
    ]
  })
}

resource "aws_iam_policy" "ddb_table_policy" {
  for_each = { for table_access in var.table_access_list : table_access.table_name => table_access }

  name        = "${var.env}-DynamoDBTableAccess-${var.consumer_account_id}-${each.key}"
  description = "Access policy to DynamoDB ${each.key} table for account ${var.consumer_account_id} on ${var.env} env"

  policy = jsonencode({
    Version   = "2012-10-17"
    Statement = [
      {
        Action    = lookup({
          "Read"            = [
            "dynamodb:BatchGetItem",
            "dynamodb:GetItem",
            "dynamodb:Query",
            "dynamodb:Scan"
          ],
          "ReadWriteUpdate" = [
            "dynamodb:BatchWriteItem",
            "dynamodb:DeleteItem",
            "dynamodb:GetItem",
            "dynamodb:PutItem",
            "dynamodb:UpdateItem"
          ],
          "Full" = ["dynamodb:*"]
        }, each.value.access_type, [])

        Effect    = "Allow"
        Resource  = [
          "arn:aws:dynamodb:*:${data.aws_caller_identity.current.account_id}:table/${each.key}",
          "arn:aws:dynamodb:*:${data.aws_caller_identity.current.account_id}:table/${each.key}/index/*"
        ]
      }
    ]
  })
}

resource "aws_iam_policy_attachment" "ddb_access_policy_attachment" {
  for_each = aws_iam_policy.ddb_table_policy

  policy_arn = each.value.arn
  roles      = [aws_iam_role.ddb_access_role.name]
  name       = "cross_acc_access_policy_attachment-${var.consumer_account_id}-${each.key}"
}

data "aws_caller_identity" "current" {}

IAM角色

创建的aws_iam_role.ddb_access_role具有以下格式的名称:[var.env]-ddb_cross_acc_access_role-[var.consumer_account_id]

假设_role_policy IAM角色的属性设置为JSON编码的字符串,该字符串定义了担任角色的权限。在这种情况下,该政策授予指定AWS帐户(arn:aws:iam::${var.consumer_account_id}:root)担任该角色的许可。应用Terraform后,您可以在IAM策略页面上的可信关系中查看此策略。

IAM政策

aws_iam_policy.ddb_table_policy提供了对var.table_access_list输入变量指定的DynamoDB表列表的访问。列表中的每个表都有关联的access_type属性,该属性指定应授予哪种访问权限(Read, ReadWriteUpdate, Full)。

策略授予基于access_type属性的表访问。具体而言,该策略允许在表上定义的表上的某些DynamoDB操作(例如BatchGetItem, PutItem等),该策略允许Lookup()函数,该功能返回相应的access_type的适当值。

for_each属性用于为列表中的每个表创建单独的IAM策略。每个策略的名称都具有以下格式:[var.env]-DynamoDBTableAccess-[var.consumer_account_id]-${each.key}

需要制定一个策略,为每个帐户中的每个新表拥有一个唯一的名称,以便我们可以轻松地重用不同帐户和环境的模块,而不必担心名称重叠。

IAM政策附件

aws_iam_policy_attachment.ddb_access_policy_attachment将IAM策略附加到较早创建的IAM角色。该附件是为每个IAM策略创建的作为aws_iam_policy.ddb_table_policy资源块的一部分而创建的。

最后,data.aws_caller_identity.current数据源用于检索Terraform用户的AWS帐户ID。帐户ID在IAM策略资源中使用,以授予该特定帐户的DynamoDB表访问。

不要忘记变量。tf

variable "consumer_account_id" {
  type        = string
  description = "The AWS account ID of the account which gains access."
}

variable "env" {
  type        = string
  description = "Environment name"
}

variable "table_access_list" {
  description = "List of DynamoDB table names and access types"
  type = list(object({
    table_name = string,
    access_type = string  #allowed values are Read, ReadWriteUpdate, and Full
  }))
}

用法

转到您的 main 帐户Terraform设置并使用我们刚创建的模块:

#.....

module "ddb_cross_acc_access" {
  source = "path/to/module"

  table_access_list = [
    {
      table_name  = "my-table-1"
      access_type = "ReadWriteUpdate"
    },
    {
      table_name  = "my-table-2"
      access_type = "Read"
    }
  ]

  consumer_account_id = "123456789012"

  env = "dev"
}

#.....

现在您可以看到,我们可以指定 main 帐户的访问级别将为消费者帐户提供DDB表!

Node.js应用程序设置

  1. 您可以使用很棒的serverless-iam-roles-per-function无服务器插件来创建每个功能的策略以访问STS:
ConsumerAccountExampleFunctions:
  handler: src/handler.handler
  name: ${self:provider.stage}-${self:service}-consumerAccountExampleFunctions
  iamRoleStatements:
    - Effect: 'Allow'
      Action:
        - 'sts:AssumeRole'
      Resource:
        - 'arn:aws:iam::${env:MAIN_ACCOUNT_ID}:role/${env:ENVIRONMENT}-ddb_cross_acc_access_role-${env:AWS_ACCOUNT_ID}'

是一个很好的安全惯例,因为我们严格控制了lambda可以访问的外部来源。

  1. 使用STS服务的授予凭证:
// get-sts-creds.ts
import {STS} from 'aws-sdk';

type CredentialsParams = {
  RoleArn: string; // Optional - only required if assuming a role
  RoleSessionName: string; // Optional - only required if assuming a role
};

export const getCredentials = async (params: CredentialsParams) => {
  const stsClient = new STS();

  // Assume the IAM role of the account that owns the target resource, if specified
  const assumedRoleObject = await stsClient
    .assumeRole({
      RoleArn: params.RoleArn,
      RoleSessionName: params.RoleSessionName,
    })
    .promise();

  // Get temporary credentials
  const credentials = {
    accessKeyId: assumedRoleObject?.Credentials?.AccessKeyId || '',
    secretAccessKey: assumedRoleObject?.Credentials?.SecretAccessKey || '',
    sessionToken: assumedRoleObject?.Credentials?.SessionToken || '',
  };

  if (!credentials.accessKeyId && !credentials.secretAccessKey && !credentials.sessionToken) {
    throw new Error('Credentials are not defined!');
  }

  return credentials;
};
  1. 使用凭据执行跨学会DDB调用!
import { DocumentClient } from 'aws-sdk/clients/dynamodb';
import { getCredentials } from './get-sts-creds.ts';

const tableName = 'ExampleDDBTableName';
// AWS_ACCOUNT_ID = Consumer account id
const roleArn = `arn:aws:iam::${process.env.MAIN_ACCOUNT_ID}:role/${process.env.ENVIRONMENT}-ddb_cross_acc_access_role-${process.env.AWS_ACCOUNT_ID}`;
const roleSessionName = 'my-session-name';

export const handler = async () => {
  const credentials = await getCredentials({ RoleArn: roleArn, RoleSessionName: roleSessionName });

  const ddbClient = new DocumentClient({
    credentials
  });

  const params = { TableName: tableName };
  const data = await ddbClient.scan(params).promise();

  return data
};

概括

现在,您知道如何为粒状跨学会DDB创建Terraform模块:

  • 在主帐户上,用消费者帐户可信关系创建角色

  • 在主帐户上创建一个策略,其中包含DDB访问权限

  • 在消费者帐户上创建一个策略,允许使用Mains帐户角色

  • 在您的应用程序中使用STS客户端以从主帐户角色授予临时凭据

  • 用授予的凭据执行DDB查询!