

# 跨 AWS 账户访问 DAX
<a name="DAX.cross-account-access"></a>

假设一个 DynamoDB Accelerator (DAX) 集群在一个 AWS 账户（账户 A）中运行，需要能够从另一个 AWS 账户（账户 B）中的 Amazon Elastic Compute Cloud (Amazon EC2) 实例访问 DAX 集群。在本教程中，您可以通过使用账户 B 中的 IAM 角色启动账户 B 中的 EC2 实例来做到这一点。然后，您可以使用 EC2 实例中的临时安全凭证来代入账户 A 中的 IAM 角色。最后，您可以通过代入账户 A 中的 IAM 角色来使用临时安全凭证，以通过与账户 A 中的 DAX 集群的 Amazon VPC 对等连接来进行应用程序调用。要执行这些任务，您将需要两个 AWS 账户中的管理访问权限。

**重要**  
 无法让 DAX 集群通过其它账户访问 DynamoDB 表。

**Topics**
+ [设置 IAM](#DAX.cross-account-access.iam-setup)
+ [设置 VPC](#DAX.cross-account-access.vpc-setup)
+ [修改 DAX 客户端以允许跨账户存取](#DAX.cross-account-access.modify-client)

## 设置 IAM
<a name="DAX.cross-account-access.iam-setup"></a>

1. 使用以下内容创建一个名为 `AssumeDaxRoleTrust.json` 的文本文件，允许 Amazon EC2 代表您工作。

------
#### [ JSON ]

****  

   ```
   {
       "Version":"2012-10-17",		 	 	 
       "Statement": [
           {
               "Effect": "Allow",
               "Principal": {
                   "Service": "ec2.amazonaws.com"
               },
               "Action": "sts:AssumeRole"
           }
       ]
   }
   ```

------

1. 在账户 B 中，创建一个启动实例时 Amazon EC2 可以使用的角色。

   ```
   aws iam create-role \
       --role-name AssumeDaxRole \
       --assume-role-policy-document file://AssumeDaxRoleTrust.json
   ```

1. 使用以下内容创建一个名为 `AssumeDaxRolePolicy.json` 的文本文件，从而允许在账户 B 中的 EC2 实例上运行的代码代入账户 A 中的 IAM 角色。将 *accountA* 替换为账户 A 的实际 ID。

------
#### [ JSON ]

****  

   ```
   {
       "Version":"2012-10-17",		 	 	 
       "Statement": [
           {
               "Effect": "Allow",
               "Action": "sts:AssumeRole",
               "Resource": "arn:aws:iam::111122223333:role/DaxCrossAccountRole"
           }
       ]
   }
   ```

------

1. 将该策略添加到您刚刚创建的角色。

   ```
   aws iam put-role-policy \
       --role-name AssumeDaxRole \
       --policy-name AssumeDaxRolePolicy \
       --policy-document file://AssumeDaxRolePolicy.json
   ```

1. 创建实例配置文件以允许实例使用该角色。

   ```
   aws iam create-instance-profile \
       --instance-profile-name AssumeDaxInstanceProfile
   ```

1. 将该角色与实例配置文件关联。

   ```
   aws iam add-role-to-instance-profile \
       --instance-profile-name AssumeDaxInstanceProfile \
       --role-name AssumeDaxRole
   ```

1. 使用以下内容创建一个名为 `DaxCrossAccountRoleTrust.json` 的文本文件，这将允许账户 B 代入账户 A 角色。将 *accountB* 替换为账户 B 的实际 ID。

------
#### [ JSON ]

****  

   ```
   {
       "Version":"2012-10-17",		 	 	 
       "Statement": [
           {
               "Effect": "Allow",
               "Principal": {
                   "AWS": "arn:aws:iam::111122223333:role/AssumeDaxRole"
               },
               "Action": "sts:AssumeRole"
           }
       ]
   }
   ```

------

1. 在账户 A 中，创建账户 B 可代入的角色。

   ```
   aws iam create-role \
       --role-name DaxCrossAccountRole \
       --assume-role-policy-document file://DaxCrossAccountRoleTrust.json
   ```

1. 创建一个名为 `DaxCrossAccountPolicy.json` 的文本文件来允许访问 DAX 集群。将 *dax-cluster-arn* 替换为 DAX 集群的正确 Amazon Resource Name (ARN)。

------
#### [ JSON ]

****  

   ```
   {
       "Version":"2012-10-17",		 	 	 
       "Statement": [
           {
               "Effect": "Allow",
               "Action": [
                   "dax:GetItem",
                   "dax:BatchGetItem",
                   "dax:Query",
                   "dax:Scan",
                   "dax:PutItem",
                   "dax:UpdateItem",
                   "dax:DeleteItem",
                   "dax:BatchWriteItem",
                   "dax:ConditionCheckItem"
               ],
               "Resource": "arn:aws:dax:us-east-1:111122223333:cache/dax-cluster-name"
           }
       ]
   }
   ```

------

1. 在账户 A 中，将策略添加到角色。

   ```
   aws iam put-role-policy \
       --role-name DaxCrossAccountRole \
       --policy-name DaxCrossAccountPolicy \
       --policy-document file://DaxCrossAccountPolicy.json
   ```

## 设置 VPC
<a name="DAX.cross-account-access.vpc-setup"></a>

1. 查找账户 A 的 DAX 集群的子网组。将 *cluster-name* 替换为账户 B 必须访问的 DAX 集群的名称。

   ```
   aws dax describe-clusters \
       --cluster-name cluster-name
       --query 'Clusters[0].SubnetGroup'
   ```

1. 使用该 *subnet-group* 查找集群的 VPC。

   ```
   aws dax describe-subnet-groups \
       --subnet-group-name subnet-group \
       --query 'SubnetGroups[0].VpcId'
   ```

1. 使用该 *vpc-id* 查找 VPC 的 CIDR。

   ```
   aws ec2 describe-vpcs \
       --vpc vpc-id \
       --query 'Vpcs[0].CidrBlock'
   ```

1. 从账户 B 中，使用与上一步中找到的 CIDR 不重叠的其他 CIDR 创建 VPC。然后，创建至少一个子网。您可以使用 AWS 管理控制台 或 [AWS CLI](https://docs.aws.amazon.com/cli/latest/reference/ec2/create-vpc.html) 的 [VPC 创建向导](https://docs.aws.amazon.com/vpc/latest/userguide/getting-started-ipv4.html#getting-started-create-vpc)。

1. 从账户 B 中，请求与账户 A VPC 的对等连接，如[创建并接受 VPC 对等连接](https://docs.aws.amazon.com/vpc/latest/peering/create-vpc-peering-connection.html)中所述。从账户 A 中，接受连接。

1. 从账户 B 中，查找新 VPC 的路由表。将 *vpc-id* 替换为您在账户 B 中创建的 VPC 的 ID。

   ```
   aws ec2 describe-route-tables \
       --filters 'Name=vpc-id,Values=vpc-id' \
       --query 'RouteTables[0].RouteTableId'
   ```

1. 添加一条路由，以将发送到账户 A 的 CIDR 的流量发送到 VPC 对等连接。请记住，将每个*用户输入占位符*替换为您帐户的正确值。

   ```
   aws ec2 create-route \
       --route-table-id accountB-route-table-id \
       --destination-cidr accountA-vpc-cidr \
       --vpc-peering-connection-id peering-connection-id
   ```

1. 在账户 A 中，使用之前找到的 *vc-id* 查找 DAX 集群路由表。

   ```
   aws ec2 describe-route-tables \
       --filters 'Name=vpc-id, Values=accountA-vpc-id' \
       --query 'RouteTables[0].RouteTableId'
   ```

1. 在账户 A 中，添加一条路由以将发送到账户 B 的 CIDR 的流量发送到 VPC 对等连接。将每个*用户输入占位符*替换为您账户的正确值。

   ```
   aws ec2 create-route \
       --route-table-id accountA-route-table-id \
       --destination-cidr accountB-vpc-cidr \
       --vpc-peering-connection-id peering-connection-id
   ```

1. 从账户 B 中，在您之前创建的 VPC 中启动 EC2 实例。为它提供 `AssumeDaxInstanceProfile`。可以使用 AWS 管理控制台 或 [AWS CLI](https://docs.aws.amazon.com/cli/latest/reference/ec2/run-instances.html) 的[启动向导](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/launching-instance.html)。记下实例的安全组。

1. 在账户 A 中，查找 DAX 集群使用的安全组。请记住将 *cluster-name* 替换为您的 DAX 集群的名称。

   ```
   aws dax describe-clusters \
       --cluster-name cluster-name \
       --query 'Clusters[0].SecurityGroups[0].SecurityGroupIdentifier'
   ```

1. 更新 DAX 集群的安全组，允许来自您在账户 B 中创建的 EC2 实例安全组的入站流量。请记住将*用户输入占位符*替换为您账户的正确值。

   ```
   aws ec2 authorize-security-group-ingress \
       --group-id accountA-security-group-id \
       --protocol tcp \
       --port 8111 \
       --source-group accountB-security-group-id \
       --group-owner accountB-id
   ```

此时，账户 B 的 EC2 实例上的应用程序可以使用实例配置文件来代入 `arn:aws:iam::accountA-id:role/DaxCrossAccountRole` 角色和使用 DAX 集群。

## 修改 DAX 客户端以允许跨账户存取
<a name="DAX.cross-account-access.modify-client"></a>

**注意**  
AWS Security Token Service (AWS STS) 凭证是临时凭证。一些客户端会自动处理刷新，而其他客户端则需要额外的逻辑才能刷新凭证。建议您遵循相应文档的指导信息。

------
#### [ Java ]

此部分帮助您修改现有的 DAX 客户端代码以允许跨账户 DAX 访问。如果您没有 DAX 客户端代码，则可在 [Java 和 DAX](DAX.client.run-application-java.md) 教程中查找工作代码示例。

1. 添加以下导入。

   ```
   import com.amazonaws.auth.STSAssumeRoleSessionCredentialsProvider;
   import com.amazonaws.services.securitytoken.AWSSecurityTokenService;
   import com.amazonaws.services.securitytoken.AWSSecurityTokenServiceClientBuilder;
   ```

1. 从 AWS STS 中获取凭证提供程序并创建 DAX 客户端对象。请记住，将每个*用户输入占位符*替换为您帐户的正确值。

   ```
   AWSSecurityTokenService awsSecurityTokenService = AWSSecurityTokenServiceClientBuilder
        .standard()
        .withRegion(region)
        .build();
   
   STSAssumeRoleSessionCredentialsProvider credentials =  new STSAssumeRoleSessionCredentialsProvider.Builder("arn:aws:iam::accountA:role/RoleName", "TryDax")
        .withStsClient(awsSecurityTokenService)
        .build();
   
   DynamoDB client = AmazonDaxClientBuilder.standard()
       .withRegion(region)
       .withEndpointConfiguration(dax_endpoint)
       .withCredentials(credentials)
       .build();
   ```

------
#### [ .NET ]

此部分帮助您修改现有的 DAX 客户端代码以允许跨账户 DAX 访问。如果您没有 DAX 客户端代码，则可在 [.NET 和 DAX](DAX.client.run-application-dotnet.md) 教程中查找工作代码示例。

1. 将 [AWSSDK.SecurityToken](https://www.nuget.org/packages/AWSSDK.SecurityToken) NuGet 程序包添加到解决方案。

   ```
   <PackageReference Include="AWSSDK.SecurityToken" Version="latest version" />
   ```

1. 使用 `SecurityToken` 和 `SecurityToken.Model` 程序包。

   ```
   using Amazon.SecurityToken;
   using Amazon.SecurityToken.Model;
   ```

1. 从 `AmazonSimpleTokenService` 中获取临时凭证并创建 `ClusterDaxClient` 对象。请记住，将每个*用户输入占位符*替换为您帐户的正确值。

   ```
   IAmazonSecurityTokenService sts = new AmazonSecurityTokenServiceClient();
   
   var assumeRoleResponse = sts.AssumeRole(new AssumeRoleRequest
   {
       RoleArn = "arn:aws:iam::accountA:role/RoleName",
                   RoleSessionName = "TryDax"
   });
   
   Credentials credentials = assumeRoleResponse.Credentials;
   
   var clientConfig = new DaxClientConfig(dax_endpoint, port)
   {
       AwsCredentials = assumeRoleResponse.Credentials
                      
   };
   
   var client = new ClusterDaxClient(clientConfig);
   ```

------
#### [ Go ]

此部分帮助您修改现有的 DAX 客户端代码以允许跨账户 DAX 访问。如果您没有 DAX 客户端代码，则可查找 [GitHub 上的工作代码示例](https://github.com/aws-samples/aws-dax-go-sample/blob/master/try_dax.go)。

1. 导入 AWS STS 和会话程序包。

   ```
   import (
       "github.com/aws/aws-sdk-go/aws/session"
       "github.com/aws/aws-sdk-go/service/sts"
       "github.com/aws/aws-sdk-go/aws/credentials/stscreds"
   )
   ```

1. 从 `AmazonSimpleTokenService` 中获取临时凭证并创建 DAX 客户端对象。请记住，将每个*用户输入占位符*替换为您帐户的正确值。

   ```
   sess, err := session.NewSession(&aws.Config{
       Region: aws.String(region)},
   )
   if err != nil {
       return nil, err
   }
   
   stsClient := sts.New(sess)
   arp := &stscreds.AssumeRoleProvider{
                   Duration:     900 * time.Second,
                   ExpiryWindow: 10 * time.Second,
                   RoleARN:      "arn:aws:iam::accountA:role/role_name",
                   Client:       stsClient,
                   RoleSessionName: "session_name",
           }cfg := dax.DefaultConfig()
   
   cfg.HostPorts = []string{dax_endpoint}
   cfg.Region = region
   cfg.Credentials = credentials.NewCredentials(arp)
   daxClient := dax.New(cfg)
   ```

------
#### [ Python ]

此部分帮助您修改现有的 DAX 客户端代码以允许跨账户 DAX 访问。如果您没有 DAX 客户端代码，则可在 [Python 和 DAX](DAX.client.run-application-python.md) 教程中查找工作代码示例。

1. 导入 `boto3`。

   ```
   import boto3
   ```

1. 从 `sts` 中获取临时凭证并创建 `AmazonDaxClient` 对象。请记住，将每个*用户输入占位符*替换为您帐户的正确值。

   ```
   sts = boto3.client('sts')
   stsresponse = sts.assume_role(RoleArn='arn:aws:iam::accountA:role/RoleName',RoleSessionName='tryDax')
   credentials = botocore.session.get_session()['Credentials']
   
   dax = amazondax.AmazonDaxClient(session, region_name=region, endpoints=[dax_endpoint], aws_access_key_id=credentials['AccessKeyId'], aws_secret_access_key=credentials['SecretAccessKey'], aws_session_token=credentials['SessionToken'])
   client = dax
   ```

------
#### [ Node.js ]

此部分帮助您修改现有的 DAX 客户端代码以允许跨账户 DAX 访问。如果您没有 DAX 客户端代码，则可在 [Node.js 和 DAX](DAX.client.run-application-nodejs.md) 教程中查找工作代码示例。请记住，将每个*用户输入占位符*替换为您帐户的正确值。

```
const AmazonDaxClient = require('amazon-dax-client');
const AWS = require('aws-sdk');
const region = 'region';
const endpoints = [daxEndpoint1, ...];

const getCredentials = async() => {
  return new Promise((resolve, reject) => {
    const sts = new AWS.STS();
    const roleParams = {
      RoleArn: 'arn:aws:iam::accountA:role/RoleName',
      RoleSessionName: 'tryDax',
    };
    sts.assumeRole(roleParams, (err, session) => {
      if(err) {
        reject(err);
      } else {
        resolve({
          accessKeyId: session.Credentials.AccessKeyId,
          secretAccessKey: session.Credentials.SecretAccessKey,
          sessionToken: session.Credentials.SessionToken,
        });
      }
    });
  });
};

const createDaxClient = async() => {
  const credentials = await getCredentials();
  const daxClient = new AmazonDaxClient({endpoints: endpoints, region: region, accessKeyId: credentials.accessKeyId, secretAccessKey: credentials.secretAccessKey, sessionToken: credentials.sessionToken});
  return new AWS.DynamoDB.DocumentClient({service: daxClient});
};

createDaxClient().then((client) => {
  client.get(...);
  ...
}).catch((error) => {
  console.log('Caught an error: ' + error);
});
```

------