I have a input parameter that is a list of role names:
Parameters:
UserRoles:
Type: CommaDelimitedList
Default: ""
Now I want to use these roles in a policy document principal. If it would be only 1 Role, I would do:
Principal:
AWS:
- !Join
- ''
- - 'arn:aws:iam::'
- !Ref 'AWS::AccountId'
- ':role/'
- !Ref UserRole
But now I want to do that for a variable number of roles. So I need some sort of "Fn::Map" function on the list of strings allowing me to transform the role names into Arns.
Is that possible?
There's no immediate solution using CloudFormation native constructs, however you can build one by using a macro.
You can find a full example below. In summary, the solution has two components:
The example below addresses the direct question being asked here, however it should be useful to the general reader who wants to do custom string manipulation.
Building a custom macro for string manipulation
The following template creates a macro backed by a lambda function. When the macro is invoked, the lambda function is executed.
The macro (and thus the lambda), takes as inputs the list of comma separated roles (e.g. role1,role2
) and the AWS account ID, and returns the formatted IAM roles (e.g. [arn:aws:iam::12345678910:role/role1,arn:aws:iam::12345678910:role/role2]
).
This is the full template:
AWSTemplateFormatVersion: 2010-09-09
Resources:
TransformExecutionRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Principal:
Service: [lambda.amazonaws.com]
Action: ['sts:AssumeRole']
Path: /
Policies:
- PolicyName: root
PolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Action: ['logs:*']
Resource: 'arn:aws:logs:*:*:*'
TransformFunction:
Type: AWS::Lambda::Function
Properties:
Code:
ZipFile: |
import traceback
def handler(event, context):
response = {
"requestId": event["requestId"],
"status": "success"
}
try:
role_names = event["params"]["RoleNames"]
account_id = str(event["params"]["AccountId"])
role_formatter = lambda role : "arn:aws:iam::" + account_id + ":role/" + role
formatted_roles = list(map(role_formatter,role_names))
response["fragment"] = formatted_roles
except Exception as e:
traceback.print_exc()
response["status"] = "failure"
response["errorMessage"] = str(e)
return response
Handler: index.handler
Runtime: python3.6
Role: !GetAtt TransformExecutionRole.Arn
TransformFunctionPermissions:
Type: AWS::Lambda::Permission
Properties:
Action: 'lambda:InvokeFunction'
FunctionName: !GetAtt TransformFunction.Arn
Principal: 'cloudformation.amazonaws.com'
Transform:
Type: AWS::CloudFormation::Macro
Properties:
Name: 'FormatIamRoles'
Description: Provides various string processing functions
FunctionName: !GetAtt TransformFunction.Arn
Consuming a custom macro for string manipulation
The following template illustrates how the custom macro previously deployed can be used for creating a policy for an S3 bucket.
The template takes as inputs the name of an S3 bucket, and a comma separated list of IAM role names.
The macro (see usage of 'Fn::Transform'
), takes as inputs the comma separated list of IAM role names and the AWS account id. It returns the formatted list of IAM roles and uses it to create the policy for the specified S3 bucket.
Parameters:
UserRoles:
Type: CommaDelimitedList
Default: "role1,role2"
MyBucket:
Type: String
Default: my-bucket
Resources:
MyBucketPolicy:
Type: AWS::S3::BucketPolicy
Properties:
Bucket: !Ref MyBucket
PolicyDocument:
Version: 2012-10-17
Statement:
- Sid: MyRoleAllow
Effect: Allow
Principal:
AWS:
'Fn::Transform':
- Name: 'FormatIamRoles'
Parameters:
RoleNames: !Ref UserRoles
AccountId: !Ref 'AWS::AccountId'
Action:
- s3:*
Resource: !Sub arn:aws:s3:::${MyBucket}/*
on completion of the stack deployment, you'll find that the IAM roles were added to the bucket:
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With