Configure cross-account access to Amazon DynamoDB - AWS Prescriptive Guidance

Configure cross-account access to Amazon DynamoDB

Created by Shashi Dalmia (AWS), Esteban Serna Parra (AWS), and Imhoertha Ojior (AWS)

Environment: Production

Technologies: Databases; Security, identity, compliance

AWS services: Amazon DynamoDB; AWS Identity and Access Management; AWS Lambda

Summary

This pattern explains the steps for configuring cross-account access to Amazon DynamoDB by using resource-based policies. For workloads that use DynamoDB, it's becoming more common to use workload isolation strategies to minimize security threats and to meet compliance requirements. Implementing workload isolation strategies often requires cross-account and cross-Region access to DynamoDB resources by using AWS Identity and Access Management (IAM) identity-based policies. This involves setting IAM permissions and establishing a trust relationship between the AWS accounts.

Resource-based policies for DynamoDB greatly simplify the security posture for cross-account workloads. This pattern provides steps and sample code to demonstrate how you can configure AWS Lambda functions in one AWS account to write data to a DynamoDB database table in a different account.

Prerequisites and limitations

Prerequisites

  • Two active AWS accounts. This pattern refers to these accounts as Account A and Account B.

  • AWS Command Line Interface (AWS CLI) installed and configured to access Account A, to create the DynamoDB table. The other steps in this pattern provide instructions for using the IAM, DynamoDB, and Lambda consoles. If you’re planning to use AWS CLI instead, configure it to access both accounts.

Limitations

Architecture

The following diagram shows a single-account architecture. AWS Lambda, Amazon Elastic Compute Cloud (Amazon EC2), and DynamoDB are all in the same account. In this scenario, Lambda functions and Amazon EC2 instances can access DynamoDB. To grant access to the DynamoDB table, you can create an identity-based policy in IAM, or you can create a resource-based policy in DynamoDB.

Using IAM permissions to access a DynamoDB table in the same account.

The following diagram shows a multi-account architecture. If resources in one AWS account require access to a DynamoDB table in a different account, you need to set up a resource-based policy in DynamoDB to grant the required access. For example, in the following diagram, access to the DynamoDB table in Account A is granted to a Lambda function in Account B by using a resource-based policy.

Using a resource-based policy to access a DynamoDB table in a different account.

This pattern describes cross-account access between Lambda and DynamoDB. You can use similar steps for other AWS services if the appropriate permissions are configured on both accounts. For example, if you want to provide a Lambda function access to an Amazon Simple Storage Service (Amazon S3) bucket in Account A, you can create a resource-based policy in Amazon S3 and add the permissions to the Lambda execution role in Account B.

Tools

AWS services

  • Amazon DynamoDB is a fully managed NoSQL database service that provides fast, predictable, and scalable performance.

  • AWS Identity and Access Management (IAM) helps you securely manage access to your AWS resources by controlling who is authenticated and authorized to use them.

  • AWS Lambda is a compute service that helps you run code without needing to provision or manage servers. It runs your code only when needed and scales automatically, so you pay only for the compute time that you use.

Code

This pattern includes sample code in the Additional information section to show how you can configure a Lambda function in Account B to write to the DynamoDB table in Account A. The code is provided only for illustration and testing purposes. If you’re implementing this pattern in a production environment, use the code as a reference, and customize it for your own environment.

Best practices

Epics

TaskDescriptionSkills required

Create a policy in Account B.

This IAM policy allows the PutItem action for a DynamoDB table in Account A.

  1. Sign in to Account B in the AWS Management Console.

  2. Open the IAM console.

  3. In the navigation pane, choose Policies, and then choose Create policy.

  4. On the Specify permissions page, for the policy editor, select JSON.

  5. Enter the following policy.

    { "Version": "2012-10-17", "Statement": [ { "Sid": "Statement1", "Effect": "Allow", "Action": "dynamodb:PutItem", "Resource": "arn:aws:dynamodb:<Region>:<Account-A-ID>:table/Table-Account-A" } ] }
  6. Replace <Region> and <Account-A-ID> with your values, and then choose Next.

  7. For Policy name, enter a unique name for your policy, such as DynamoDB-PutItem-Policy.

  8. (Optional) Add a policy description.

  9. Choose Create policy.

General AWS

Create a role in Account B.

The Lambda function in Account B uses this IAM role to access the DynamoDB table in Account A.

  1. Open the IAM console.

  2. In the navigation pane, choose Roles, and then choose Create role.

  3. For Select trusted entity, choose AWS service.

  4. In the Use case section, choose Lambda.

  5. Choose Next: Permissions.

  6. In the Filter policies box, enter DynamoDB.

  7. In the list of DynamoDB policies, choose DynamoDB-PutItem-Policy.

  8. Clear the Filter policies box, and then enter Lambda.

  9. In the list of Lambda policies, choose AWSLambdaExecute.

  10. Choose Next: Name, review, and create.

  11. For Role name, enter a unique name for your role, such as DynamoDB-PutItemAccess.

  12. (Optional) Add a role description.

  13. (Optional) Add metadata to the role by attaching tags as key-value pairs.

  14. Choose Create role.

