Search code examples
amazon-web-servicesaws-lambdaaws-cloudformationassume-role

AWS lambda cannot assume a role triggered by cloudformation


Lambda cannot assume a cross-account role when triggered through a CloudFormation script.

I have a CloudFormation script which creates and triggers a Lambda function in account A. This function needs to copy an object from account B. I am using role base cross-account access.

On account B, I have the following role, For testing purposes, I'm using S3 full access.

CrossAccountAccessRole: 
    Type: 'AWS::IAM::Role'
    Properties: 
      RoleName: 'CrossAccountAccessRole'
      Path: '/'
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
        - Effect: Allow
          Principal: 
            AWS: !Sub 'arn:aws:iam::<AccountA>:role/CustomerCrossAccountAccessRole'
          Action: sts:AssumeRole  
  CrossAccountAccessPolicy:
    Type: "AWS::IAM::ManagedPolicy"
    Properties:
      Path: '/'
      ManagedPolicyName: CrossAccountAccessPolicy
      Roles: 
        - !Ref CrossAccountAccessRole
      PolicyDocument:
        Version: "2012-10-17"
        Statement:       
        - Effect: Allow
          Action:
            - s3:*  

On account A, I have the following role with lambda execution, S3 full access and assumed role policies.

  CustomerCrossAccountAccessRole:
    Type: 'AWS::IAM::Role'
    Properties:
      RoleName: CustomerCrossAccountAccessRole
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Principal:
              Service: lambda.amazonaws.com
            Action: sts:AssumeRole
      Path: /
      Policies:
        - PolicyName: LambdaBasicExecutionPolicy
          PolicyDocument:
            Version: 2012-10-17
            Statement:
              - Effect: Allow
                Action:
                  - 'logs:CreateLogGroup'
                  - 'logs:CreateLogStream'
                  - 'logs:PutLogEvents'
                Resource: '*'
        - PolicyName: LambdaSourceBucketPolicy
          PolicyDocument:
            Version: 2012-10-17
            Statement:
              Effect: Allow
              Action: 's3:*'
              Resource: '*'
        - PolicyName: LambdaAssumeRolePolicy
          PolicyDocument:
            Version: '2012-10-17'
            Statement:
            - Effect: Allow
              Action: sts:AssumeRole
              Resource: !Sub 'arn:aws:iam::<AccountA>:role/CrossAccountAccessRole'

I run this python script inside my lambda function,

    try:
        print('Assume role of a cross account access role')
        boto_sts_client=boto3.client('sts', region_name='ap-southeast-2')
        stsresponse = boto_sts_client.assume_role(
            RoleSessionName = 'CrossAccountAccessSession',
            RoleArn = 'arn:aws:iam::<AccountA>:role/CrossAccountAccessRole',
                DurationSeconds = 3000
        )

        s3_assumed_client = boto3.client(
            's3',
            region_name='ap-southeast-2',
            aws_access_key_id = stsresponse["Credentials"]["AccessKeyId"],
            aws_secret_access_key = stsresponse["Credentials"]["SecretAccessKey"],
            aws_session_token = stsresponse["Credentials"]["SessionToken"]
        )
        s3_assumed_client.download_file(<BucketName>, <FilePath>,<FileName>)
    except:
        traceback.print_exc()

The CloudFormation script returns the following error,

botocore.exceptions.ClientError: An error occurred (AccessDenied) when calling the AssumeRole operation: Access denied

But if I run the same Lambda (created using CloudFormation script) in test mode (using "Test" button on the AWS console), it downloads the file without any error.

Thanks


Solution

  • Your policy contains the following mistakes:

    • Trust policy should specify the account-A as a Principal, meaning that authorized users from this account can use the CrossAccountAccessRole role. You cannot specify ARN of users/roles in different accounts directly in policies.
    • The administrators in account-A must attach a policy that allows the role to call AssumeRole for the ARN of CrossAccountAccessRole.
    • Make sure that you assigned CustomerCrossAccountAccessRole to Lambda when creating it.
    CrossAccountAccessRole: 
        Type: 'AWS::IAM::Role'
        Properties: 
          RoleName: 'CrossAccountAccessRole'
          Path: '/'
          AssumeRolePolicyDocument:
            Version: '2012-10-17'
            Statement:
            - Effect: Allow
              Principal: 
                AWS: 'arn:aws:iam::<AccountA>:root'
    ...
    
    - PolicyName: LambdaAssumeRolePolicy
          PolicyDocument:
            Version: '2012-10-17'
            Statement:
            - Effect: Allow
              Action: sts:AssumeRole
              Resource: 'arn:aws:iam::<AccountB>:role/CrossAccountAccessRole'
    
    stsresponse = boto_sts_client.assume_role(
       RoleSessionName = 'CrossAccountAccessSession',
       RoleArn = 'arn:aws:iam::<Account-B>:role/CrossAccountAccessRole',
       DurationSeconds = 3000
    )
    

    You wrote Account-A in few places (ARNs) where it should actually be Account-B but I guess these are typos.