AWS Security Blog

Delegating API Access to AWS Services Using IAM Roles

Suppose you run a research lab and you dump a terabyte or so of data into Amazon DynamoDB for easy processing and analysis. Your colleagues at other labs and in the commercial sphere have become aware of your research and would like to reproduce your results and perform further analysis on their own.

AWS supports this very important (not to mention powerful) use case with AWS Identity and Access Management (IAM) roles in combination with IAM users to enable cross-account API access or delegate API access within an account.  This functionality gives you better control and simplifies access management when you are managing services and resources across multiple AWS accounts. You can enable cross-account API access or delegate API access within an account or across multiple accounts without having to share long-term security credentials.

When you assume an IAM role, you get a set of temporary security credentials that have the permissions associated with the role. You use these temporary security credentials instead of your long-term security credentials in calls to AWS services. You then interact with the service with the permissions granted to the IAM role you assumed.

Let’s take a closer look at how you might use this in practice. 

How do I grant users API access across AWS accounts?

Let’s assume your company has two AWS accounts:

  • research@example.com is where data from several research projects are stored
  • aws@example.com is your company’s main account where you have created most of your IAM users

Let’s also assume that a developer is defined as IAM user “Joe” in aws@example.com. Joe needs read-only access to data stored in DynamoDB tables that are in research@example.com.

Enabling cross-account API access requires establishing a trust relationship between the two accounts. (In our example, that’s between aws@example.com and research@example.com.)

Diagram illustrating the trusted entity and trusting entity

Account administrators can establish the trust relationship in two steps:

  1. Configure the trusting entity (the account that owns the resources)
  2. Configure the trusted entity (the account where the IAM user is managed)

1. Configure the trusting entity

To establish the trust relationship, the administrator for research@example.com performs the following steps to grant IAM users in aws@example.com the ability to assume a role that grants read-only access to DynamoDB:

  1. Sign in to the IAM console and clicks the Roles link.
  2. Click Create a role
  3. Enter a name for the IAM role. In this example, the role is named DynamoDB-ReadOnly-role.

Screenshot showing Role Name box

4. Expand Roles for Cross-Account Access and select the role type Provide access between AWS accounts you own.

Screenshot of selecting the role type "Provide access between AWS accounts you own"

5. Add aws@example.com as the account from which IAM users can access research@example.com. Do this by specifying the AWS account ID for aws@example.com. Find the AWS Account ID from the My Account page in the AWS Management Console drop-down.

Screenshot showing the Account ID box

6. Assign a policy to the IAM role that grants read-only access to DynamoDB. Do this by picking theAmazon DynamoDB Read Only Access policy from the policy template list.

2. Configure the trusted entity

Now that the admin of research@example.com has granted access to aws@example.com, the administrator of aws@example.com has to go in and explicitly grant access to the individual IAM users which should have access to assume the DynamoDB-ReadOnly-role. This is because IAM users are secure by default, meaning that they only have the permissions explicitly granted by their administrators. To do this, the administrator for aws@example.com performs the following steps:

  1. Sign in to the IAM console.
  2. Assign a policy to Joe (or to a group that Joe belongs to) that grants the right to call AssumeRole on the DynamoDB-ReadOnly-role. The policy below is an example of such a policy (with 111122223333 as the account ID for research@example.com):
{ "Statement": [
  {
   "Effect": "Allow",
   "Action": "sts:AssumeRole",
   "Resource": "arn:aws:iam::111122223333:role/DynamoDB-ReadOnly-role"
  }
 ]
}

How does Joe use the IAM role to access resources in the research@example.com?

The application that Joe uses to accesses data in the research@example.com account will make API calls to assume the IAM role and use the role’s temporary security credentials when interacting with the DynamoDB table in research@example.com. The following diagram illustrates the flow.

Diagram illustrating the process flow

This simple sample code, using the AWS Java SDK, illustrates the individual steps for assuming a role and using the roles temporary security credentials to access DynamoDB.

import java.util.HashMap;

import com.amazonaws.services.securitytoken.AWSSecurityTokenServiceClient;
import com.amazonaws.services.securitytoken.model.AssumeRoleRequest;
import com.amazonaws.services.securitytoken.model.AssumeRoleResult;
import com.amazonaws.services.dynamodb.AmazonDynamoDBClient;
import com.amazonaws.services.dynamodb.model.*;
import com.amazonaws.auth.*;

public class AssumeRoleDemo {
    private static final String ROLE_ARN =
    "arn:aws:iam::111122223333:role/DynamoDB-ReadOnly-role";
    private static final String TABLE_NAME = "TestProject";
    private static final String KEY_VALUE = "1234567890";

    private static AWSCredentials longTermCredentials_;
   
    private static void init() throws Exception {
    // acquire long term credentials from the properties file
    longTermCredentials_ = new PropertiesCredentials(AssumeRoleDemo.class.getResourceAsStream("AwsCredentials.properties"));
    }
   
    public static void main(String[] args) throws Exception {
        init();

    // Step 1. Use Joe.s long-term credentials to call the
    // AWS Security Token Service (STS) AssumeRole API, specifying 
    // the ARN for the role DynamoDB-RO-role in research@example.com.

        AWSSecurityTokenServiceClient stsClient = new
            AWSSecurityTokenServiceClient(longTermCredentials_);
       
        AssumeRoleRequest assumeRequest = new AssumeRoleRequest()
            .withRoleArn(ROLE_ARN)
            .withDurationSeconds(3600)
            .withRoleSessionName("demo");
       
        AssumeRoleResult assumeResult =
        stsClient.assumeRole(assumeRequest);

    // Step 2. AssumeRole returns temporary security credentials for 
    // the IAM role.

        BasicSessionCredentials temporaryCredentials =
        new BasicSessionCredentials(
                    assumeResult.getCredentials().getAccessKeyId(),
                    assumeResult.getCredentials().getSecretAccessKey(),
                    assumeResult.getCredentials().getSessionToken());

    // Step 3. Make DynamoDB service calls to read data from a 
    // DynamoDB table, stored in research@example.com, using the 
    // temporary security credentials from the DynamoDB-ReadOnly-role 
    // that were returned in the previous step.

        AmazonDynamoDBClient dynamoClient = new AmazonDynamoDBClient(temporaryCredentials);
       
        GetItemResult result = dynamoClient.getItem(new GetItemRequest(TABLE_NAME, new Key(new AttributeValue(KEY_VALUE))));
    }
}

For additional information about this feature visit the Delegating API Access by Using Roles section in the Using IAM guide.

Enabling cross-account API access is another step in the evolution of IAM roles. In the future, we will evaluate integrating the ability to assume roles into the AWS Management Console as well as enabling additional AWS services to integrate with roles.

If you have specific feature requests or want to tell us about your use case please post to the IAM forum. As always, we are eager to hear about how customers use AWS IAM to control access to your AWS resources.

– Anders