<

Using boto to consume a service assuming a role within another account with terraform.

I came across a scenario today, when working with a task management platform, where the task runner (which was using python) needed to be able to assume a role, in another account to be able to consume some resources (dynamodb).

Since the platform the task runner was running on was fargate, I looked at the options and used assume role to be able to assume the identity of the secondary account from the running task role, and consume the resources on behalf of it.

Below is a bit of a write up of how I did this:

Create a task role for your fargate task in Account1

resource "aws_iam_role" "task_iam_role" {
  name               = "my-fargate-task-role"
  assume_role_policy = "${file("${path.module}/policies/iam/ecs-task-service-trust.json")}"
}

The assume role policy should look something like this: ecs-task-service-trust.json

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

Create a role for the service you want to consume in Account2

resource "aws_iam_role" "switch_role" {
  name               = "switch-role"
  assume_role_policy = "${data.template_file.trust_policy_file.rendered}"
}

The assume role policy should look something like this: trust_policy_file.json

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "AWS": [
          "arn:aws:iam::${ACCOUNT1_ID}:role/my-fargate-task-role"
        ]
      },
      "Action": "sts:AssumeRole"
    }
  ]
}

The second role should also contain a policy with the permissions to access the resources it needs.

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": ["dynamodb:*"],
      "Resource": "arn:aws:dynamodb:eu-west-1:${ACCOUNT2_ID}:table/*"
    }
  ]
}

Within the python application, create a function that will assume a role, based on the ARN, and return a session pre-authenticated with the switch role

def assumed_role_session(role_arn: str):
    role = boto3.client('sts').assume_role(RoleArn=role_arn, RoleSessionName='switch-role')
    credentials = role['Credentials']
    aws_access_key_id = credentials['AccessKeyId']
    aws_secret_access_key = credentials['SecretAccessKey']
    aws_session_token = credentials['SessionToken']
    return boto3.session.Session(
        aws_access_key_id=aws_access_key_id,
        aws_secret_access_key=aws_secret_access_key,
        aws_session_token=aws_session_token)

Consume the service, such as dynamodb in this example, as the assumed role, from a fargate task within ACCOUNT1

def call_dynamo(*args, **kwargs):
    assumed_session = assumed_role_session('arn:aws:iam::ACCOUNT2_ID:role/switch-role')
    dynamo_client = assumed_session.client('dynamodb')
    response = dynamo_client.list_tables(Limit=100)
    print(str(response))

Testing it out

You can test this easily using the aws cli with a command like this:

AWS_PROFILE=my-profile \ 
  aws sts assume-role \ 
  --role-arn "arn:aws:iam::XXXXXXXX:role/switch-role"

If a set of temporary credentials are returned, then it worked!

Written on September 11, 2019.