def handler(event, context): print(f"Received event:{json.dumps(event)}") domain_name = event['ResourceProperties']['DomainName'] request_type = event['RequestType'] if 'AwsRegion' in event['ResourceProperties']: os.environ['ACM_REGION'] = event['ResourceProperties']['AwsRegion'] else: os.environ['ACM_REGION'] = os.environ['AWS_REGION'] try: if request_type == 'Create': # create and validate cert issue_validate_cert_respond(domain_name, event, context) return # physical id should depend on domain name if request_type == 'Update': # check if domain name is the same existing_domain_name = event['OldResourceProperties']['DomainName'] if existing_domain_name != domain_name: # issue new certificate with new physical id issue_validate_cert_respond(domain_name, event, context) return else: # no changes just respond with success r = cr_response.CustomResourceResponse(event) r.respond({'CertificateArn': event['PhysicalResourceId']}) return if request_type == 'Delete': delete_validate_cert(domain_name, event, context) return except Exception as ex: print("Failed CCR Provisioning. Payload:\n" + str(event)) print(str(ex)) traceback.print_exc(file=sys.stdout) # if there is fallback ARN provided, respond with fallback arn if 'FallbackCertificateArn' in event['ResourceProperties']: r = cr_response.CustomResourceResponse(event) event['PhysicalResourceId'] = event['ResourceProperties'][ 'FallbackCertificateArn'] r.respond({ 'CertificateArn': event['ResourceProperties']['FallbackCertificateArn'] }) else: r = cr_response.CustomResourceResponse(event) r.respond_error(str(ex)) return
def handler(event, context): log.info(f"Received event:{json.dumps(event)}") cr_resp = cr_response.CustomResourceResponse(event) params = event['ResourceProperties'] print(f"Resource Properties {params}") try: if 'Region' not in params: raise Exception('Resource must have a Region') region = params['Region'] if 'LoggingRole' not in params: raise Exception('Resource must have a LoggingRole') if 'Tags' not in params: params['Tags'] = [] if event['RequestType'] == 'Create': sftp_server = create_sftp_server(params, region) event['PhysicalResourceId'] = sftp_server['ServerId'] _wait_till_online(context, sftp_server, region) cr_resp.respond(sftp_server) elif event['RequestType'] == 'Update': sftp_server = update_sftp_server(event['PhysicalResourceId'], params, region) cr_resp.respond(sftp_server) elif event['RequestType'] == 'Delete': delete_sftp_server(event['PhysicalResourceId'], region) cr_resp.respond() except Exception as e: traceback.print_exc(file=sys.stdout) cr_resp.respond_error(str(e)) return 'OK'
def lambda_handler(event, context): print(f"Received event:{json.dumps(event)}") lambda_response = cr_response.CustomResourceResponse(event) cr_params = event['ResourceProperties'] waf_logic = WafRateLimit(cr_params) try: # if create request, generate physical id, both for create/update copy files if event['RequestType'] == 'Create': event['PhysicalResourceId'] = waf_logic._create_rate_based_rule() data = {"RuleID": event['PhysicalResourceId']} lambda_response.respond(data) elif event['RequestType'] == 'Update': waf_logic._update_rate_based_rule(event['PhysicalResourceId']) data = {"RuleID": event['PhysicalResourceId']} lambda_response.respond(data) elif event['RequestType'] == 'Delete': print(event['PhysicalResourceId']) waf_logic._delete_rate_based_rule(event['PhysicalResourceId']) data = {} lambda_response.respond(data) except Exception as e: message = str(e) lambda_response.respond_error(message) return 'OK'
def wait_stack_states(success_states, failure_states, lambda_payload, lambda_context): """ Wait for stack states, either be it success or failure. If none of the states appear and lambda is running out of time, it will be re-invoked with lambda_payload parameters :param lambda_context: :param stack_id: :param success_states: :param failure_states: :param lambda_payload: :return: """ manage = stack_manage.StackManagement() result = manage.wait_stack_status( lambda_payload['ResourceProperties']['Region'], lambda_payload['PhysicalResourceId'], success_states, failure_states, lambda_context) # in this case we need to restart lambda execution if result is None: invoke = lambda_invoker.LambdaInvoker() invoke.invoke(lambda_payload) else: # one of the states is reached, and reply should be sent back to cloud formation cfn_response = cr_response.CustomResourceResponse(lambda_payload) cfn_response.response['PhysicalResourceId'] = lambda_payload[ 'PhysicalResourceId'] cfn_response.response['Status'] = result.status cfn_response.response['Reason'] = result.reason cfn_response.response['StackId'] = lambda_payload['StackId'] cfn_response.respond()
def respond_disabled_region(region, payload): cfn_response = cr_response.CustomResourceResponse(payload) payload[ 'PhysicalResourceId'] = f"Disabled{region.replace('-','')}{payload['ResourceProperties']['StackName']}" cfn_response.response['Status'] = 'SUCCESS' cfn_response.respond() return
def lambda_handler(event, context): print(f"Received event:{json.dumps(event)}") lambda_response = cr_response.CustomResourceResponse(event) params = event['ResourceProperties'] print(f"Resource Properties {params}") try: if event['RequestType'] == 'Create': event['PhysicalResourceId'] = params['Path'] password, version = put_parameter(params=params, override=False) lambda_response.respond(data={ "Password": password, "Version": version }) elif event['RequestType'] == 'Update': event['PhysicalResourceId'] = params['Path'] password, version = put_parameter(params=params, override=True) lambda_response.respond(data={ "Password": password, "Version": version }) elif event['RequestType'] == 'Delete': delete_parameter(params['Path']) lambda_response.respond() except Exception as e: message = str(e) lambda_response.respond_error(message) return 'OK'
def issue_validate_cert_respond(domain_name, alternative_names, event, context): logic = AwsAcmCertValidatorLogic() if 'WaitOnly' in event and event['WaitOnly']: acm_certificate_arn = event['PhysicalResourceId'] validation_record = event['ValidationRecord'] else: acm_certificate_arn = logic.request(domain_name=domain_name, alternative_names=alternative_names, event=event) validation_record = logic.validate(cert_arn=acm_certificate_arn) remaining_lambda_time = (context.get_remaining_time_in_millis() / 1000) - 20 print(f"Remaining wait secs:{remaining_lambda_time}") if 'StartWait' not in event: start_wait = time.time() else: start_wait = int(event['StartWait']) result = logic.wait_cert_validated( cert_arn=acm_certificate_arn, wait_interval_secs=5, max_wait_secs=remaining_lambda_time, return_empty_on_timeout=True ) if result is None: lambda_client = boto3.client('lambda') function_name = os.environ['AWS_LAMBDA_FUNCTION_NAME'] event['PhysicalResourceId'] = acm_certificate_arn event['WaitOnly'] = True event['ValidationRecord'] = validation_record if 'StartWait' not in event: event['StartWait'] = start_wait if 'WaitIteration' not in event: event['WaitIteration'] = 2 else: event['WaitIteration'] += 1 # if total wait time elapsed raise exception if int(time.time()) - int(event['StartWait']) > MAX_WAIT_TIME: raise Exception(f"Total wait time of {MAX_WAIT_TIME} elapsed") lambda_client.invoke( FunctionName=function_name, Payload=json.dumps(event).encode('utf-8'), InvocationType='Event' ) return # remove dns record try: if 'Cleanup' in event['ResourceProperties'] and event['ResourceProperties']['Cleanup'] == 'true': logic.remove_validation_record(domain_name, validation_record) except: print(f"Faild to remove validation record, continuing... ") # respond r = cr_response.CustomResourceResponse(event) event['PhysicalResourceId'] = acm_certificate_arn r.respond({'CertificateArn': acm_certificate_arn})
def lambda_handler(event, context): lambda_response = cr_response.CustomResourceResponse(event) cr_params = event['ResourceProperties'] # Validate input for key in ['Source', 'Destination']: if key not in cr_params: lambda_response.respond_error(f"{key} property missing") return # Validate input params format src_param = cr_params['Source'] dst_param = cr_params['Destination'] src_param_match = re.match(r's3:\/\/(.*?)\/(.*)', src_param) dst_param_match = re.match(r's3:\/\/(.*?)\/(.*)', dst_param) if src_param_match is None or dst_param_match is None: lambda_response.respond_error(f"Source/Destination must be in s3://bucket/key format") return # get prefixes src_prefix = src_param_match.group(2) dst_prefix = dst_param_match.group(2) dst = {'Bucket': dst_param_match.group(1), 'Prefix': dst_prefix} try: if event['RequestType'] == 'Delete': logic.S3CopyLogic(context, type='clean', src=None, dst=dst).clean_destination() lambda_response.respond() return # if create request, generate physical id, both for create/update copy files if event['RequestType'] == 'Create': event['PhysicalResourceId'] = dst_param # check if source is prefix - than it is sync type if src_prefix.endswith('/'): src = {'Bucket': src_param_match.group(1), 'Prefix': src_prefix} logic.S3CopyLogic(context, type='sync', src=src, dst=dst).copy() lambda_response.respond() # if prefix ends with zip, we need to unpack file first elif src_prefix.endswith('.zip'): src = {'Bucket': src_param_match.group(1), 'Key': src_prefix} logic.S3CopyLogic(context, type='object-zip', src=src, dst=dst).copy() lambda_response.respond() # by default consider prefix as key - regular s3 object else: src = {'Bucket': src_param_match.group(1), 'Key': src_prefix} logic.S3CopyLogic(context, type='object', src=src, dst=dst).copy() lambda_response.respond() except Exception as e: message = str(e) lambda_response.respond_error(message) return 'OK'
def lambda_handler(event, context): logger.info(f"Received event:{json.dumps(event)}") cr_params = event['ResourceProperties'] logger.info(f"Resource Properties {json.dumps(cr_params)}") region = cr_params['Region'] account_id = cr_params['AccountId'] stack_id = cr_params['StackName'] notification = cr_params['LambdaNotification'] #needs the PhysicalResourceId set incase of errors during creation otherwise you'll end up with an invalid physical id if 'PhysicalResourceId' not in event: event['PhysicalResourceId'] = generate_physical_id( notification, stack_id) lambda_response = cr_response.CustomResourceResponse(event) if not notification['Bucket']: logger.info( "Skipping notification creation as the bucket name is empty.") lambda_response.respond(data={}, NoEcho=False) return 'OK' try: if event['RequestType'] == 'Create': statement_id = add_lambda_notification(notification=notification, region=region, account_id=account_id, stack_id=stack_id) event['PhysicalResourceId'] = statement_id lambda_response.respond(data={'PhysicalResourceId': statement_id}, NoEcho=False) elif event['RequestType'] == 'Update': remove_lambda_notification( notification_id=event['PhysicalResourceId'], region=region) statement_id = add_lambda_notification(notification=notification, region=region, account_id=account_id, stack_id=stack_id) lambda_response.respond(data={'PhysicalResourceId': statement_id}, NoEcho=False) elif event['RequestType'] == 'Delete': remove_lambda_notification( notification_id=event['PhysicalResourceId'], region=region) lambda_response.respond( data={'PhysicalResourceId': event['PhysicalResourceId']}, NoEcho=False) except Exception as e: message = str(e) lambda_response.respond_error(message) return 'OK'
def delete_validate_cert(domain_name, event, context): logic = AwsAcmCertValidatorLogic() cert_arn = event['PhysicalResourceId'] if cert_arn.startswith('arn:aws'): acm = boto3.client('acm', region_name=os.environ['ACM_REGION']) cert_info = acm.describe_certificate(CertificateArn=cert_arn) validation_options = cert_info['Certificate']['DomainValidationOptions'][0] dns_validation_record = validation_options['ResourceRecord'] acm.delete_certificate(CertificateArn=cert_arn) logic.remove_validation_record(domain_name, dns_validation_record) r = cr_response.CustomResourceResponse(event) r.respond({'CertificateArn': event['PhysicalResourceId']})
def handler(event, context): print(f"Received event:{json.dumps(event)}") request_type = event['RequestType'] dns_zone = event['ResourceProperties']['RootDomainName'] domain_name = event['ResourceProperties']['DomainName'] region = event['ResourceProperties']['AwsRegion'] ns_records = event['ResourceProperties']['NSRecords'] iam_role = event['ResourceProperties']['ParentIAMRole'] try: dns_request = get_ns_upsert(domain_name, ns_records, dns_zone) if request_type == 'Create': print( f"adding {domain_name} NS records {ns_records} to dns zone {dns_zone}" ) create_route53_nsrecord(dns_request, dns_zone, iam_role, region) event['PhysicalResourceId'] = domain_name r = cr_response.CustomResourceResponse(event) r.respond() return if request_type == 'Update': print( f"updating {domain_name} NS records {ns_records} to dns zone {dns_zone}" ) create_route53_nsrecord(dns_request, dns_zone, iam_role, region) r = cr_response.CustomResourceResponse(event) r.respond() return if request_type == 'Delete': print(f"ignoring delete for {domain_name}") r = cr_response.CustomResourceResponse(event) r.respond() return except Exception as ex: print("Failed Adding NS Records. Payload:\n" + str(event)) print(str(ex)) traceback.print_exc(file=sys.stdout) r = cr_response.CustomResourceResponse(event) r.respond_error(str(ex))
def create_update_stack(cmd, payload): if 'Capabilities' not in payload['ResourceProperties']: payload['ResourceProperties']['Capabilities'] = 'CAPABILITY_IAM' # compile stack parameters stack_params = {} for key, value in payload['ResourceProperties'].items(): if key.startswith('StackParam_'): param_key = key.replace('StackParam_', '') param_value = value stack_params[param_key] = param_value # instantiate and use management handler manage = stack_manage.StackManagement() on_failure = 'DELETE' if 'OnFailure' in payload['ResourceProperties']: on_failure = payload['ResourceProperties']['OnFailure'] stack_id = '' if cmd == 'create': stack_id = manage.create( payload['ResourceProperties']['Region'], payload['ResourceProperties']['StackName'], payload['ResourceProperties']['TemplateUrl'], stack_params, payload['ResourceProperties']['Capabilities'].split(','), on_failure) elif cmd == 'update': stack_id = payload['PhysicalResourceId'] result = manage.update( payload['ResourceProperties']['Region'], stack_id, payload['ResourceProperties']['TemplateUrl'], stack_params, payload['ResourceProperties']['Capabilities'].split(','), ) # no updates to be performed if result is None: cfn_response = cr_response.CustomResourceResponse(payload) cfn_response.respond() return None else: raise 'Cmd must be create or update' return stack_id
def handler(event, context): print(f"Received event:{json.dumps(event)}") lambda_response = cr_response.CustomResourceResponse(event) params = event['ResourceProperties'] print(f"Resource Properties {params}") bucket = params['BucketName'] region = params['Region'] notifications = [] if "Notifications" in params: notifications = params['Notifications'] arn = f'arn:aws:s3:::{bucket}' # Accessed using the path-style URL. # https://docs.aws.amazon.com/AmazonS3/latest/dev/UsingBucket.html#access-bucket-intro if region == 'us-east-1': domain_name = f's3.amazonaws.com/{bucket}' else: domain_name = f's3.{region}.amazonaws.com/{bucket}' data = {'DomainName': domain_name, 'Arn': arn} try: if event['RequestType'] == 'Create': event['PhysicalResourceId'] = bucket create_bucket(params, event, context) lambda_response.respond(data) elif event['RequestType'] == 'Update': event['PhysicalResourceId'] = params['BucketName'] update_bucket(params, event, context) lambda_response.respond(data) elif event['RequestType'] == 'Delete': print(f"ignoring deletion of bucket {params['BucketName']}") lambda_response.respond() except Exception as e: message = str(e) lambda_response.respond_error(message) return 'OK'
def lambda_handler(event, context): try: params = dict([(k, v) for k, v in event['ResourceProperties'].items() if k != 'ServiceToken']) client = boto3.client('inspector') if event['RequestType'] == 'Create': response_data = client.subscribe_to_event(**params) if event['RequestType'] == 'Delete': response_data = client.unsubscribe_from_event(**params) if event['RequestType'] == 'Update': old_params = dict([ (k, v) for k, v in event['OldResourceProperties'].items() if k != 'ServiceToken' ]) client.unsubscribe_from_event(**old_params) response_data = client.subscribe_to_event(**params) print(response_data) lambda_response = cr_response.CustomResourceResponse(event, context) lambda_response.respond() except Exception as e: lambda_response.respond_error(e) raise e
def handler(event, context): print(f"Received event:{json.dumps(event)}") lambda_response = cr_response.CustomResourceResponse(event) params = event['ResourceProperties'] print(f"Resource Properties {params}") try: if event['RequestType'] == 'Create': event['PhysicalResourceId'] = params['BucketName'] create_bucket(params, event, context) lambda_response.respond() elif event['RequestType'] == 'Update': event['PhysicalResourceId'] = params['BucketName'] update_bucket(params, event, context) lambda_response.respond() elif event['RequestType'] == 'Delete': print(f"ignoring deletion of bucket {params['BucketName']}") lambda_response.respond() except Exception as e: message = str(e) lambda_response.respond_error(message) return 'OK'
def lambda_handler(payload, context): # if lambda invoked to wait for stack status print(f"Received event:{json.dumps(payload)}") # handle disable region situation if 'EnabledRegions' in payload['ResourceProperties']: region_list = payload['ResourceProperties']['EnabledRegions'].split( ',') current_region = payload['ResourceProperties']['Region'] print( f"EnabledRegions: {region_list}. Current region={current_region}") if current_region not in region_list: # if this is create request just skip if payload['RequestType'] == 'Create' or payload[ 'RequestType'] == 'Update': print(f"{current_region} not enabled, skipping") # report disabled # in case of region disable (update), physical record changes, so cleanup delete request is # sent subsequently via Cf, which will delete the stack respond_disabled_region(current_region, payload) return # lambda was invoked by itself, we just have to wait for stack operation to be completed if ('WaitComplete' in payload) and (payload['WaitComplete']): print("Waiting for stack status...") if payload['RequestType'] == 'Create': wait_stack_states(create_stack_success_states, create_stack_failure_states, payload, context) elif payload['RequestType'] == 'Update': wait_stack_states(update_stack_success_states, update_stack_failure_states, payload, context) elif payload['RequestType'] == 'Delete': wait_stack_states(delete_stack_success_states, delete_stack_failure_states, payload, context) # lambda was invoked directly by cf else: # depending on request type different handler is called print("Executing stack CRUD...") stack_id = None if 'PhysicalResourceId' in payload: stack_id = payload['PhysicalResourceId'] try: manage = stack_manage.StackManagement() stack_name = payload['ResourceProperties']['StackName'] region = payload['ResourceProperties']['Region'] stack_exists = manage.stack_exists(region, stack_name) if payload['RequestType'] == 'Create': # stack exists, create request => update # stack not exists, create request => create if stack_exists: print( f"Create request came for {stack_name}, but it already exists in {region}, updating..." ) payload['RequestType'] = 'Update' lambda_handler(payload, context) return else: stack_id = create_update_stack('create', payload) elif payload['RequestType'] == 'Update': # stack exists, update request => update # stack not exists, update request => create if stack_exists: stack_id = create_update_stack('update', payload) if stack_id is None: # no updates to be performed return else: print( f"Update request came for {stack_name}, but it does not exist in {region}, creating..." ) payload['RequestType'] = 'Create' lambda_handler(payload, context) return elif payload['RequestType'] == 'Delete': # stack exists, delete request => delete # stack not exists, delete request => report ok # for delete we are interested in actual stack id stack_exists = manage.stack_exists(region, stack_id) if stack_exists: delete_stack(payload) else: # reply with success print( f"Delete request came for {stack_name}, but it is nowhere to be found..." ) cfn_response = cr_response.CustomResourceResponse(payload) cfn_response.response[ 'Reason'] = 'CloudFormation stack has not been found, may be removed manually' cfn_response.respond() return # if haven't moved to other operation, set payloads stack id to created/updated stack and wait # for appropriate stack status payload['PhysicalResourceId'] = stack_id payload['WaitComplete'] = True invoker = lambda_invoker.LambdaInvoker() invoker.invoke(payload) except Exception as e: print(f"Exception:{e}\n{str(e)}") print(traceback.format_exc()) cfn_response = cr_response.CustomResourceResponse(payload) if 'PhysicalResourceId' in payload: cfn_response.response['PhysicalResourceId'] = payload[ 'PhysicalResourceId'] cfn_response.response['Status'] = 'FAILED' cfn_response.response['Reason'] = str(e) cfn_response.respond() raise e
def lambda_handler(event, context): print(f"Received event:{json.dumps(event)}") # Create cr_response object lambda_response = cr_response.CustomResourceResponse(event) cr_params = event['ResourceProperties'] # Validate input for key in ['ReplicationTaskArn']: if key not in cr_params: lambda_response.respond_error(f"{key} property missing") return # Validate input params format arn_param = cr_params['ReplicationTaskArn'] arn_param_match = re.match(r"arn:aws:dms:(.*)", arn_param) if arn_param_match is None: arn_example = "arn:aws:dms:us-east-1:123456789012:task:2PVREMWNPGYJCVU2IBPTOYTIV4" arn_format = "arn:aws:dms:<region>:<account>:task:<resourcename>" lambda_response.respond_error( f"ReplicationTaskArn must of type {arn_format}. For example: {arn_example}" ) return # All validations pass. try/expect block to implement logic def check_status(status, str): return True if status == str else False try: client = boto3.client('dms') event['PhysicalResourceId'] = arn_param status = client.describe_replication_tasks( Filters=[{ 'Name': 'replication-task-arn', 'Values': [arn_param] }])['ReplicationTasks'][0]['Status'] if event['RequestType'] == 'Create' and check_status( status.lower(), 'ready'): response = client.start_replication_task( ReplicationTaskArn=arn_param, StartReplicationTaskType='start-replication') response_data = { k: response['ReplicationTask'][k] for k in response['ReplicationTask'] if k not in ['ReplicationTaskStats'] } lambda_response.respond(data=response_data) return if event['RequestType'] in ['Delete', 'Update'] and check_status( status.lower(), 'running'): response = client.stop_replication_task( ReplicationTaskArn=arn_param) response_data = { k: response['ReplicationTask'][k] for k in response['ReplicationTask'] if k not in ['ReplicationTaskStats'] } lambda_response.respond(data=response_data) return else: response_data = {} lambda_response.respond(data=response_data) except Exception as e: message = str(e) lambda_response.respond_error(message) return 'OK'