Search code examples
amazon-web-servicesaws-lambdaaws-cloudformationaws-cloudformation-custom-resource

How to Display a Resource From a Custom Lambda


I have a custom CloudFormation resource that creates an S3 bucket if it doesn't exist. Here is the code:

  S3CustomResource:
Type: Custom::S3CustomResource
Properties:
  ServiceToken: !GetAtt AWSLambdaFunction.Arn
  the_bucket: !Ref S3BucketName

  AWSLambdaFunction:
 Type: "AWS::Lambda::Function"
 Properties:
   Description: "Work with S3 Buckets!"
   FunctionName: !Sub '${AWS::StackName}-${AWS::Region}-lambda'
   Handler: index.handler
   Role: !GetAtt AWSLambdaExecutionRole.Arn
   Timeout: 360
   Runtime: python3.6
   Code:
     ZipFile: |
      import boto3
      import cfnresponse
      def handler(event, context):
          # Init ...
          the_event = event['RequestType']
          print("The event is: ", str(the_event))
          response_data = {}
          s_3 = boto3.client('s3')
          # Retrieve parameters
          the_bucket = event['ResourceProperties']['the_bucket']
          try:
              if the_event in ('Create', 'Update'):
                  print("Requested to create S3 bucket: ", str(the_bucket))
                  for bucket_name in the_bucket:
                      print("Creating: ", str(bucket_name))
                      s_3.create_bucket(Bucket=the_bucket)
              elif the_event == 'Delete':
                  print("Whoopsie, this bucket has some seriuos information in it - let's not delete it")
              # Everything OK... send the signal back
              print("Execution succesfull!")
              cfnresponse.send(event,
                               context,
                               cfnresponse.SUCCESS,
                               response_data)
          except Exception as e:
              print("Execution failed...")
              print(str(e))
              response_data['Data'] = str(e)
              cfnresponse.send(event,
                               context,
                               cfnresponse.FAILED,
                               response_data)

I would like to reference the ARN of this bucket in my other CloudFormation resources. The S3CustomResource resource only shows the PhysicalID of the CloudWatch Log name. How can I get CloudFormation to show the bucket ARN as a PhysicalID in the Resource tab?


Solution

  • Bucket ARNs have fixed format, so you just use that in your code (assuming aws partition):

    bucket_arn = "arn:aws:s3:::" + bucket_name
    

    and you return them in response_data. Below is fully working, amended code that can create multiple buckets using custom resource and returns their ARN to cloudformation for future use. The code has a lot of room for improvement, e.g. return arns of buckets if they exist, check if buckets were correctly created and more. Thus its not ideal code, but it works and shows the key elements:

    • passing lists to custom resource
    • returning lists of arns to CFN
    • accessing the list in CFN
    
    Parameters:
    
      S3BucketName:
        Type: CommaDelimitedList  
        Default: test-bucket-312ddfff,test-bucket3333-312ddfff
    
    Resources:
    
      S3CustomResource:
        Type: Custom::S3CustomResource
        Properties:
          ServiceToken: !GetAtt AWSLambdaFunction.Arn
          the_bucket: !Ref S3BucketName
    
      AWSLambdaFunction:
         Type: "AWS::Lambda::Function"
         Properties:
           Description: "Work with S3 Buckets!"
           FunctionName: !Sub '${AWS::StackName}-${AWS::Region}-lambda'
           Handler: index.handler
           Role: !GetAtt AWSLambdaExecutionRole.Arn
           Timeout: 360
           Runtime: python3.6
           Code:
             ZipFile: |
              import boto3
              import cfnresponse
    
              s_3 = boto3.client('s3')
              s3_waiter = s_3.get_waiter('bucket_exists')
    
              def handler(event, context):
                  # Init ...
                  
                  print(event)
    
                  the_event = event['RequestType']
                  print("The event is: ", str(the_event))
                  
                  response_data = {}              
    
                  # Retrieve parameters
                  the_bucket = event['ResourceProperties']['the_bucket']
                  
                  print("the_bucket", the_bucket)
                  
                  bucket_arns = []
    
                  try:
                      if the_event in ('Create', 'Update'):
    
                          print("Requested to create S3 bucket: ", 
                                 str(the_bucket))
    
                          for bucket_name in the_bucket:
                          
                              print("Creating: ", str(bucket_name))
                              
                              s_3.create_bucket(Bucket=bucket_name)
                              
                              s3_waiter.wait(Bucket=bucket_name)
                              
                              bucket_arn = "arn:aws:s3:::" + bucket_name
                              
                              bucket_arns.append(bucket_arn)
    
                      elif the_event == 'Delete':
                          print("Whoopsie, this bucket has some"
                                "seriuos information in it "
                                "- let's not delete it")
                          cfnresponse.send(event, context,
                                           cfnresponse.SUCCESS,
                                           response_data)
                          return
    
                      # Everything OKindex... send the signal back
    
                      print("Execution successful!")
                      
                      response_data['arns'] = ','.join(bucket_arns)
    
                      cfnresponse.send(event,
                                       context,
                                       cfnresponse.SUCCESS,
                                       response_data)
    
                  except Exception as e:
    
                      print("Execution failed...")
                      print(str(e))
                      
                      response_data['Data'] = str(e)
    
                      cfnresponse.send(event,
                                       context,
                                       cfnresponse.FAILED,
                                       response_data)
                   
                   
      AWSLambdaExecutionRole:
        Type: AWS::IAM::Role
        Properties:
          AssumeRolePolicyDocument:
            Version: '2012-10-17'               
            Statement:
              - Effect: Allow
                Principal: {'Service': ['lambda.amazonaws.com']}
                Action: ['sts:AssumeRole']
          ManagedPolicyArns:
            - arn:aws:iam::aws:policy/AWSLambdaExecute
            - arn:aws:iam::aws:policy/AmazonS3FullAccess
          Path: '/'
          
          
    Outputs:
    
      BucketArns:
        Value: !GetAtt S3CustomResource.arns