1
votes

I'm trying to create sagemaker role, and as trust principals I need service sagemaker and also that role. Problem is I get following error:

An error occurred (ValidationError) when calling the CreateChangeSet operation: Circular dependency between resources: [SagemakerRole]

SagemakerRole:
Type: 'AWS::IAM::Role'
Properties:
  RoleName: sagemaker-role
  AssumeRolePolicyDocument:
    Version: 2012-10-17
    Statement:
      - Effect: Allow
        Principal:
          Service:
            - sagemaker.amazonaws.com
        Action: 'sts:AssumeRole'
      - Effect: Allow
        Principal:
          AWS:
            - !Ref SagemakerRole
        Action: 'sts:AssumeRole'
  Path: /
  ManagedPolicyArns:
    - arn:aws:iam::aws:policy/AmazonS3FullAccess            
    - arn:aws:iam::aws:policy/AmazonSageMakerFullAccess

I need to pass somehow following principal "arn:aws:iam::${AWS::AccountId}:role/sagemaker-role"

1
Is this something that SM requires? Its rather unusual to have a role that assumes itself.Marcin
I have docker container running R(Bring your own container) and in R code I need to assume this role to be able to store data to S3. This works btw when I write trust policy in console. I wanted to save it as cloudformation script and I do not know how.datahack
Instead of !Ref SagemakerRole, try !Sub 'arn:${AWS::Partition}:iam::${AWS::AccountId}:role/sagemaker-role'jarmod
@jarmod it won't work because while creating the policy it checks for the role existence. <Invalid principal in policy: "AWS":"arn:aws:iam::1234567890:role/mysagemaker"> error message when you use !sub function for constructing the role arnsamtoddler
Running bash commands in AWS CloudFormation templates this might another way to update the role's trust policy though.samtoddler

1 Answers

1
votes

I think the only way to do is through custom resource in two stages:

  1. Create your role with "normal" thrust policy
  2. Update the role using custom resource

Below is fully working exemplary code on how to do it:


Resources:

  SagemakerRole:
    Type: 'AWS::IAM::Role'
    Properties:
      RoleName: sagemaker-role
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Principal:
              Service:
                - sagemaker.amazonaws.com
            Action: 'sts:AssumeRole'
      Path: /
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/AmazonS3FullAccess            
        - arn:aws:iam::aws:policy/AmazonSageMakerFullAccess


  LambdaBasicExecutionRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Statement:
        - Effect: Allow
          Principal:
            Service: lambda.amazonaws.com
          Action: sts:AssumeRole
      Path: /
      Policies:
       - PolicyName: UpdateAssumePolicy
         PolicyDocument:
           Version: 2012-10-17
           Statement:          
             - Effect: Allow
               Action: 
                  - iam:UpdateAssumeRolePolicy
                  - iam:GetRole
               Resource: !GetAtt SagemakerRole.Arn        
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole

  MyCustomResource:
    Type: Custom::RoleAssumesItself
    Properties:
      ServiceToken: !GetAtt MyCustomFunction.Arn
      RoleName: !Ref SagemakerRole

  MyCustomFunction:
    Type: AWS::Lambda::Function
    Properties:
      Handler: index.lambda_handler
      Timeout: 10
      Role: !GetAtt 'LambdaBasicExecutionRole.Arn'
      Runtime: python3.7
      Code:
        ZipFile: |
          import json
          import cfnresponse
          import boto3

          iam = boto3.resource('iam')

          def lambda_handler(event, context):

            print(json.dumps(event, default=str))
            
            try:

              responseData = {}

              if event['RequestType'] in ["Create"]:                      
                
                role_name = event['ResourceProperties']['RoleName']                

                role = iam.Role(role_name)
                
                current_permissions = role.assume_role_policy_document
                
                print(current_permissions)
                
                current_permissions['Statement'].append(
                      {'Effect': 'Allow', 
                        'Principal': 
                          {'AWS': role.arn}, 
                        'Action': 'sts:AssumeRole'
                      })
                      
                #print(current_permissions)
                
                response = role.AssumeRolePolicy().update(
                      PolicyDocument=json.dumps(current_permissions))
                
                print(response)

                cfnresponse.send(event, context, 
                                 cfnresponse.SUCCESS, responseData)

              else:
                print('Unexpected RequestType!') 
                cfnresponse.send(event, context, 
                                  cfnresponse.SUCCESS, responseData)

            except Exception as err:

              print(str(err))
              responseData = {"Data": str(err)}
              cfnresponse.send(event,context, 
                               cfnresponse.FAILED,responseData)
            return