For more information about creating roles, see the IAM documentation.

General AWS

Note the role ARN.

  1. Open the IAM console.

  2. In the navigation pane, choose Roles.

  3. In the search box, enter DynamoDB-PutItemAccess, and then choose the role.

  4. On the summary page for the role, copy the Amazon Resource Name (ARN). You use the ARN when you set up the Lambda function.

General AWS
TaskDescriptionSkills required

Create a DynamoDB table.

Use the following AWS CLI command to create a DynamoDB table.

aws dynamodb create-table \ --table-name Table-Account-A \ --attribute-definitions \ AttributeName=category,AttributeType=S \ AttributeName=item,AttributeType=S \ --key-schema \ AttributeName=category,KeyType=HASH \ AttributeName=item,KeyType=RANGE \ --provisioned-throughput \ ReadCapacityUnits=5,WriteCapacityUnits=5 \ --resource-policy \ '{ "Version": "2012-10-17", "Statement": [ { "Sid": "Statement1", "Effect": "Allow", "Principal": { "AWS": "arn:aws:iam::<Account-B-ID>:role/<Role-Name>" }, "Action": "dynamodb:PutItem", "Resource": "arn:aws:dynamodb:<Region>:<Account-A-ID>:table/Table-Account-A" } ] }'

Replace the following in this code sample:

  • <Account-B-ID> is the ID of Account B.

  • <Role-Name> is the name of the IAM role that you created, such as DynamoDB-PutItemAccess.

  • <Region> is the AWS Region where you are creating the DynamoDB table.

  • <Account-A-ID> is the ID of Account A.

Note: You specify the resource-based policy configuration in the create-table statement by using the --resource-policy flag. This policy refers to the ARN for the DynamoDB table in Account A.

For more information about creating tables, see the DynamoDB documentation.

General AWS
TaskDescriptionSkills required

Create a Lambda function to write data to DynamoDB.

  1. Sign in to Account B in the AWS Management Console.

  2. Open the Lambda console.

  3. In the navigation pane, choose Functions, and then choose Create function.

  4. For Name, enter lambda_write_function.

  5. For Runtime, choose Python 3.8 or later.

  6. Under Change default execution role, choose Use an existing role.

  7. For Existing role, choose the IAM role that you created, such as DynamoDB-PutItemAccess.

  8. Choose Create function.

  9. In the Code tab, paste the sample code that is provided in the Additional information section of this pattern. Replace the following in this code sample:

    • <Account-A-ID> is the ID of Account A.

    • <Region> is the AWS Region where you created the DynamoDB table.

  10. Choose Deploy.

  11. Choose Test. This prompts you to configure a test event. Create a new event with your preferred name, such as MyTestEventForWrite, and then save the configuration.

  12. Choose Test again. This runs the Lambda function with the event name you provided.

  13. Check the output from the function. It should indicate that the function accessed the DynamoDB table in Account A and was able to write data to it.

For more information about creating Lambda functions, see the Lambda documentation.

General AWS
TaskDescriptionSkills required

Delete resources.

To avoid incurring costs associated with the resources created in this pattern, do the following to delete these resources:

  1. In Account B, delete the Lambda function you created to connect to DynamoDB. For instructions, see the Lambda documentation.

  2. In Account A, delete the DynamoDB table you created. For instructions, see the DynamoDB documentation.

  3. For security best practices, delete the IAM policy (DynamoDB-PutItem-Policy) when it is no longer needed. For more information, see the IAM documentation.

  4. For security best practices, delete the IAM role (DynamoDB-PutItemAccess) when it is no longer needed. For more information, see the IAM documentation.

General AWS

Troubleshooting

IssueSolution

When creating the Lambda function, you receive a ResourceNotFoundException error.

Confirm that you have correctly entered the AWS Region and ID of Account A. These are part of the ARN for the DynamoDB table.

Related resources

Additional information

Sample code

import boto3 from datetime import datetime dynamodb_client = boto3.client('dynamodb') def lambda_handler(event, context): now = datetime.now().isoformat() data = dynamodb_client.put_item(TableName='arn:aws:dynamodb:<Region>:<Account-A-ID>:table/Table-Account-A', Item={"category": {"S": "Fruit"},"item": {"S": "Apple"},"time": {"S": now}}) return data

Note: When the DynamoDB client is instantiated, the ARN of the DynamoDB table is provided instead of the table name. This is required so that the Lambda function connects to the correct DynamoDB table when it runs.