This article shows how to create a reusable assume role script that can for example be used by AWS CodeBuild to assume a role in another AWS account.
To accomplish this we need to:
Table of Contents
AWS multi-account setup example
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.
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: "*"
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
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
Here we create an IAM role that has a managed policy. Within the policy, we allow sts:AssumeRole
action on the 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 that’s the assume role script.
Create the Assume role script
The script that we’re going to create assumes the role of 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 into 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"
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-account
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
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
Conclusion
Now you’ve set up a reusable assume role script by finishing the following steps:
- Created an IAM role on the target AWS Account
- Created an IAM role on the source AWS Account
You can now re-use this script in multiple CI/CD pipelines and grant cross-account access to your AWS resources.
Note: 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.