# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. # SPDX-License-Identifier: MIT-0 AWSTemplateFormatVersion: '2010-09-09' Description: 'Amazon Redshift user defined functions for Amazon Location Service APIs' Parameters: OriginBucketName: AllowedPattern: ^[0-9a-zA-Z]+([0-9a-zA-Z-]*[0-9a-zA-Z])*$ Default: redshift-udf-als-dev Type: String OriginKeyPrefix: AllowedPattern: ^[0-9a-zA-Z-/]*$ Default: resources/ Type: String PlaceIndex: AllowedPattern: ^[0-9a-zA-Z-._]*$ Type: String Resources: LambdaZipsBucket: Type: AWS::S3::Bucket Properties: VersioningConfiguration: Status: Enabled BucketEncryption: ServerSideEncryptionConfiguration: - ServerSideEncryptionByDefault: SSEAlgorithm: AES256 CopyZips: Type: Custom::CopyZips Properties: ServiceToken: !GetAtt 'CopyZipsFunction.Arn' DestBucket: !Ref 'LambdaZipsBucket' SourceBucket: !Ref 'OriginBucketName' Prefix: !Ref 'OriginKeyPrefix' Objects: - lambdas/geocoding.zip - lambdas/reverse_geocoding.zip CopyZipsRole: 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/service-role/AWSLambdaBasicExecutionRole Path: / Policies: - PolicyName: lambda-copier PolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Action: - s3:GetObject Resource: - !Sub 'arn:aws:s3:::${OriginBucketName}/${OriginKeyPrefix}*' - Effect: Allow Action: - s3:PutObject - s3:DeleteObject Resource: - !Sub 'arn:aws:s3:::${LambdaZipsBucket}/${OriginKeyPrefix}*' CopyZipsFunction: Type: AWS::Lambda::Function Properties: Description: Copies objects from a source S3 bucket to a destination Handler: index.handler Runtime: python2.7 Role: !GetAtt 'CopyZipsRole.Arn' Timeout: 240 Code: ZipFile: | import json import logging import threading import boto3 import cfnresponse def copy_objects(source_bucket, dest_bucket, prefix, objects): s3 = boto3.client('s3') for o in objects: key = prefix + o copy_source = { 'Bucket': source_bucket, 'Key': key } print('copy_source: %s' % copy_source) print('dest_bucket = %s'%dest_bucket) print('key = %s' %key) s3.copy_object(CopySource=copy_source, Bucket=dest_bucket, Key=key) def delete_objects(bucket, prefix, objects): s3 = boto3.client('s3') objects = {'Objects': [{'Key': prefix + o} for o in objects]} s3.delete_objects(Bucket=bucket, Delete=objects) def timeout(event, context): logging.error('Execution is about to time out, sending failure response to CloudFormation') cfnresponse.send(event, context, cfnresponse.FAILED, {}, None) def handler(event, context): # make sure we send a failure to CloudFormation if the function # is going to timeout timer = threading.Timer((context.get_remaining_time_in_millis() / 1000.00) - 0.5, timeout, args=[event, context]) timer.start() print('Received event: %s' % json.dumps(event)) status = cfnresponse.SUCCESS try: source_bucket = event['ResourceProperties']['SourceBucket'] dest_bucket = event['ResourceProperties']['DestBucket'] prefix = event['ResourceProperties']['Prefix'] objects = event['ResourceProperties']['Objects'] if event['RequestType'] == 'Delete': delete_objects(dest_bucket, prefix, objects) else: copy_objects(source_bucket, dest_bucket, prefix, objects) except Exception as e: logging.error('Exception: %s' % e, exc_info=True) status = cfnresponse.FAILED finally: timer.cancel() cfnresponse.send(event, context, status, {}, None) GeocodeFunctionRole: 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/service-role/AWSLambdaBasicExecutionRole Policies: - PolicyName: GeoocodingPolicy PolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Action: - geo:SearchPlaceIndexForText Resource: !Sub 'arn:aws:geo:${AWS::Region}:${AWS::AccountId}:place-index/${PlaceIndex}' ReverseGeocodeFunctionRole: 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/service-role/AWSLambdaBasicExecutionRole Policies: - PolicyName: ReverseGeoocodingPolicy PolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Action: - geo:SearchPlaceIndexForPosition Resource: !Sub 'arn:aws:geo:${AWS::Region}:${AWS::AccountId}:place-index/${PlaceIndex}' GeocodeFunction: DependsOn: CopyZips Type: AWS::Lambda::Function Properties: FunctionName: RedshiftUDFGeocodeFunction Description: A Lambda to work as a Redshift UDF to geocode values passed as parameters Handler: geocoding_lambda/lambda_function.handler Runtime: python3.7 Role: !GetAtt 'GeocodeFunctionRole.Arn' Timeout: 300 Code: S3Bucket: !Ref 'LambdaZipsBucket' S3Key: !Sub '${OriginKeyPrefix}lambdas/geocoding.zip' Environment: Variables: PLACE_INDEX: !Ref 'PlaceIndex' ReverseGeocodeFunction: DependsOn: CopyZips Type: AWS::Lambda::Function Properties: FunctionName: RedshiftUDFReverseGeocodeFunction Description: A Lambda to work as a Redshift UDF to reverse geocode values passed as parameters Handler: reverse_geocoding_lambda/lambda_function.handler Runtime: python3.7 Role: !GetAtt 'ReverseGeocodeFunctionRole.Arn' Timeout: 300 Code: S3Bucket: !Ref 'LambdaZipsBucket' S3Key: !Sub '${OriginKeyPrefix}lambdas/reverse_geocoding.zip' Environment: Variables: PLACE_INDEX: !Ref 'PlaceIndex' RedshiftGeocodeFunctionRole: Type: AWS::IAM::Role Properties: RoleName: RedshiftGeocodeFunctionRole AssumeRolePolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Principal: Service: redshift.amazonaws.com Action: sts:AssumeRole Policies: - PolicyName: RedshiftGeocodeFunctionPolicy PolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Action: - lambda:InvokeFunction Resource: !Sub 'arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:RedshiftUDFGeocodeFunction' RedshiftReverseGeocodeFunctionRole: Type: AWS::IAM::Role Properties: RoleName: RedshiftReverseGeocodeFunctionRole AssumeRolePolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Principal: Service: redshift.amazonaws.com Action: sts:AssumeRole Policies: - PolicyName: RedshiftReverseGeocodeFunctionPolicy PolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Action: - lambda:InvokeFunction Resource: !Sub 'arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:RedshiftUDFReverseGeocodeFunction'