首页 > 解决方案 > AWS Cloudformation 资源之间的循环依赖

问题描述

我正在尝试创建 sagemaker 角色,作为信任委托人,我需要服务 sagemaker 以及该角色。问题是我收到以下错误:

调用 CreateChangeSet 操作时发生错误(ValidationError):资源之间的循环依赖:[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

我需要以某种方式通过以下主体“arn:aws:iam::${AWS::AccountId}:role/sagemaker-role”

标签: amazon-web-servicesamazon-cloudformationamazon-iam

解决方案


我认为唯一的方法是通过两个阶段自定义资源

  1. 使用“正常”推力策略创建您的角色
  2. 使用自定义资源更新角色

以下是有关如何执行此操作的完整示例代码:


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        


推荐阅读