Search code examples
aws-lambdaamazon-dynamodbamazon-iamaws-access-policyassume-role

How to access aws resources created in other account


In my use case, I want to access DynamoDB table created in AWS account A and Lambda created in account B. For this I have followed many references on Internet which suggests me to use AWS assume role feature. I have added following permission in Lambda execution role

{
  "Version": "2012-10-17",
  "Statement": {
    "Effect": "Allow",
    "Action": "sts:AssumeRole",
    "Resource": "arn:aws:iam::aws-account-A-number:role/test-db-access"
   }
}

Following is the trust relationship of Lambda

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
           "Service": "lambda.amazonaws.com"
       },
      "Action": "sts:AssumeRole"
    },
    {
      "Effect": "Allow",
      "Principal": {
      "AWS": "arn:aws:iam::aws-account-A-number:root"
       },
     "Action": "sts:AssumeRole"
    }
  ]
}

In account A, I have created role(test-db-access) for allowing others to access this account and added AmazonDynamoDBFullAccess and AdministratorAccess policies. Following is the trust relationship I have added in this account

{
  "Version": "2012-10-17",
   "Statement": [
     {
        "Effect": "Allow",
        "Principal": {
        "AWS": "arn:aws:iam::aws-account-B-number:role/sam-dev-test- 
            TestLambda-LambdaRole-1FH5IC18J0MYT"
         },
       "Action": "sts:AssumeRole"
     },
     {
       "Effect": "Allow",
       "Principal": {
           "Service": "lambda.amazonaws.com"
         },
       "Action": "sts:AssumeRole"
     }
  ]
}

Following is the Java code I have added to access Dynamo DB instance

AssumeRoleRequest assumeRequest = new AssumeRoleRequest()
            .withRoleArn("arn:aws:iam::aws-account-A-number:role/test-db-access").withRoleSessionName("cross_acct_lambda").withDurationSeconds(900);
final AWSSecurityTokenService sts = AWSSecurityTokenServiceClientBuilder.standard().withRegion("eu-west-1").build();
final Credentials credentials = sts.assumeRole(assumeRequest).getCredentials();

following is the crash log coming on executing lambda

{ "errorMessage": "User: arn:aws:sts::aws-account-B-number:assumed-role/sam-dev-test-TestLambda-LambdaRole-1FH5IC18J0MYT/sam-dev-test-TestLambda-LambdaFunction-73TVOBN6VXXX is not authorized to perform: sts:AssumeRole on resource: arn:aws:iam::aws-account-A-number:role/test-db-access (Service: AWSSecurityTokenService; Status Code: 403; Error Code: AccessDenied; Request ID: 100bd3a3-3f9c-11ea-b642-d3b4d9ff35de)", "errorType": "com.amazonaws.services.securitytoken.model.AWSSecurityTokenServiceException" }


Solution

  • It appears your requirements are:

    • From an AWS Lambda function in Account-B, access a DynamoDB table in Account-A

    To reproduce your situation I did the following:

    • Created a DynamoDB table in Account-A
    • Created an IAM Role (Role-A) in Account-A with the following policy:
    {
        "Version": "2012-10-17",
        "Statement": [
            {
                "Effect": "Allow",
                "Action": "dynamodb:*",
                "Resource": "arn:aws:dynamodb:ap-southeast-2:<Account-A>:table/Inventory"
            }
        ]
    }
    

    And this Trust Relationship (pointing to the Role created in the next step):

    {
      "Version": "2012-10-17",
      "Statement": [
        {
          "Effect": "Allow",
          "Principal": {
            "AWS": "arn:aws:iam::<Account-B>:role/role-b"
          },
          "Action": "sts:AssumeRole"
        }
      ]
    }
    
    • Created an IAM Role (Role-B) in Account-B for use with the Lambda function, with this policy:
    {
        "Version": "2012-10-17",
        "Statement": [
            {
                "Effect": "Allow",
                "Action": "sts:AssumeRole",
                "Resource": "arn:aws:iam::<Account-A>:role/role-a"
            }
        ]
    }
    

    And with this Trust Relationship:

    {
      "Version": "2012-10-17",
      "Statement": [
        {
          "Effect": "Allow",
          "Principal": {
            "Service": "lambda.amazonaws.com"
          },
          "Action": "sts:AssumeRole"
        }
      ]
    }
    
    • Created an AWS Lambda function in Account-B that will:
      • Assume Role-A in Account-A
      • Access the DynamoDB table in Account-A

    I'm a Python person, so my function is:

    import boto3
    
    def lambda_handler(event, context):
    
        # Assume Role
        sts_client = boto3.client('sts')
    
        response = sts_client.assume_role(
            RoleArn='arn:aws:iam::<Account-A>:role/stack-role-a', 
            RoleSessionName='bar')
    
        session = boto3.Session(
            aws_access_key_id=response['Credentials']['AccessKeyId'],
            aws_secret_access_key=response['Credentials']['SecretAccessKey'],
            aws_session_token=response['Credentials']['SessionToken']
        )
    
        # Update DynamoDB
        dynamodb_client = session.client('dynamodb')
    
        dynamodb_client.update_item(
            TableName='Inventory',
            Key={'Item': {'S': 'foo'}},
            UpdateExpression="ADD #count :increment",
            ExpressionAttributeNames = {
                '#count': 'count'
            },
            ExpressionAttributeValues = {
                ':increment': {'N': '1'},
            }
        ) 
    

    I tested this by clicking Test on the Lambda function in Account-B. It successfully updated the DynamoDB table in Account-A.

    I suspect the difference is with your trust policies, which appear to be a little bit different.