0
votes

I'm trying to implement a use case, where a Lambda function can be invoked across-account through an AWS CloudFormation template.

I'm deploying an AWS CloudFormation stack in account B, which has a custom resource that invokes that Lambda function defined in account A. I have created a cross account role for Lambda in account A with account B as a trusted entity. Also, the user in account B has been attached with a policy that allows it to assume this role. Unfortunately, the stack creation fails the access error.

Would appreciate any help. Here's my template for account B:

{
  "AWSTemplateFormatVersion": "2010-09-09",
  "Resources": {
    "GetNwInterfaces":{
      "Type": "Custom::GetNwInterfaces",
      "Properties": {
        "ServiceToken": "arn:aws:lambda:ap-northeast-2:XXXXXXXXXX:function:getini2",
        "region": "us-west-2",
        "uid" : "01100"
      }
    }
  }
}
1
What's the exact error you're getting? Can you share the other template (i.e. the one being used account A)?Aditya

1 Answers

1
votes

Create the Lambda function in account A with the proper permissions, eg, use this cloudformation template:

Parameters:
  OtherAccountId:
    Type: String

Resources:
  TestFunction:
    Type: AWS::Lambda::Function
    Properties:
      Runtime: python2.7
      Handler: index.handler
      Role: !GetAtt TestRole.Arn
      Code:
        ZipFile: !Sub |
          from botocore.vendored import requests
          import json


          def send(event, context, responseStatus, responseData, physicalResourceId):
              responseUrl = event['ResponseURL']

              print responseUrl

              responseBody = {}
              responseBody['Status'] = responseStatus
              responseBody['Reason'] = 'See the details in CloudWatch Log Stream: ' + context.log_stream_name
              responseBody['PhysicalResourceId'] = physicalResourceId or context.log_stream_name
              responseBody['StackId'] = event['StackId']
              responseBody['RequestId'] = event['RequestId']
              responseBody['LogicalResourceId'] = event['LogicalResourceId']
              responseBody['Data'] = responseData

              json_responseBody = json.dumps(responseBody)

              print "Response body:\n" + json_responseBody

              headers = {
                  'content-type' : '',
                  'content-length' : str(len(json_responseBody))
              }

              try:
                  response = requests.put(responseUrl,
                                          data=json_responseBody,
                                          headers=headers)
                  print "Status code: " + response.reason
              except Exception as e:
                  print "send(..) failed executing requests.put(..): " + str(e)

          def handler(event, context):
              print event
              print context

              responseData = {}

              send(event, context, "SUCCESS", responseData, "CustomResourcePhysicalID")

  CrossAccountPermission:
    Type: AWS::Lambda::Permission
    Properties:
      Action: lambda:InvokeFunction
      FunctionName: !Ref TestFunction
      Principal: !Ref OtherAccountId

  TestRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          -
            Effect: Allow
            Principal:
              Service:
                - lambda.amazonaws.com
            Action:
              - sts:AssumeRole
      Policies:
        - PolicyName: AllowAccess
          PolicyDocument:
            Version: 2012-10-17
            Statement:
              - Effect: Allow
                Action:
                  - "logs:*"
                Resource: "arn:aws:logs:*:*:*"

The CrossAccountPermission record is the important one here, there we grant access to account B.

In account B, test it with the following template:

Parameters:
  LambdaArn:
    Type: String

Resources:
  CustomResourceTest:
    Type: Custom::Demo
    Properties:
      ServiceToken: !Ref LambdaArn

There is an additional possible gotcha here: The user/role that is running cloudformation in account B also needs permission to execute the Lambda function, if you are running CF with an admin user that shouldn't be an issue as you would have lambda:* permissions on resource * anyway.