From 19c8df6c6c5541198f728beade3e5af3e27574b3 Mon Sep 17 00:00:00 2001 From: badra001 Date: Tue, 12 Apr 2022 12:50:08 -0400 Subject: [PATCH] Add functions - get_heritage_application_name() - get_heritage_item() - compare_heritage() - verify_heritage_owner() --- code/ddns-lambda.py | 207 +++++++++++++++++++++++++++++--------------- 1 file changed, 138 insertions(+), 69 deletions(-) diff --git a/code/ddns-lambda.py b/code/ddns-lambda.py index c72bff5..1892c12 100755 --- a/code/ddns-lambda.py +++ b/code/ddns-lambda.py @@ -72,7 +72,7 @@ LOGGER = logging.getLogger() ACCOUNT = None REGION = None -VERSION = '0.2.2' +VERSION = '0.2.3' # Read Env variables DEBUG_LOG_LEVEL = os.environ.get('DebugLogLevel', 'INFO') @@ -212,7 +212,8 @@ def lambda_handler( LOGGER.info('DynamoDB table already exists: %s', DDBNAME) else: LOGGER.error('DynamoDB table does not exist, exiting function: %s', DDBNAME) - caller_response.append('DynamoDB table does not exist, exiting function: ' + DDBNAME) + caller_response.append( + 'DynamoDB table does not exist, exiting function: ' + DDBNAME) if SNS_ENABLE: sns_msg = {} sns_msg['instance_id'] = event['detail']['instance-id'] @@ -273,10 +274,10 @@ def lambda_handler( LOGGER.debug("instance: %s", str(instance) + lineno()) instance = remove_empty_from_dict(instance) instance_dump = json.dumps(instance, default=json_serial) - + # dont' change to dictionary. Keep it as string to be written to DDB # instance_attributes = json.loads(instance_dump) - instance_attributes = instance_dump + instance_attributes = instance_dump LOGGER.info("instance_attributes: %s", str(instance_attributes) + lineno()) LOGGER.debug("trying to put instance information in " "dynamo table %s", str(instance_attributes) + lineno()) @@ -327,7 +328,7 @@ def lambda_handler( LOGGER.debug("private_dns_name: %s", str(private_dns_name) + lineno()) LOGGER.debug("private_host_name: %s", str(private_host_name) + lineno()) except: - LOGGER.error("Unable to extract IP Address or DNS name from instance: %s, exiting script", + LOGGER.error("Unable to extract IP Address or DNS name from instance: %s, exiting script", str(instance_id) + lineno()) caller_response.append( 'Unable to extract IP Address or DNS name from instance {} for state {}. Exiting script.'.format(instance_id, state)) @@ -343,16 +344,20 @@ def lambda_handler( reversed_ip_address = reverse_list(private_ip) if reversed_ip_address == None: - LOGGER.error("Error in getting reverse IP address for: %s", str(private_ip) + lineno()) - caller_response.append("Error in getting reverse IP address for: " + str(private_ip)) + LOGGER.error("Error in getting reverse IP address for: %s", + str(private_ip) + lineno()) + caller_response.append( + "Error in getting reverse IP address for: " + str(private_ip)) return caller_response reversed_domain_prefix = get_reversed_domain_prefix(subnet_mask, private_ip) reversed_domain_prefix = reverse_list(reversed_domain_prefix) if reversed_domain_prefix == None: - LOGGER.error("Error in getting reverse domain prefix for: %s", str(private_ip) + lineno()) - caller_response.append("Error in getting reverse domain prefix for: " + str(private_ip)) + LOGGER.error("Error in getting reverse domain prefix for: %s", + str(private_ip) + lineno()) + caller_response.append( + "Error in getting reverse domain prefix for: " + str(private_ip)) return caller_response LOGGER.debug("reversed_domain_prefix is: %s", @@ -369,8 +374,10 @@ def lambda_handler( if is_dns_hostnames_enabled(compute, instance_id, vpc_id): LOGGER.debug("DNS hostnames enabled for %s", str(vpc_id) + lineno()) else: - LOGGER.error("DNS hostnames disabled for %s. You have to enable DNS hostnames to use Route 53 private hosted zones. %s", vpc_id, lineno()) - caller_response.append("You have to enable DNS hostnames to use Route 53 private hosted zones. DNS hostnames disabled for " + vpc_id) + LOGGER.error( + "DNS hostnames disabled for %s. You have to enable DNS hostnames to use Route 53 private hosted zones. %s", vpc_id, lineno()) + caller_response.append( + "You have to enable DNS hostnames to use Route 53 private hosted zones. DNS hostnames disabled for " + vpc_id) if SNS_ENABLE: sns_msg = {} sns_msg['instance_id'] = instance_id @@ -385,8 +392,10 @@ def lambda_handler( if is_dns_support_enabled(compute, instance_id, vpc_id): LOGGER.debug("DNS support enabled for %s", str(vpc_id) + lineno()) else: - LOGGER.error("DNS support disabled for %s. You have to enabled DNS support to use Route 53 private hosted zones. %s", vpc_id, lineno()) - caller_response.append("You have to enabled DNS support to use Route 53 private hosted zones. DNS support disabled for " + vpc_id) + LOGGER.error( + "DNS support disabled for %s. You have to enabled DNS support to use Route 53 private hosted zones. %s", vpc_id, lineno()) + caller_response.append( + "You have to enabled DNS support to use Route 53 private hosted zones. DNS support disabled for " + vpc_id) if SNS_ENABLE: sns_msg = {} sns_msg['instance_id'] = instance_id @@ -473,7 +482,8 @@ def lambda_handler( LOGGER.debug("trying to get dhcp option set id %s", lineno()) dhcp_options_id = get_dhcp_option_set_id_for_vpc(compute, instance_id, vpc_id) LOGGER.debug("dhcp_options_id: %s", str(dhcp_options_id) + lineno()) - dhcp_configurations = get_dhcp_configurations(compute, instance_id, dhcp_options_id) + dhcp_configurations = get_dhcp_configurations( + compute, instance_id, dhcp_options_id) LOGGER.debug("dhcp_configurations: %s", str(get_dhcp_configurations) + lineno()) except BaseException as err: @@ -897,24 +907,27 @@ def lambda_handler( if delete_response == 'NoSuchHostedZone': delete_records = False caller_response.append("Failed, no such zone: " + response_text) - LOGGER.info("instance: %s, NoSuchHostedZone: %s", + LOGGER.info("instance: %s, NoSuchHostedZone: %s", instance_id, response_text + lineno()) elif delete_response == 'InvalidChangeBatch-RecordNotFound': caller_response.append("Failed, Record Not Found: " + response_text) - LOGGER.info("instance: %s, InvalidChangeBatch-RecordNotFound: %s", + LOGGER.info("instance: %s, InvalidChangeBatch-RecordNotFound: %s", instance_id, response_text + lineno()) elif delete_response == 'InvalidChangeBatch-RecordDoNotMatch': delete_records = False - caller_response.append("Failed, requested delete do not match existing record: " + response_text) - LOGGER.info("instance: %s, InvalidChangeBatch-RecordDoNotMatch: %s", + caller_response.append( + "Failed, requested delete do not match existing record: " + response_text) + LOGGER.info("instance: %s, InvalidChangeBatch-RecordDoNotMatch: %s", instance_id, response_text + lineno()) elif delete_response == {}: delete_records = False - caller_response.append("Failed, could NOT delete Record: " + response_text) - LOGGER.info("instance: %s, Failed, could NOT delete Record: %s", + caller_response.append( + "Failed, could NOT delete Record: " + response_text) + LOGGER.info("instance: %s, Failed, could NOT delete Record: %s", instance_id, response_text + lineno()) else: - LOGGER.info("instance: %s, Success: %s",instance_id, response_text + lineno()) + LOGGER.info("instance: %s, Success: %s", + instance_id, response_text + lineno()) caller_response.append("Success: " + response_text) except BaseException as err: delete_records = False @@ -955,23 +968,23 @@ def lambda_handler( if delete_response == 'NoSuchHostedZone': delete_records = False caller_response.append("Failed, no such zone: " + response_text) - LOGGER.info("instance: %s, NoSuchHostedZone: %s", + LOGGER.info("instance: %s, NoSuchHostedZone: %s", instance_id, response_text + lineno()) elif delete_response == 'InvalidChangeBatch-RecordNotFound': caller_response.append("Failed, Record Not Found: " + response_text) - LOGGER.info("instance: %s, InvalidChangeBatch-RecordNotFound: %s", + LOGGER.info("instance: %s, InvalidChangeBatch-RecordNotFound: %s", instance_id, response_text + lineno()) elif delete_response == 'InvalidChangeBatch-RecordDoNotMatch': delete_records = False caller_response.append( "Failed, requested delete do not match existing record: " + response_text) - LOGGER.info("instance: %s, InvalidChangeBatch-RecordDoNotMatch: %s", + LOGGER.info("instance: %s, InvalidChangeBatch-RecordDoNotMatch: %s", instance_id, response_text + lineno()) elif delete_response == {}: delete_records = False caller_response.append( "Failed, could NOT delete Record: " + response_text) - LOGGER.info("instance: %s, Failed Could NOT delete Record: %s", + LOGGER.info("instance: %s, Failed Could NOT delete Record: %s", instance_id, response_text + lineno()) else: caller_response.append("Success: " + response_text) @@ -1006,23 +1019,23 @@ def lambda_handler( if delete_response == 'NoSuchHostedZone': delete_records = False caller_response.append("Failed, no such zone: " + response_text) - LOGGER.info("instance: %s, NoSuchHostedZone: %s", + LOGGER.info("instance: %s, NoSuchHostedZone: %s", instance_id, response_text + lineno()) elif delete_response == 'InvalidChangeBatch-RecordNotFound': caller_response.append("Failed, Record Not Found: " + response_text) - LOGGER.info("instance: %s, InvalidChangeBatch-RecordNotFound: %s", + LOGGER.info("instance: %s, InvalidChangeBatch-RecordNotFound: %s", instance_id, response_text + lineno()) elif delete_response == 'InvalidChangeBatch-RecordDoNotMatch': delete_records = False caller_response.append( "Failed, requested delete do not match existing record: " + response_text) - LOGGER.info("instance: %s, InvalidChangeBatch-RecordDoNotMatch: %s", + LOGGER.info("instance: %s, InvalidChangeBatch-RecordDoNotMatch: %s", instance_id, response_text + lineno()) elif delete_response == {}: delete_records = False caller_response.append( "Failed, could NOT delete Record: " + response_text) - LOGGER.info("instance: %s, Failed could NOT delete Record: %s", + LOGGER.info("instance: %s, Failed could NOT delete Record: %s", instance_id, response_text + lineno()) else: caller_response.append("Success: " + response_text) @@ -1065,23 +1078,23 @@ def lambda_handler( if delete_response == 'NoSuchHostedZone': delete_records = False caller_response.append("Failed, no such zone: " + response_text) - LOGGER.info("instance: %s, NoSuchHostedZone: %s", + LOGGER.info("instance: %s, NoSuchHostedZone: %s", instance_id, response_text + lineno()) elif delete_response == 'InvalidChangeBatch-RecordNotFound': caller_response.append("Failed, Record Not Found: " + response_text) - LOGGER.info("instance: %s, InvalidChangeBatch-RecordNotFound: %s", + LOGGER.info("instance: %s, InvalidChangeBatch-RecordNotFound: %s", instance_id, response_text + lineno()) elif delete_response == 'InvalidChangeBatch-RecordDoNotMatch': delete_records = False caller_response.append( "Failed, requested delete do not match existing record: " + response_text) - LOGGER.info("instance: %s, InvalidChangeBatch-RecordDoNotMatch: %s", + LOGGER.info("instance: %s, InvalidChangeBatch-RecordDoNotMatch: %s", instance_id, response_text + lineno()) elif delete_response == {}: delete_records = False caller_response.append( "Failed, could NOT delete Record: " + response_text) - LOGGER.info("instance: %s, Failed could NOT delete Record: %s", + LOGGER.info("instance: %s, Failed could NOT delete Record: %s", instance_id, response_text + lineno()) else: caller_response.append("Success: " + response_text) @@ -1200,23 +1213,23 @@ def lambda_handler( if delete_response == 'NoSuchHostedZone': delete_records = False caller_response.append("Failed, no such zone: " + response_text) - LOGGER.info("instance: %s, NoSuchHostedZone: %s", + LOGGER.info("instance: %s, NoSuchHostedZone: %s", instance_id, response_text + lineno()) elif delete_response == 'InvalidChangeBatch-RecordNotFound': caller_response.append("Failed, Record Not Found: " + response_text) - LOGGER.info("instance: %s, InvalidChangeBatch-RecordNotFound: %s", + LOGGER.info("instance: %s, InvalidChangeBatch-RecordNotFound: %s", instance_id, response_text + lineno()) elif delete_response == 'InvalidChangeBatch-RecordDoNotMatch': delete_records = False caller_response.append( "Failed, requested delete do not match existing record: " + response_text) - LOGGER.info("instance: %s, InvalidChangeBatch-RecordDoNotMatch: %s", + LOGGER.info("instance: %s, InvalidChangeBatch-RecordDoNotMatch: %s", instance_id, response_text + lineno()) elif delete_response == {}: delete_records = False caller_response.append( "Failed, could NOT delete Record: " + response_text) - LOGGER.info("instance: %s, Failed could NOT delete Record: %s", + LOGGER.info("instance: %s, Failed could NOT delete Record: %s", instance_id, response_text + lineno()) else: caller_response.append("Success: " + response_text) @@ -1260,25 +1273,25 @@ def lambda_handler( if delete_response == 'NoSuchHostedZone': delete_records = False caller_response.append("Failed, no such zone: " + response_text) - LOGGER.info("instance: %s, NoSuchHostedZone: %s", + LOGGER.info("instance: %s, NoSuchHostedZone: %s", instance_id, response_text + lineno()) elif delete_response == 'InvalidChangeBatch-RecordNotFound': caller_response.append( "Failed, Record Not Found: " + response_text) - LOGGER.info("instance: %s, InvalidChangeBatch-RecordNotFound: %s", - instance_id, response_text + lineno()) + LOGGER.info("instance: %s, InvalidChangeBatch-RecordNotFound: %s", + instance_id, response_text + lineno()) elif delete_response == 'InvalidChangeBatch-RecordDoNotMatch': delete_records = False caller_response.append( "Failed, requested delete do not match existing record: " + response_text) - LOGGER.info("instance: %s, InvalidChangeBatch-RecordDoNotMatch: %s", - instance_id, response_text + lineno()) + LOGGER.info("instance: %s, InvalidChangeBatch-RecordDoNotMatch: %s", + instance_id, response_text + lineno()) elif delete_response == {}: delete_records = False caller_response.append( "Failed, could NOT delete Record: " + response_text) - LOGGER.info("instance: %s, Failed could NOT delete Record: %s", - instance_id, response_text + lineno()) + LOGGER.info("instance: %s, Failed could NOT delete Record: %s", + instance_id, response_text + lineno()) else: caller_response.append("Success: " + response_text) LOGGER.info("instance: %s, Success: %s", @@ -1349,7 +1362,7 @@ def get_instances(client, instance_id): except ClientError as err: if 'An error occurred (RequestLimitExceeded)' in str(err): - LOGGER.info("instance: %s, describe_instances RequestLimitExceeded, %s", + LOGGER.info("instance: %s, describe_instances RequestLimitExceeded, %s", instance_id, str(err) + lineno()) else: LOGGER.error("unexpected error. %s\n", str(err) + lineno()) @@ -1358,7 +1371,7 @@ def get_instances(client, instance_id): LOGGER.info("instance: %s, describe_instances returned RequestLimitExceeded, waiting before retry. %s", instance_id, str(i) + lineno()) time.sleep(i) - + return instance_data # def list_hosted_zones(client): @@ -1372,6 +1385,7 @@ def get_instances(client, instance_id): # except ClientError as err: # LOGGER.info("unexpected error. %s\n", str(err) + lineno()) + def new_list_hosted_zones(client, instance_id): """ Get route53 hosted zones @@ -1408,7 +1422,7 @@ def new_list_hosted_zones(client, instance_id): try: sns_msg = {} sns_msg['instance_id'] = instance_id - sns_msg['account_id'] = get_caller_account_id() + sns_msg['account_id'] = get_caller_account_id() sns_msg['client'] = 'route53' sns_msg['boto3_method'] = 'list_hosted_zones' sns_msg['message'] = 'list_hosted_zones timed out' @@ -1503,9 +1517,9 @@ def get_item_from_dynamodb_table(client, table, instance_id): if 'Item' in item: LOGGER.debug("returned item:" " %s", str(item['Item']['InstanceAttributes']['S']) + lineno()) - + instance_attribute = item['Item']['InstanceAttributes']['S'] - + # these 7 lines are handling for legacy DDB items created prior how items were written instance_attribute = instance_attribute.replace("'", '"') instance_attribute = instance_attribute.replace(" True,", ' "True",') @@ -1573,7 +1587,7 @@ def get_dhcp_option_set_id_for_vpc(client, instance_id, vpc_id): while i < MAX_API_RETRY: try: - + option_sets = {} results = client.describe_vpcs() for item in results['Vpcs']: @@ -1582,13 +1596,13 @@ def get_dhcp_option_set_id_for_vpc(client, instance_id, vpc_id): else: option_sets[str(item['VpcId'])] = None option_set_for_vpc = option_sets[vpc_id] - LOGGER.debug("option set for vpc: %s", - str(option_set_for_vpc) + lineno()) + LOGGER.debug("option set for vpc: %s", + str(option_set_for_vpc) + lineno()) break except ClientError as err: if 'An error occurred (RequestLimitExceeded)' in str(err): - LOGGER.info("instance: %s, describe_vpcs RequestLimitExceeded, %s", + LOGGER.info("instance: %s, describe_vpcs RequestLimitExceeded, %s", instance_id, str(err) + lineno()) else: LOGGER.error("unexpected error. %s\n", str(err) + lineno()) @@ -1600,6 +1614,7 @@ def get_dhcp_option_set_id_for_vpc(client, instance_id, vpc_id): return option_set_for_vpc + def get_dynamodb_table(client, table_name): """ Get the dynamodb table @@ -1738,7 +1753,7 @@ def new_change_resource_recordset(client, instance_id, zone_id, host_name, hoste if i >= MAX_API_RETRY: LOGGER.error("instance: %s, change_resource_record_sets exceeded max retry of %s", - instance_id, MAX_API_RETRY + lineno()) + instance_id, MAX_API_RETRY + lineno()) if update_response == {}: if SNS_ENABLE: @@ -1919,12 +1934,12 @@ def new_get_resource_record(client, instance_id, zone_id, host_name, hosted_zone if i >= MAX_API_RETRY: LOGGER.error("instance: %s, list_resource_record_sets exceeded max retry of %s", - instance_id, MAX_API_RETRY + lineno()) + instance_id, MAX_API_RETRY + lineno()) if SNS_ENABLE: try: sns_msg = {} sns_msg['instance_id'] = instance_id - sns_msg['account_id'] = get_caller_account_id() + sns_msg['account_id'] = get_caller_account_id() sns_msg['client'] = 'route53' sns_msg['boto3_method'] = 'list_resource_record_sets' sns_msg['message'] = 'list_resource_record_sets timed out' @@ -2074,14 +2089,14 @@ def new_delete_resource_record(client, instance_id, zone_id, host_name, hosted_z if i >= MAX_API_RETRY: LOGGER.error("instance: %s, change_resource_record_sets exceeded max retry of %s", - instance_id, MAX_API_RETRY + lineno()) + instance_id, MAX_API_RETRY + lineno()) if (delete_response == {} or delete_response == "InvalidChangeBatch-RecordDoNotMatch"): if SNS_ENABLE: try: sns_msg = {} sns_msg['instance_id'] = instance_id - sns_msg['account_id'] = get_caller_account_id() + sns_msg['account_id'] = get_caller_account_id() sns_msg['client'] = 'route53' sns_msg['boto3_method'] = 'change_resource_record_sets' sns_msg['message'] = 'change_resource_record_sets could not DELETE record' @@ -2242,7 +2257,7 @@ def get_dhcp_configurations(client, instance_id, dhcp_options_id): except ClientError as err: if 'An error occurred (RequestLimitExceeded)' in str(err): - LOGGER.info("instance: %s, describe_dhcp_options RequestLimitExceeded, %s", + LOGGER.info("instance: %s, describe_dhcp_options RequestLimitExceeded, %s", instance_id, str(err) + lineno()) else: LOGGER.error("unexpected error. %s\n", str(err) + lineno()) @@ -2254,6 +2269,7 @@ def get_dhcp_configurations(client, instance_id, dhcp_options_id): return zone_names + def new_reverse_list(ip_list): """ Reverses the order of the instance's IP address and helps construct the reverse lookup zone name. @@ -2395,11 +2411,11 @@ def is_dns_hostnames_enabled(client, instance_id, vpc_id): LOGGER.debug("%s", str(response) + lineno()) dns_hostname_enabled = response['EnableDnsHostnames']['Value'] break - + except ClientError as err: if 'An error occurred (RequestLimitExceeded)' in str(err): - LOGGER.info("instance: %s, describe_vpc_attribute RequestLimitExceeded, %s", + LOGGER.info("instance: %s, describe_vpc_attribute RequestLimitExceeded, %s", instance_id, str(err) + lineno()) else: LOGGER.error("unexpected error. %s\n", str(err) + lineno()) @@ -2411,6 +2427,7 @@ def is_dns_hostnames_enabled(client, instance_id, vpc_id): return dns_hostname_enabled + def is_dns_support_enabled(client, instance_id, vpc_id): """ Whether dns support is enabled @@ -2431,11 +2448,11 @@ def is_dns_support_enabled(client, instance_id, vpc_id): LOGGER.debug('response2: %s', str(response) + lineno()) dns_suppport_enabled = response['EnableDnsSupport']['Value'] break - + except ClientError as err: if 'An error occurred (RequestLimitExceeded)' in str(err): - LOGGER.info("instance: %s, describe_vpc_attribute RequestLimitExceeded, %s", + LOGGER.info("instance: %s, describe_vpc_attribute RequestLimitExceeded, %s", instance_id, str(err) + lineno()) else: LOGGER.error("unexpected error. %s\n", str(err) + lineno()) @@ -2513,7 +2530,7 @@ def new_get_hosted_zone_properties(client, instance_id, zone_id): try: sns_msg = {} sns_msg['instance_id'] = instance_id - sns_msg['account_id'] = get_caller_account_id() + sns_msg['account_id'] = get_caller_account_id() sns_msg['client'] = 'route53' sns_msg['boto3_method'] = 'get_hosted_zone' sns_msg['message'] = 'get_hosted_zone timed out' @@ -2524,7 +2541,6 @@ def new_get_hosted_zone_properties(client, instance_id, zone_id): LOGGER.info("instance: %s, error: %s", instance_id, str(sys.exc_info()[0]) + lineno()) - return hosted_zone_properties @@ -2536,7 +2552,6 @@ def get_subnet_cidr_block(client, instance_id, subnet_id): :return: """ - i = 0 while i < MAX_API_RETRY: try: @@ -2551,8 +2566,8 @@ def get_subnet_cidr_block(client, instance_id, subnet_id): except ClientError as err: if 'An error occurred (RequestLimitExceeded)' in str(err): - LOGGER.error("instance: %s, describe_subnets RequestLimitExceeded, %s", - instance_id, str(err) + lineno()) + LOGGER.error("instance: %s, describe_subnets RequestLimitExceeded, %s", + instance_id, str(err) + lineno()) else: LOGGER.error("unexpected error. %s\n", str(err) + lineno()) @@ -2560,7 +2575,7 @@ def get_subnet_cidr_block(client, instance_id, subnet_id): LOGGER.info("instance: %s, describe_subnets returned RequestLimitExceeded, waiting before retry. %s", instance_id, str(i) + lineno()) time.sleep(i) - + return cidr_block @@ -2594,6 +2609,7 @@ def dump_heritage(data): def add_heritage_item(data, key, value): """ Add a key/value pair to the heritage dict. + :param dict(string) data: Dictionary containing heritage data :param str key: The key for the key/value pair :param str value: The value for the key/value pair @@ -2663,6 +2679,59 @@ def parse_heritage(info): return {} +def get_heritage_application_name(data): + """ + Get the heritage application name the structure. + + :param dict(string) data: Dictionary containing heritage data + :return: The value of the application name if properly constructed, otherwise, None + """ + + return data.get('application_name', None) + + +def get_heritage_item(data, key): + """ + Get a heritage value from the structure. + + :param dict(string) data: Dictionary containing heritage data + :param str key: The key for the expected item. You cannot get the application_name from this + :return: The value of the heritage item if it exists, and None if not + """ + + result = None + items = data.get('items', {}) + if len(items): + v = items.get(key, None) + result = v + return result + + +def compare_heritage(data, key, value): + """ + Compare a heritage record key to the specified value: + + :param dict(string) data: Dictionary containing heritage data + :param str key: The key to compare within the heriage data + :param str value: The value for the comparision + :return: True if the heritage value for the key matches the supplied value. False if not or if missing + """ + + return True if value == get_heritage_item(data, key) else False + + +def verify_heritage_owner(data, value): + """ + Determine if a heritage record is created by us with the value of application_name + + :param dict(string) data: Dictionary containing heritage data + :param str value: The application_name expected + :return: True if the heritage record was created witho our application_name, and False otherwise + """ + + return True if value == get_heritage_application_name(data) else False + + def publish_to_sns(client, message): """ Publish a simple message to the specified SNS topic