How to create an AWS cross account assume role script

How to create an AWS cross account assume role script

Written on June 16, 2021 by Danny Steenman.

Last updated: June 16, 2021.

6 min read

This article shows how to create a reusable assume role script that can be used by AWS CodeBuild for example to assume a role in another AWS account.

To accomplish this we need to:

Introduction

In this day and age, we're moving more towards AWS multi-account setups where workloads are being managed separately. With a multi-account setup comes a shared services account that acts as a central hub in which AWS Codepipelines are hosted and deploy infrastructure and services to our workload accounts e.g. development, testing, acceptance, and production AWS accounts.

AWS assume role cross-account diagram example with AWS Codebuild and AWS Codepipeline

The example above displays a simple multi-account setup in which a shared services account acts as a hub to deploy resources to the workload accounts. To make it possible for the shared services account to access the other AWS accounts it needs to assume a role on the target account.

In the next step, you'll be shown how to create an IAM role on the target AWS account.

Create an IAM role on the target AWS Account

On the target AWS account e.g. Test we need to create an IAM role that can be assumed by the source AWS account, in this case, it's the shared services account. As an example, we'll use AWS CloudFormation to create a stack that can be deployed to AWS.

AWSTemplateFormatVersion: "2010-09-09" Description: A CloudFormation template that creates a cross-account role that can be assumed by the source (shared services) account. Parameters: Environment: { Description: Environment, Type: String } SourceAccount: { Description: Source AWS account ID, Type: String } S3Bucket: { Description: S3 Bucket that the shared account needs access to, Type: String } Resources: CrossTargetAccountRole: Type: AWS::IAM::Role Properties: RoleName: !Sub crossaccount-${Environment}-role AssumeRolePolicyDocument: Version: "2012-10-17" Statement: - Effect: Allow Principal: AWS: !Sub arn:aws:iam::${SourceAccount}:root Action: sts:AssumeRole Path: "/" Policies: - PolicyName: S3BucketAccess PolicyDocument: Version: "2012-10-17" Statement: - Effect: Allow Action: - s3:Get* - s3:List* - s3:PutObject* - s3:DeleteObject* Resource: - !Sub arn:aws:s3:::${S3Bucket}/* - !Sub arn:aws:s3:::${S3Bucket} - PolicyName: SSMAccess PolicyDocument: Version: "2012-10-17" Statement: - Effect: Allow Action: - ssm:PutParameter Resource: "*"
bash

The CloudFormation template shows an example of an IAM role that grants the Source account access to an S3 bucket in the target account and gives the ability to create SSM Parameters.

Once you deploy it you can get the ARN of the newly created role by running the following AWS CLI command:

➜ aws iam list-roles --output text --query 'Roles[*].[Arn]'|grep crossaccount arn:aws:iam::012345678910:role/crossaccount-tst-role
bash

Create an IAM role on the source AWS Account

Next up we need to have an IAM role on the shared service account that assumes the role we created previously on the target account, so it can access the S3 bucket.

AWSTemplateFormatVersion: 2010-09-09 Description: A CloudFormation template that creates a role that can assume a role on a target AWS account Parameters: CrossAccountRoleTestARN: Type: String Description: "The ARN of role on the target account that the source account (AWS Codebuild) assumes to access its services" Resources: CodeBuildServiceRole: Type: AWS::IAM::Role Properties: Path: / AssumeRolePolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Principal: Service: codebuild.amazonaws.com Action: sts:AssumeRole ManagedPolicyArns: - !Ref CrossAccountAssumePolicy CrossAccountAssumePolicy: Type: AWS::IAM::ManagedPolicy Properties: PolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Action: - sts:AssumeRole Resource: - !Ref CrossAccountRoleTestARN
bash

Here we create an IAM role the has a managed policy. Within the policy, we allow sts:AssumeRole action on the arn:aws:iam::012345678910:role/crossaccount-tst-role that we created in the previous step.

Now you can use this IAM role for AWS CodeBuild or AWS CodePipeline to automatically access resources on other accounts. But until then we need one more thing and thats the assume role script.

Create an Assume role script

The script that we're going to create assumes the role on the target account and uses Simple Token Service (sts) to create temporary AWS credentials. Then it automatically creates an AWS profile that will be stored in the AWS config so that it can be used in a shell environment e.g. AWS CodeBuild step.

You can copy the code below and paste it in a shell script:

#!/bin/bash # Usage # # ./assume-role.sh $CLIENT_ROLE_ARN client # aws s3 ls --profile client --region eu-central-1 ROLE_ARN=$1 OUTPUT_PROFILE=$2 echo "Assuming role $ROLE_ARN" sts=$(aws sts assume-role \ --role-arn "$ROLE_ARN" \ --role-session-name "$OUTPUT_PROFILE" \ --query 'Credentials.[AccessKeyId,SecretAccessKey,SessionToken]' \ --output text) echo "Converting sts to array" sts=($sts) echo "AWS_ACCESS_KEY_ID is ${sts[0]}" aws configure set aws_access_key_id ${sts[0]} --profile $OUTPUT_PROFILE aws configure set aws_secret_access_key ${sts[1]} --profile $OUTPUT_PROFILE aws configure set aws_session_token ${sts[2]} --profile $OUTPUT_PROFILE echo "credentials stored in the profile named $OUTPUT_PROFILE"
bash

How to use the assume role script

You run the assume role script with 2 arguments. First, you insert the role ARN of the target AWS account that you want to assume e.g. arn:aws:iam::012345678910:role/crossaccount-tst-role. The second argument is how you want to name the AWS profile that you're creating e.g. test-account.

./assume_role.sh arn:aws:iam::012345678910:role/crossaccount-tst-role test-acount
bash

Once the AWS profile is created with the STS credentials, we can run our actions on the target AWS account to run S3 commands for example:

aws s3 ls --profile test-account --region eu-central-1
bash

We can also write SSM parameters to the target test account:

aws ssm put-parameter --name Environment --value Test --type "String" --overwrite --profile test-acount --region eu-central-1
bash

Finished!

Now that you've set up a reusable assume role script, you can reuse it in your CI/CD pipelines and grant cross-account access to AWS resources.

If you want to have access to different AWS resources, then make sure to update the policies that are attached to the IAM role on the target account.

Share on Twitter

Subscribe to the newsletter

If you're interested in AWS Cloud, Infrastructure as Code, DevOps, and getting certified in AWS then subscribe to my newsletter to get exclusive tips and tricks on becoming a successful Cloud Engineer.

- subscribers