diff --git a/CHANGELOG.md b/CHANGELOG.md index 62adb86..286b41a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -47,3 +47,15 @@ - code 0.1.18 - lots of bug fixes - update to use launch_time as the timestamp in the TXT ecords to be able to reconstruct it to delete it + +* 0.2.0 -- 2022-03-08 + - code 0.2.0 + - SNS code prep + - SNS resource prep + - refactor the route53 API calls + - add better API timeouts + - new variables: + - sns_topic_name + - sqs_queue_name + - enable_sns + - enable_sqs diff --git a/README.md b/README.md index 1e3c14d..ab76264 100644 --- a/README.md +++ b/README.md @@ -28,11 +28,14 @@ No modules. | [aws_lambda_alias.lambda](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/lambda_alias) | resource | | [aws_lambda_function.lambda](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/lambda_function) | resource | | [aws_lambda_permission.allow_cloudwatch](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/lambda_permission) | resource | +| [aws_sns_topic.topic](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/sns_topic) | resource | +| [aws_sns_topic_policy.topic](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/sns_topic_policy) | resource | | [aws_arn.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/arn) | data source | | [aws_caller_identity.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/caller_identity) | data source | | [aws_iam_policy.lambda_policies](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy) | data source | | [aws_iam_policy_document.lambda_assume](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | | [aws_iam_policy_document.lambda_policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | +| [aws_iam_policy_document.topic](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | | [aws_region.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/region) | data source | ## Inputs @@ -43,11 +46,15 @@ No modules. | [account\_id](#input\_account\_id) | AWS Account ID (default will pull from current user) | `string` | `""` | no | | [component\_tags](#input\_component\_tags) | Additional tags for Components (s3, kms, ddb) | `map(map(string))` |
{
"ddb": {},
"kms": {},
"s3": {}
} | no |
| [create](#input\_create) | Flag to indicate whether to create the resources or not (default: true) | `bool` | `true` | no |
-| [dynamodb\_table\_name](#input\_dynamodb\_table\_name) | Different DynamoDB table name to override default of var.name) | `string` | `null` | no |
-| [lambda\_environment\_variables](#input\_lambda\_environment\_variables) | Map of lambda environment variables and values | `map(string)` | {
"DNS_RR_TimeToLive": 60,
"DynamoDBName": null,
"HeritageIdentifier": "dynr53",
"HeritageTXTRecordPrefix": "_txt",
"SleepTime": 60,
"TagKeyCname": "boc:dns:cname",
"TagKeyHostName": "boc:dns:name",
"TagKeyZone": "boc:dns:zone"
} | no |
-| [lambda\_name](#input\_lambda\_name) | Different Lambda name to override default of var.name) | `string` | `null` | no |
+| [dynamodb\_table\_name](#input\_dynamodb\_table\_name) | Different DynamoDB table name to override default of var.name | `string` | `null` | no |
+| [enable\_sns](#input\_enable\_sns) | Enable use of SNS for reporting errors | `bool` | `false` | no |
+| [enable\_sqs](#input\_enable\_sqs) | Enable use of SQS for SNS to send errors | `bool` | `false` | no |
+| [lambda\_environment\_variables](#input\_lambda\_environment\_variables) | Map of lambda environment variables and values | `map(string)` | {
"DNS_RR_TimeToLive": 60,
"DynamoDBName": null,
"HeritageIdentifier": "dynr53",
"HeritageTXTRecordPrefix": "_txt",
"MaxApiRetry": 10,
"SleepTime": 60,
"SnsEnable": false,
"SnsTopicArn": "",
"TagKeyCname": "boc:dns:cname",
"TagKeyHostName": "boc:dns:name",
"TagKeyZone": "boc:dns:zone"
} | no |
+| [lambda\_name](#input\_lambda\_name) | Different Lambda name to override default of var.name | `string` | `null` | no |
| [name](#input\_name) | Name to use within all the created resources (default: inf-dynamic-route53) | `string` | `"inf-dynamic-route53"` | no |
| [override\_prefixes](#input\_override\_prefixes) | Override built-in prefixes by component. This should be used primarily for common infrastructure things | `map(string)` | `{}` | no |
+| [sns\_topic\_name](#input\_sns\_topic\_name) | Different SNS Topic name to override default of var.name | `string` | `null` | no |
+| [sqs\_queue\_name](#input\_sqs\_queue\_name) | Different SQS queue name to override default of var.name | `string` | `null` | no |
| [tags](#input\_tags) | AWS Tags to apply to appropriate resources | `map(string)` | `{}` | no |
## Outputs
diff --git a/code/ddns-lambda.py b/code/ddns-lambda.py
index 87c0846..5e29838 100755
--- a/code/ddns-lambda.py
+++ b/code/ddns-lambda.py
@@ -72,7 +72,7 @@
LOGGER = logging.getLogger()
ACCOUNT = None
REGION = None
-VERSION = '0.1.20'
+VERSION = '0.2.0'
# Read Env variables
DEBUG_LOG_LEVEL = os.environ.get('DebugLogLevel', 'INFO')
@@ -86,7 +86,8 @@
TF_MODULE_VERSION = os.environ.get('tf_module_version', '(unknown)')
MAX_API_RETRY = int(os.environ.get('MaxApiRetry', '10'))
SNS_TOPIC_ARN = os.environ.get('SnsTopicArn', '')
-SNS_ENABLE = os.environ.get('SnsEnable', 'False')
+SNS_ENABLE = (os.environ.get('SnsEnable', 'False').lower() in [
+ 'yes', 'y', 'true', '1']) and SNS_TOPIC_ARN != ''
# for CNAMEs
TXT_RR_PREFIX = os.environ.get('HeritageTXTRecordPrefix', '_txt')
@@ -104,11 +105,12 @@
elif DEBUG_LOG_LEVEL == 'CRITICAL':
LOGGER.setLevel(logging.CRITICAL)
else:
- LOGGER.setLevel(logging.INFO)
+ LOGGER.setLevel(logging.INFO)
print('Loading function v{} tf_module_version {}: {}'.format(
VERSION, TF_MODULE_VERSION, datetime.datetime.now().time().isoformat()))
+
def lineno(): # pragma: no cover
"""
Returns the current line number in our script
@@ -178,7 +180,7 @@ def lambda_handler(
dynamodb_client=get_dynamodb_client(),
compute=get_ec2_client(),
route53=get_route53_client(),
- sns_client=get_sns_client()
+ sns_client=get_sns_client()
):
@@ -249,7 +251,8 @@ def lambda_handler(
"%s", t_private_dns_name + "," + t_private_ip + "," + t_subnet_id + "," + t_vpc_id + lineno())
break
except:
- LOGGER.info("instance: %s, no instance data, repeat check: %s", instance_id, lineno())
+ LOGGER.info("instance: %s, no instance data, repeat check: %s",
+ instance_id, lineno())
# Remove response metadata from the response
if 'ResponseMetadata' in instance:
@@ -360,7 +363,8 @@ def lambda_handler(
# Check to see whether a reverse lookup zone for the instance
# already exists. If it does, check to see whether
# the reverse lookup zone is associated with the instance's VPC.
- LOGGER.info("instance: %s, reversed_lookup_zone: %s", instance_id, str(reversed_lookup_zone) + lineno())
+ LOGGER.info("instance: %s, reversed_lookup_zone: %s",
+ instance_id, str(reversed_lookup_zone) + lineno())
reverse_zone = None
for record in hosted_zones['HostedZones']:
LOGGER.debug("record name: %s", str(record['Name']) + lineno())
@@ -375,15 +379,16 @@ def lambda_handler(
reverse_lookup_zone_id) + lineno())
reverse_hosted_zone_properties = new_get_hosted_zone_properties(
- route53, instance_id, reverse_lookup_zone_id)
+ route53, instance_id, reverse_lookup_zone_id)
# need to check if the property is empty {}
if reverse_hosted_zone_properties == {}:
- LOGGER.error("get_private_hosted_zone_properties returned no zone property", reverse_lookup_zone_id + lineno())
+ LOGGER.error("get_private_hosted_zone_properties returned no zone property",
+ reverse_lookup_zone_id + lineno())
reverse_zone_associated = False
- else:
+ else:
LOGGER.debug("reverse_hosted_zone_properties:"
- " %s", str(reverse_hosted_zone_properties) + lineno())
+ " %s", str(reverse_hosted_zone_properties) + lineno())
if vpc_id in map(lambda x: x['VPCId'], reverse_hosted_zone_properties['VPCs']):
LOGGER.info("instance: %s, Reverse lookup zone %s is associated with VPC %s %s",
@@ -424,8 +429,9 @@ def lambda_handler(
LOGGER.debug("dhcp_configurations: %s", str(get_dhcp_configurations) + lineno())
except BaseException as err:
- LOGGER.error("instance: %s, No DHCP option set assigned to this VPC %s\n", instance_id, str(err) + lineno())
- if SNS_ENABLE.lower() == 'true':
+ LOGGER.error("instance: %s, No DHCP option set assigned to this VPC %s\n",
+ instance_id, str(err) + lineno())
+ if SNS_ENABLE:
sns_msg = {}
sns_msg['instance_id'] = instance_id
sns_msg['client'] = 'ec2'
@@ -616,7 +622,8 @@ def lambda_handler(
# determine correct A/PTR record to be created based upon the boolean values from the tags above
if has_valid_hostname_tag and has_valid_zone_tag:
- LOGGER.info("instance: %s, custom hostname tag and custom zone tag valid.", instance_id)
+ LOGGER.info(
+ "instance: %s, custom hostname tag and custom zone tag valid.", instance_id)
final_private_hostname = custom_host_name
final_hosted_zone_name = zone_tag_hosted_zone_name
elif has_valid_hostname_tag and not (has_valid_zone_tag) and has_dhcp_dns_zone_associated_vpc: # 3
@@ -624,19 +631,23 @@ def lambda_handler(
final_private_hostname = custom_host_name
final_hosted_zone_name = private_hosted_zone_name
elif has_valid_Name_tag_hostname and has_valid_Name_tag_zonename:
- LOGGER.info("instance: %s, Name tag hostname valid and Name tag zonename valid.", instance_id)
+ LOGGER.info(
+ "instance: %s, Name tag hostname valid and Name tag zonename valid.", instance_id)
final_private_hostname = name_host
final_hosted_zone_name = name_domain_suffix
elif has_valid_Name_tag_hostname and has_valid_zone_tag:
- LOGGER.info("instance: %s, Name tag hostname valid and custom zone tag valid.", instance_id)
+ LOGGER.info(
+ "instance: %s, Name tag hostname valid and custom zone tag valid.", instance_id)
final_private_hostname = name_host
final_hosted_zone_name = zone_tag_hosted_zone_name
elif has_valid_Name_tag_hostname and has_dhcp_dns_zone_associated_vpc:
- LOGGER.info("instance: %s, Name tag hostname valid and DHCP zone is valid.", instance_id)
+ LOGGER.info(
+ "instance: %s, Name tag hostname valid and DHCP zone is valid.", instance_id)
final_private_hostname = name_host
final_hosted_zone_name = private_hosted_zone_name
elif has_valid_zone_tag and not (has_valid_hostname_tag) and not(has_valid_Name_tag_hostname):
- LOGGER.info("instance: %s, custom zone tag valid but no custom hostname, using IP address.", instance_id)
+ LOGGER.info(
+ "instance: %s, custom zone tag valid but no custom hostname, using IP address.", instance_id)
final_private_hostname = private_host_name
final_hosted_zone_name = zone_tag_hosted_zone_name
elif has_dhcp_dns_zone_associated_vpc:
@@ -644,7 +655,8 @@ def lambda_handler(
final_private_hostname = private_host_name
final_hosted_zone_name = private_hosted_zone_name
else: # none of the use-case and no suitable zone to create the A record
- LOGGER.info("instance: %s, No DHCP Associated for VPC and no custom tags. Exiting Script", instance_id)
+ LOGGER.info(
+ "instance: %s, No DHCP Associated for VPC and no custom tags. Exiting Script", instance_id)
# nothing to do, exit out script
caller_response.append(
'No DHCP Associated for VPC and no custom tags. Exiting Script')
@@ -688,119 +700,129 @@ def lambda_handler(
try:
LOGGER.debug("Creating resource records %s", lineno())
create_response = create_resource_record(
- route53,
- instance_id,
- final_hosted_zone_id,
- final_private_hostname,
- final_hosted_zone_name,
- 'A',
- private_ip
- )
+ route53,
+ instance_id,
+ final_hosted_zone_id,
+ final_private_hostname,
+ final_hosted_zone_name,
+ 'A',
+ private_ip
+ )
append_msg = 'A record in zone id: ' + \
- str(final_hosted_zone_id) + \
- ' for hosted zone ' + \
- str(final_private_hostname) + '.' + \
- str(final_hosted_zone_name) + \
- ' with value: ' + \
- str(private_ip)
+ str(final_hosted_zone_id) + \
+ ' for hosted zone ' + \
+ str(final_private_hostname) + '.' + \
+ str(final_hosted_zone_name) + \
+ ' with value: ' + \
+ str(private_ip)
if create_response == 'success':
- LOGGER.info("instance: %s, Created %s", instance_id, append_msg + lineno())
+ LOGGER.info("instance: %s, Created %s",
+ instance_id, append_msg + lineno())
caller_response.append('Created ' + append_msg)
else:
caller_response.append(create_response)
- caller_response.append('Failed to create ' + append_msg)
+ caller_response.append('Failed to create ' + append_msg)
LOGGER.error('Failed to create A record: %s', create_response)
except BaseException as err:
- LOGGER.error("instance: %s, unexpected error. %s\n", instance_id, str(err) + lineno())
+ LOGGER.error("instance: %s, unexpected error. %s\n",
+ instance_id, str(err) + lineno())
try:
if len(heritage) > 0:
- LOGGER.debug("Creating heritage TXT resource records %s", final_private_hostname + lineno())
+ LOGGER.debug("Creating heritage TXT resource records %s",
+ final_private_hostname + lineno())
create_response = create_resource_record(
- route53,
- instance_id,
- final_hosted_zone_id,
- final_private_hostname,
- final_hosted_zone_name,
- 'TXT',
- heritage_value
- )
+ route53,
+ instance_id,
+ final_hosted_zone_id,
+ final_private_hostname,
+ final_hosted_zone_name,
+ 'TXT',
+ heritage_value
+ )
append_msg = 'TXT record in zone id: ' + \
- str(final_hosted_zone_id) + \
- ' for hosted zone ' + \
- str(final_private_hostname) + '.' + \
- str(final_hosted_zone_name) + \
- ' with value: ' + \
- str(heritage_value)
+ str(final_hosted_zone_id) + \
+ ' for hosted zone ' + \
+ str(final_private_hostname) + '.' + \
+ str(final_hosted_zone_name) + \
+ ' with value: ' + \
+ str(heritage_value)
if create_response == 'success':
- LOGGER.info("instance: %s, Created %s", instance_id, append_msg + lineno())
+ LOGGER.info("instance: %s, Created %s",
+ instance_id, append_msg + lineno())
caller_response.append('Created ' + append_msg)
else:
caller_response.append(create_response)
- caller_response.append('Failed to create ' + append_msg)
+ caller_response.append('Failed to create ' + append_msg)
LOGGER.error('Failed to create TXT record: %s', create_response)
except BaseException as err:
- LOGGER.error("instance: %s, unexpected error. %s\n", instance_id, str(err) + lineno())
+ LOGGER.error("instance: %s, unexpected error. %s\n",
+ instance_id, str(err) + lineno())
try:
if reverse_zone_associated:
create_response = create_resource_record(
- route53,
- instance_id,
- reverse_lookup_zone_id,
- reversed_ip_address,
- 'in-addr.arpa',
- 'PTR',
- final_private_dns_name
- )
+ route53,
+ instance_id,
+ reverse_lookup_zone_id,
+ reversed_ip_address,
+ 'in-addr.arpa',
+ 'PTR',
+ final_private_dns_name
+ )
append_msg = 'PTR record in zone id: ' + \
- str(reverse_lookup_zone_id) + \
- ' for hosted zone ' + \
- str(reversed_ip_address) + \
- 'in-addr.arpa with value: ' + \
- str(final_private_dns_name)
+ str(reverse_lookup_zone_id) + \
+ ' for hosted zone ' + \
+ str(reversed_ip_address) + \
+ 'in-addr.arpa with value: ' + \
+ str(final_private_dns_name)
if create_response == 'success':
- LOGGER.info("instance: %s, Created %s", instance_id, append_msg + lineno())
+ LOGGER.info("instance: %s, Created %s",
+ instance_id, append_msg + lineno())
caller_response.append('Created ' + append_msg)
else:
caller_response.append(create_response)
- caller_response.append('Failed to create ' + append_msg)
+ caller_response.append('Failed to create ' + append_msg)
LOGGER.error('Failed to create PTR record: %s', create_response)
except BaseException as err:
- LOGGER.error("instance: %s, unexpected error. %s\n", instance_id, str(err) + lineno())
+ LOGGER.error("instance: %s, unexpected error. %s\n",
+ instance_id, str(err) + lineno())
try:
if reverse_zone_associated and len(heritage) > 0:
- LOGGER.debug("Creating heritage TXT resource records %s", reversed_ip_address + lineno())
+ LOGGER.debug("Creating heritage TXT resource records %s",
+ reversed_ip_address + lineno())
create_response = create_resource_record(
- route53,
- instance_id,
- reverse_lookup_zone_id,
- reversed_ip_address,
- 'in-addr.arpa',
- 'TXT',
- heritage_value
- )
+ route53,
+ instance_id,
+ reverse_lookup_zone_id,
+ reversed_ip_address,
+ 'in-addr.arpa',
+ 'TXT',
+ heritage_value
+ )
append_msg = 'TXT reverse record in zone id: ' + \
- str(reverse_lookup_zone_id) + \
- ' for hosted zone ' + \
- str(reversed_ip_address) + \
- 'in-addr.arpa with value: ' + \
- str(heritage_value)
+ str(reverse_lookup_zone_id) + \
+ ' for hosted zone ' + \
+ str(reversed_ip_address) + \
+ 'in-addr.arpa with value: ' + \
+ str(heritage_value)
if create_response == 'success':
- LOGGER.info("instance: %s, Created %s", instance_id, append_msg + lineno())
+ LOGGER.info("instance: %s, Created %s",
+ instance_id, append_msg + lineno())
caller_response.append('Created ' + append_msg)
else:
caller_response.append(create_response)
- caller_response.append('Failed to create ' + append_msg)
+ caller_response.append('Failed to create ' + append_msg)
LOGGER.error('Failed to create TXT record: %s', create_response)
except BaseException as err:
- LOGGER.error("instance: %s, unexpected error. %s\n", instance_id, str(err) + lineno())
+ LOGGER.error("instance: %s, unexpected error. %s\n",
+ instance_id, str(err) + lineno())
# elif state == 'terminated':
else: # not running so delete the records
@@ -808,19 +830,19 @@ def lambda_handler(
try:
LOGGER.debug("Deleting A record %s", final_private_hostname + lineno())
response_text = 'Delete A record in zone id: ' + str(final_hosted_zone_id) + \
- ' for hosted zone ' + str(final_private_hostname) + \
- '.' + str(final_hosted_zone_name) + ' with value: ' + \
- str(private_ip)
+ ' for hosted zone ' + str(final_private_hostname) + \
+ '.' + str(final_hosted_zone_name) + ' with value: ' + \
+ str(private_ip)
delete_response = new_delete_resource_record(
- route53,
- instance_id,
- final_hosted_zone_id,
- final_private_hostname,
- final_hosted_zone_name,
- 'A',
- private_ip
- )
+ route53,
+ instance_id,
+ final_hosted_zone_id,
+ final_private_hostname,
+ final_hosted_zone_name,
+ 'A',
+ private_ip
+ )
if delete_response == 'NoSuchHostedZone':
delete_records = False
@@ -829,16 +851,20 @@ def lambda_handler(
caller_response.append("Failed, Record Not Found: " + response_text)
elif delete_response == 'InvalidChangeBatch-RecordDoNotMatch':
delete_records = False
- caller_response.append("Failed, requested delete do not match existing record: " + response_text)
+ caller_response.append(
+ "Failed, requested delete do not match existing record: " + response_text)
elif delete_response == {}:
delete_records = False
- caller_response.append("Failed, could NOT delete Record: " + response_text)
- else:
+ caller_response.append(
+ "Failed, could NOT delete Record: " + response_text)
+ else:
caller_response.append("Success: " + response_text)
- LOGGER.info("instance: %s, Success: %s", instance_id, response_text + lineno())
+ LOGGER.info("instance: %s, Success: %s",
+ instance_id, response_text + lineno())
except BaseException as err:
delete_records = False
- LOGGER.error("instance: %s, unexpected error. %s\n", instance_id, str(err) + lineno())
+ LOGGER.error("instance: %s, unexpected error. %s\n",
+ instance_id, str(err) + lineno())
# delete TXT record associated with A record
try:
@@ -855,21 +881,22 @@ def lambda_handler(
heritage_value
)
if len(heritage) > 0:
- LOGGER.debug("Deleting heritage TXT resource records %s", final_private_hostname + lineno())
+ LOGGER.debug("Deleting heritage TXT resource records %s",
+ final_private_hostname + lineno())
response_text = 'Delete TXT record in zone id: ' + str(final_hosted_zone_id) + \
- ' for hosted zone ' + str(final_private_hostname) + \
- '.' + str(final_hosted_zone_name) + ' with value: ' + \
- str(heritage_value)
+ ' for hosted zone ' + str(final_private_hostname) + \
+ '.' + str(final_hosted_zone_name) + ' with value: ' + \
+ str(heritage_value)
delete_response = new_delete_resource_record(
- route53,
- instance_id,
- final_hosted_zone_id,
- final_private_hostname,
- final_hosted_zone_name,
- 'TXT',
- heritage_value
- )
+ route53,
+ instance_id,
+ final_hosted_zone_id,
+ final_private_hostname,
+ final_hosted_zone_name,
+ 'TXT',
+ heritage_value
+ )
if delete_response == 'NoSuchHostedZone':
delete_records = False
caller_response.append("Failed, no such zone: " + response_text)
@@ -877,16 +904,20 @@ def lambda_handler(
caller_response.append("Failed, Record Not Found: " + response_text)
elif delete_response == 'InvalidChangeBatch-RecordDoNotMatch':
delete_records = False
- caller_response.append("Failed, requested delete do not match existing record: " + response_text)
+ caller_response.append(
+ "Failed, requested delete do not match existing record: " + response_text)
elif delete_response == {}:
delete_records = False
- caller_response.append("Failed, could NOT delete Record: " + response_text)
- else:
+ caller_response.append(
+ "Failed, could NOT delete Record: " + response_text)
+ else:
caller_response.append("Success: " + response_text)
- LOGGER.info("instance: %s, Success: %s", instance_id, response_text + lineno())
+ LOGGER.info("instance: %s, Success: %s",
+ instance_id, response_text + lineno())
except BaseException as err:
delete_records = False
- LOGGER.error("instance: %s, unexpected error. %s\n", instance_id, str(err) + lineno())
+ LOGGER.error("instance: %s, unexpected error. %s\n",
+ instance_id, str(err) + lineno())
# delete PTR record
try:
@@ -895,18 +926,19 @@ def lambda_handler(
LOGGER.debug("Deleting PTR record %s", reversed_ip_address + lineno())
response_text = 'Delete PTR record in zone id: ' + str(reverse_lookup_zone_id) + \
- ' for hosted zone ' + str(reversed_ip_address) + \
- str(private_dns_name) + ' with value: ' + str(final_private_dns_name)
+ ' for hosted zone ' + str(reversed_ip_address) + \
+ str(private_dns_name) + ' with value: ' + \
+ str(final_private_dns_name)
delete_response = new_delete_resource_record(
- route53,
- instance_id,
- reverse_lookup_zone_id,
- reversed_ip_address,
- 'in-addr.arpa',
- 'PTR',
- final_private_dns_name
- )
+ route53,
+ instance_id,
+ reverse_lookup_zone_id,
+ reversed_ip_address,
+ 'in-addr.arpa',
+ 'PTR',
+ final_private_dns_name
+ )
if delete_response == 'NoSuchHostedZone':
delete_records = False
@@ -915,16 +947,20 @@ def lambda_handler(
caller_response.append("Failed, Record Not Found: " + response_text)
elif delete_response == 'InvalidChangeBatch-RecordDoNotMatch':
delete_records = False
- caller_response.append("Failed, requested delete do not match existing record: " + response_text)
+ caller_response.append(
+ "Failed, requested delete do not match existing record: " + response_text)
elif delete_response == {}:
delete_records = False
- caller_response.append("Failed, could NOT delete Record: " + response_text)
- else:
+ caller_response.append(
+ "Failed, could NOT delete Record: " + response_text)
+ else:
caller_response.append("Success: " + response_text)
- LOGGER.info("instance: %s, Success: %s", instance_id, response_text + lineno())
+ LOGGER.info("instance: %s, Success: %s",
+ instance_id, response_text + lineno())
except BaseException as err:
delete_records = False
- LOGGER.error("instance: %s, unexpected error. %s\n", instance_id, str(err) + lineno())
+ LOGGER.error("instance: %s, unexpected error. %s\n",
+ instance_id, str(err) + lineno())
try:
if get_rr:
@@ -940,20 +976,21 @@ def lambda_handler(
heritage_value
)
if len(heritage) > 0:
- LOGGER.debug("Deleting heritage TXT resource records %s", reversed_ip_address + lineno())
+ LOGGER.debug("Deleting heritage TXT resource records %s",
+ reversed_ip_address + lineno())
response_text = 'Delete TXT record in zone id: ' + str(reverse_lookup_zone_id) + \
- ' for hosted zone ' + str(reversed_ip_address) + str(private_dns_name) + \
- ' with value: ' + str(heritage_value)
+ ' for hosted zone ' + str(reversed_ip_address) + str(private_dns_name) + \
+ ' with value: ' + str(heritage_value)
delete_response = new_delete_resource_record(
- route53,
- instance_id,
- reverse_lookup_zone_id,
- reversed_ip_address,
- 'in-addr.arpa',
- 'TXT',
- heritage_value
- )
+ route53,
+ instance_id,
+ reverse_lookup_zone_id,
+ reversed_ip_address,
+ 'in-addr.arpa',
+ 'TXT',
+ heritage_value
+ )
if delete_response == 'NoSuchHostedZone':
delete_records = False
caller_response.append("Failed, no such zone: " + response_text)
@@ -961,16 +998,20 @@ def lambda_handler(
caller_response.append("Failed, Record Not Found: " + response_text)
elif delete_response == 'InvalidChangeBatch-RecordDoNotMatch':
delete_records = False
- caller_response.append("Failed, requested delete do not match existing record: " + response_text)
+ caller_response.append(
+ "Failed, requested delete do not match existing record: " + response_text)
elif delete_response == {}:
delete_records = False
- caller_response.append("Failed, could NOT delete Record: " + response_text)
- else:
+ caller_response.append(
+ "Failed, could NOT delete Record: " + response_text)
+ else:
caller_response.append("Success: " + response_text)
- LOGGER.info("instance: %s, Success: %s", instance_id, response_text + lineno())
+ LOGGER.info("instance: %s, Success: %s",
+ instance_id, response_text + lineno())
except BaseException as err:
delete_records = False
- LOGGER.error("instance: %s, unexpected error. %s\n", instance_id, str(err) + lineno())
+ LOGGER.error("instance: %s, unexpected error. %s\n",
+ instance_id, str(err) + lineno())
# Create the CNAME record only if it has passed the check
if has_valid_cname_tag:
@@ -992,84 +1033,90 @@ def lambda_handler(
" %s", str(cname_domain_suffix_id) + lineno())
create_response = create_resource_record(
- route53,
- instance_id,
- cname_domain_suffix_id,
- cname_host_name,
- cname_domain_suffix,
- 'CNAME',
- final_private_dns_name
- )
+ route53,
+ instance_id,
+ cname_domain_suffix_id,
+ cname_host_name,
+ cname_domain_suffix,
+ 'CNAME',
+ final_private_dns_name
+ )
append_msg = 'CNAME record in zone id: ' + \
- str(cname_domain_suffix_id) + \
- ' for hosted zone ' + \
- str(cname_host_name) + '.' + \
- str(cname_domain_suffix) + \
- ' with value: ' + \
- str(final_private_dns_name)
+ str(cname_domain_suffix_id) + \
+ ' for hosted zone ' + \
+ str(cname_host_name) + '.' + \
+ str(cname_domain_suffix) + \
+ ' with value: ' + \
+ str(final_private_dns_name)
if create_response == 'success':
- LOGGER.info("instance: %s, Created %s", instance_id, append_msg + lineno())
+ LOGGER.info("instance: %s, Created %s",
+ instance_id, append_msg + lineno())
caller_response.append('Created ' + append_msg)
else:
caller_response.append(create_response)
- caller_response.append('Failed to create ' + append_msg)
+ caller_response.append('Failed to create ' + append_msg)
LOGGER.error('Failed to create CNAME record: %s', create_response)
except BaseException as err:
- LOGGER.error("instance: %s, unexpected error. %s\n", instance_id, str(err) + lineno())
+ LOGGER.error("instance: %s, unexpected error. %s\n",
+ instance_id, str(err) + lineno())
try:
if len(heritage) > 0:
- LOGGER.debug("Creating heritage TXT resource records %s", TXT_RR_PREFIX + '.' + cname_host_name + lineno())
+ LOGGER.debug("Creating heritage TXT resource records %s",
+ TXT_RR_PREFIX + '.' + cname_host_name + lineno())
create_response = create_resource_record(
- route53,
- instance_id,
- cname_domain_suffix_id,
- TXT_RR_PREFIX + '.' + cname_host_name,
- cname_domain_suffix,
- 'TXT',
- heritage_value
- )
+ route53,
+ instance_id,
+ cname_domain_suffix_id,
+ TXT_RR_PREFIX + '.' + cname_host_name,
+ cname_domain_suffix,
+ 'TXT',
+ heritage_value
+ )
append_msg = 'TXT for CNAME record in zone id: ' + \
- str(cname_domain_suffix_id) + \
- ' for hosted zone ' + \
- str(TXT_RR_PREFIX) + '.' + \
- str(cname_host_name) + '.' + \
- str(cname_domain_suffix) + \
- ' with value: ' + \
- str(heritage_value)
+ str(cname_domain_suffix_id) + \
+ ' for hosted zone ' + \
+ str(TXT_RR_PREFIX) + '.' + \
+ str(cname_host_name) + '.' + \
+ str(cname_domain_suffix) + \
+ ' with value: ' + \
+ str(heritage_value)
if create_response == 'success':
- LOGGER.info("instance: %s, Created %s", instance_id, append_msg + lineno())
+ LOGGER.info("instance: %s, Created %s",
+ instance_id, append_msg + lineno())
caller_response.append('Created ' + append_msg)
else:
caller_response.append(create_response)
caller_response.append('Failed to create ' + append_msg)
- LOGGER.error('Failed to create TXT fpr CNAME record: %s', create_response)
+ LOGGER.error(
+ 'Failed to create TXT fpr CNAME record: %s', create_response)
except BaseException as err:
- LOGGER.error("instance: %s, unexpected error. %s\n", instance_id, str(err) + lineno())
+ LOGGER.error("instance: %s, unexpected error. %s\n",
+ instance_id, str(err) + lineno())
else:
# delete the CNAME record
try:
LOGGER.debug("deleting CNAME record %s", lineno())
response_text = 'Delete CNAME record in zone id: ' + str(cname_domain_suffix_id) + \
- ' for hosted zone ' + str(cname_host_name) + '.' + \
- str(cname_domain_suffix) + ' with value: ' + \
- str(final_private_dns_name)
+ ' for hosted zone ' + str(cname_host_name) + '.' + \
+ str(cname_domain_suffix) + ' with value: ' + \
+ str(final_private_dns_name)
# pause 1 before deleting to avoid API limit
# time.sleep(1)
delete_response = new_delete_resource_record(
- route53,
- instance_id,
- cname_domain_suffix_id,
- cname_host_name,
- cname_domain_suffix,
- 'CNAME',
- final_private_dns_name
- )
+ route53,
+ instance_id,
+ cname_domain_suffix_id,
+ cname_host_name,
+ cname_domain_suffix,
+ 'CNAME',
+ final_private_dns_name
+ )
if delete_response == 'NoSuchHostedZone':
delete_records = False
@@ -1078,16 +1125,20 @@ def lambda_handler(
caller_response.append("Failed, Record Not Found: " + response_text)
elif delete_response == 'InvalidChangeBatch-RecordDoNotMatch':
delete_records = False
- caller_response.append("Failed, requested delete do not match existing record: " + response_text)
+ caller_response.append(
+ "Failed, requested delete do not match existing record: " + response_text)
elif delete_response == {}:
delete_records = False
- caller_response.append("Failed, could NOT delete Record: " + response_text)
- else:
+ caller_response.append(
+ "Failed, could NOT delete Record: " + response_text)
+ else:
caller_response.append("Success: " + response_text)
- LOGGER.info("instance: %s, Success: %s", instance_id, response_text + lineno())
+ LOGGER.info("instance: %s, Success: %s",
+ instance_id, response_text + lineno())
except BaseException as err:
delete_records = False
- LOGGER.error("instance: %s, unexpected error. %s\n", instance_id, str(err) + lineno())
+ LOGGER.error("instance: %s, unexpected error. %s\n",
+ instance_id, str(err) + lineno())
# delete the CNAME txt record
try:
@@ -1102,52 +1153,61 @@ def lambda_handler(
heritage_value
)
if len(heritage) > 0:
- LOGGER.debug("Deleting heritage TXT resource records %s", TXT_RR_PREFIX + '.' + cname_host_name + lineno())
+ LOGGER.debug("Deleting heritage TXT resource records %s",
+ TXT_RR_PREFIX + '.' + cname_host_name + lineno())
response_text = 'Delete TXT for CNAME record in zone id: ' + str(cname_domain_suffix_id) \
- + ' for hosted zone ' + str(TXT_RR_PREFIX) + '.' + str(cname_host_name) \
- + '.' + str(cname_domain_suffix) + ' with value: ' \
- + str(heritage_value)
+ + ' for hosted zone ' + str(TXT_RR_PREFIX) + '.' + str(cname_host_name) \
+ + '.' + str(cname_domain_suffix) + ' with value: ' \
+ + str(heritage_value)
delete_response = new_delete_resource_record(
- route53,
- instance_id,
- cname_domain_suffix_id,
- TXT_RR_PREFIX + '.' + cname_host_name,
- cname_domain_suffix,
- 'TXT',
- heritage_value
- )
+ route53,
+ instance_id,
+ cname_domain_suffix_id,
+ TXT_RR_PREFIX + '.' + cname_host_name,
+ cname_domain_suffix,
+ 'TXT',
+ heritage_value
+ )
if delete_response == 'NoSuchHostedZone':
delete_records = False
caller_response.append("Failed, no such zone: " + response_text)
elif delete_response == 'InvalidChangeBatch-RecordNotFound':
- caller_response.append("Failed, Record Not Found: " + response_text)
+ caller_response.append(
+ "Failed, Record Not Found: " + response_text)
elif delete_response == 'InvalidChangeBatch-RecordDoNotMatch':
delete_records = False
- caller_response.append("Failed, requested delete do not match existing record: " + response_text)
+ caller_response.append(
+ "Failed, requested delete do not match existing record: " + response_text)
elif delete_response == {}:
delete_records = False
- caller_response.append("Failed, could NOT delete Record: " + response_text)
- else:
+ caller_response.append(
+ "Failed, could NOT delete Record: " + response_text)
+ else:
caller_response.append("Success: " + response_text)
- LOGGER.info("instance: %s, Success: %s", instance_id, response_text + lineno())
+ LOGGER.info("instance: %s, Success: %s",
+ instance_id, response_text + lineno())
except BaseException as err:
delete_records = False
- LOGGER.error("instance: %s, unexpected error. %s\n", instance_id, str(err) + lineno())
+ LOGGER.error("instance: %s, unexpected error. %s\n",
+ instance_id, str(err) + lineno())
# Clean up DynamoDB after deleting records
if state != 'running':
# only if all records were succesfully deleted
- if delete_records:
+ if delete_records:
delete_item_from_dynamodb_table(dynamodb_client, DDBNAME, instance_id)
- LOGGER.info("instance: %s, deleted the item from DynamoDB: %s", instance_id, DDBNAME + lineno())
+ LOGGER.info("instance: %s, deleted the item from DynamoDB: %s",
+ instance_id, DDBNAME + lineno())
caller_response.insert(0, 'Successfully removed recordsets')
return caller_response
else:
- LOGGER.info("instance: %s, not all records deleted, leaving item in DynamoDB: %s", instance_id, DDBNAME + lineno())
- caller_response.insert(0, 'Failed to remove recordsets, leaving DynamoDB item for instance: ' + instance_id)
+ LOGGER.info("instance: %s, not all records deleted, leaving item in DynamoDB: %s",
+ instance_id, DDBNAME + lineno())
+ caller_response.insert(
+ 0, 'Failed to remove recordsets, leaving DynamoDB item for instance: ' + instance_id)
return caller_response
else:
@@ -1208,7 +1268,7 @@ def new_list_hosted_zones(client, instance_id):
i = 0
hosted_zones = {}
# retry to handle errors in the possible API call
- while i < MAX_API_RETRY:
+ while i < MAX_API_RETRY:
try:
hosted_zones = client.list_hosted_zones()
LOGGER.debug("list_hosted_zones returned without error. %s", lineno())
@@ -1216,18 +1276,23 @@ def new_list_hosted_zones(client, instance_id):
except ClientError as err:
error_message = str(err)
if "(Throttling)" in str(err):
- LOGGER.debug("list_hosted_zones throttled due to API limit, retrying: %s", str(err) + lineno())
+ LOGGER.debug(
+ "list_hosted_zones throttled due to API limit, retrying: %s", str(err) + lineno())
else:
- LOGGER.info("instance: %s, unexpected error. %s\n", instance_id, error_message + lineno())
- i +=1
- LOGGER.info("instance: %s, list_hosted_zones returned error, waiting before retry. %s", instance_id, str(i) + lineno())
+ LOGGER.info("instance: %s, unexpected error. %s\n",
+ instance_id, error_message + lineno())
+ i += 1
+ LOGGER.info("instance: %s, list_hosted_zones returned error, waiting before retry. %s",
+ instance_id, str(i) + lineno())
time.sleep(i)
if hosted_zones == {}:
- LOGGER.error("instance: %s, list_hosted_zones returned error. Timed out. %s", instance_id, str(i) + lineno())
+ LOGGER.error("instance: %s, list_hosted_zones returned error. Timed out. %s",
+ instance_id, str(i) + lineno())
return hosted_zones
+
def list_tables(client):
"""
List the dynamodb tables
@@ -1255,7 +1320,8 @@ def delete_item_from_dynamodb_table(client, table, instance_id):
'InstanceId': {'S': instance_id}
})
except ClientError as err:
- LOGGER.error("instance: %s, unexpected error. %s\n", instance_id, str(err) + lineno())
+ LOGGER.error("instance: %s, unexpected error. %s\n",
+ instance_id, str(err) + lineno())
def put_item_in_dynamodb_table(client, table, instance_id, instance_attributes):
@@ -1279,7 +1345,8 @@ def put_item_in_dynamodb_table(client, table, instance_id, instance_attributes):
}
)
except ClientError as err:
- LOGGER.error("instance: %s, unexpected error. %s\n", instance_id, str(err) + lineno())
+ LOGGER.error("instance: %s, unexpected error. %s\n",
+ instance_id, str(err) + lineno())
def get_item_from_dynamodb_table(client, table, instance_id):
@@ -1314,7 +1381,8 @@ def get_item_from_dynamodb_table(client, table, instance_id):
return json.loads(item)
return None
except ClientError as err:
- LOGGER.error("instance: %s, unexpected error. %s\n", instance_id, str(err) + lineno())
+ LOGGER.error("instance: %s, unexpected error. %s\n",
+ instance_id, str(err) + lineno())
def get_private_hosted_zone_collection(private_hosted_zones):
@@ -1461,36 +1529,38 @@ def new_change_resource_recordset(client, instance_id, zone_id, host_name, hoste
i = 0
update_response = {}
# retry to handle errors in the possible API call
- while i < MAX_API_RETRY:
+ while i < MAX_API_RETRY:
try:
LOGGER.debug("Creating %s record %s in zone %s"
- " %s", record_type, host_name, hosted_zone_name, lineno())
+ " %s", record_type, host_name, hosted_zone_name, lineno())
change_batch = {
- "Comment": "Updated by Lambda DDNS",
- "Changes": [
- {
- "Action": "UPSERT",
- "ResourceRecordSet": {
- "Name": host_name + hosted_zone_name,
- "Type": record_type,
- "TTL": DNS_RR_TTL,
- "ResourceRecords": [
- {
- "Value": value
- },
- ]
- }
- },
- ]
- }
-
- LOGGER.debug("change_resource_record_sets change_batch: %s", json.dumps(change_batch) + lineno())
+ "Comment": "Updated by Lambda DDNS",
+ "Changes": [
+ {
+ "Action": "UPSERT",
+ "ResourceRecordSet": {
+ "Name": host_name + hosted_zone_name,
+ "Type": record_type,
+ "TTL": DNS_RR_TTL,
+ "ResourceRecords": [
+ {
+ "Value": value
+ },
+ ]
+ }
+ },
+ ]
+ }
+
+ LOGGER.debug("change_resource_record_sets change_batch: %s",
+ json.dumps(change_batch) + lineno())
update_response = client.change_resource_record_sets(
HostedZoneId=zone_id,
ChangeBatch=change_batch
)
- LOGGER.debug("change_resource_record_sets UPSERT returned without error - response: %s", str(update_response) + lineno())
+ LOGGER.debug("change_resource_record_sets UPSERT returned without error - response: %s",
+ str(update_response) + lineno())
break
# return response
except ClientError as err:
@@ -1499,33 +1569,40 @@ def new_change_resource_recordset(client, instance_id, zone_id, host_name, hoste
update_response = "NoSuchHostedZone"
break
elif 'InvalidChangeBatch' in str(err) and 'is not permitted in zone' in str(err):
- LOGGER.error("Cannot create record - most likely wrong zone name specified: %s", str(err) + lineno())
+ LOGGER.error(
+ "Cannot create record - most likely wrong zone name specified: %s", str(err) + lineno())
update_response = "InvalidChangeBatch-WrongZoneName"
break
elif "(Throttling)" in str(err):
- LOGGER.debug("change_resource_record_sets UPSERT throttled due to API limit, retrying: %s", str(err) + lineno())
+ LOGGER.debug("change_resource_record_sets UPSERT throttled due to API limit, retrying: %s", str(
+ err) + lineno())
else:
- LOGGER.info("instance: %s, unexpected error. %s\n", instance_id, str(err) + lineno())
-
- i +=1
- LOGGER.info("instance: %s, change_resource_record_sets UPSERT returned error, waiting before retry. %s", instance_id, str(i) + lineno())
+ LOGGER.info("instance: %s, unexpected error. %s\n",
+ instance_id, str(err) + lineno())
+
+ i += 1
+ LOGGER.info("instance: %s, change_resource_record_sets UPSERT returned error, waiting before retry. %s",
+ instance_id, str(i) + lineno())
time.sleep(i)
if update_response == {}:
- if SNS_ENABLE.lower() == 'true':
+ if SNS_ENABLE:
try:
sns_msg = {}
sns_msg['instance_id'] = instance_id
sns_msg['client'] = 'route53'
sns_msg['boto3_method'] = 'change_resource_record_sets'
sns_msg['message'] = 'change_resource_record_sets could not UPSERT record'
- sns_msg['change_resource_record_sets']= {'HostedZoneId' : zone_id,'ChangeBatch': change_batch}
+ sns_msg['change_resource_record_sets'] = {
+ 'HostedZoneId': zone_id, 'ChangeBatch': change_batch}
publish_to_sns(get_sns_client(), json.dumps(sns_msg))
except:
- LOGGER.info("instance: %s, error: %s", instance_id, str(sys.exc_info()[0]) + lineno())
+ LOGGER.info("instance: %s, error: %s", instance_id,
+ str(sys.exc_info()[0]) + lineno())
return update_response
+
def create_resource_record(client, instance_id, zone_id, host_name, hosted_zone_name, record_type, value):
"""
This function creates resource records in the hosted zone passed by the calling function.
@@ -1553,25 +1630,29 @@ def create_resource_record(client, instance_id, zone_id, host_name, hosted_zone_
# time.sleep(1)
create_response = new_change_resource_recordset(
- client,
- instance_id,
- zone_id,
- host_name,
- hosted_zone_name,
- record_type,
- value
- )
+ client,
+ instance_id,
+ zone_id,
+ host_name,
+ hosted_zone_name,
+ record_type,
+ value
+ )
if create_response == 'NoSuchHostedZone':
- LOGGER.debug("DNS Record create failed: %s", str(create_response) + lineno())
+ LOGGER.debug("DNS Record create failed: %s",
+ str(create_response) + lineno())
msg = 'NoSuchHostedZone: ' + str(create_response)
elif create_response == 'InvalidChangeBatch-WrongZoneName':
- LOGGER.debug("DNS Record create failed: %s", str(create_response) + lineno())
+ LOGGER.debug("DNS Record create failed: %s",
+ str(create_response) + lineno())
msg = 'InvalidChangeBatch-WrongZoneName: ' + str(create_response)
- elif create_response =={}:
- LOGGER.debug("DNS Record create failed: %s", str(create_response) + lineno())
+ elif create_response == {}:
+ LOGGER.debug("DNS Record create failed: %s",
+ str(create_response) + lineno())
msg = 'DNS Recored Create Failed: ' + str(create_response)
- else:
- LOGGER.debug("DNS Record create success: %s", str(create_response) + lineno())
+ else:
+ LOGGER.debug("DNS Record create success: %s",
+ str(create_response) + lineno())
msg = 'success'
LOGGER.debug("response: %s", str(create_response) + lineno())
@@ -1640,37 +1721,42 @@ def new_get_resource_record(client, instance_id, zone_id, host_name, hosted_zone
i = 0
value = None
- while i < MAX_API_RETRY:
+ while i < MAX_API_RETRY:
try:
LOGGER.debug("Getting %s record %s in zone %s"
- " %s", record_type, host_name, hosted_zone_name, lineno())
+ " %s", record_type, host_name, hosted_zone_name, lineno())
if host_name[-1] != '.':
host_name = host_name + '.'
response = client.list_resource_record_sets(
- HostedZoneId=zone_id,
- StartRecordName=host_name,
- StartRecordType=record_type,
- MaxItems=1)
+ HostedZoneId=zone_id,
+ StartRecordName=host_name,
+ StartRecordType=record_type,
+ MaxItems=1)
if len(response) > 0:
rr_set = response['ResourceRecordSets'][0]
if rr_set['Name'] == host_name and rr_set['Type'] == record_type:
value = rr_set['ResourceRecords'][0]['Value']
-
- LOGGER.debug("list_resource_record_sets returned without error. %s", lineno())
+
+ LOGGER.debug(
+ "list_resource_record_sets returned without error. %s", lineno())
break
except ClientError as err:
if 'Not Found' in str(err):
- LOGGER.debug("list_resource_record_sets not found error: %s", str(err) + lineno())
+ LOGGER.debug("list_resource_record_sets not found error: %s",
+ str(err) + lineno())
if 'InvalidChangeBatch' in str(err) and 'it was not found' in str(err):
- LOGGER.debug("list_resource_record_sets not found error: %s", str(err) + lineno())
+ LOGGER.debug("list_resource_record_sets not found error: %s",
+ str(err) + lineno())
- LOGGER.info("instance: %s, list_resource_record_sets unexpected error. %s\n", instance_id, str(err) + lineno())
-
- i +=1
- LOGGER.info("instance: %s, list_resource_record_sets returned error, waiting before retry. %s", instance_id, str(i) + lineno())
+ LOGGER.info("instance: %s, list_resource_record_sets unexpected error. %s\n",
+ instance_id, str(err) + lineno())
+
+ i += 1
+ LOGGER.info("instance: %s, list_resource_record_sets returned error, waiting before retry. %s",
+ instance_id, str(i) + lineno())
time.sleep(i)
return value
@@ -1745,44 +1831,46 @@ def new_delete_resource_record(client, instance_id, zone_id, host_name, hosted_z
i = 0
delete_response = {}
# retry to handle errors in the possible API call
- while i < MAX_API_RETRY:
+ while i < MAX_API_RETRY:
try:
LOGGER.debug("Deleting %s record %s in zone %s"
- " %s", record_type, host_name, hosted_zone_name, lineno())
+ " %s", record_type, host_name, hosted_zone_name, lineno())
if host_name[-1] != '.':
host_name = host_name + '.'
change_batch = {
- "Comment": "Updated by Lambda DDNS",
- "Changes": [
- {
- "Action": "DELETE",
- "ResourceRecordSet": {
- "Name": host_name + hosted_zone_name,
- "Type": record_type,
- "TTL": DNS_RR_TTL,
- "ResourceRecords": [
- {
- "Value": value
- },
- ]
- }
- }
- ]
- }
-
- LOGGER.debug("change_resource_record_sets change_batch: %s", json.dumps(change_batch) + lineno())
+ "Comment": "Updated by Lambda DDNS",
+ "Changes": [
+ {
+ "Action": "DELETE",
+ "ResourceRecordSet": {
+ "Name": host_name + hosted_zone_name,
+ "Type": record_type,
+ "TTL": DNS_RR_TTL,
+ "ResourceRecords": [
+ {
+ "Value": value
+ },
+ ]
+ }
+ }
+ ]
+ }
+
+ LOGGER.debug("change_resource_record_sets change_batch: %s",
+ json.dumps(change_batch) + lineno())
delete_response = client.change_resource_record_sets(
HostedZoneId=zone_id,
ChangeBatch=change_batch
)
- LOGGER.debug("change_resource_record_sets DELETE returned without error - response: %s", str(delete_response) + lineno())
+ LOGGER.debug("change_resource_record_sets DELETE returned without error - response: %s",
+ str(delete_response) + lineno())
break
except ClientError as err:
-
+
if 'NoSuchHostedZone' in str(err) and 'No hosted zone found with ID' in str(err):
LOGGER.debug("Hosted zone not found error: %s", str(err) + lineno())
delete_response = "NoSuchHostedZone"
@@ -1792,30 +1880,36 @@ def new_delete_resource_record(client, instance_id, zone_id, host_name, hosted_z
delete_response = "InvalidChangeBatch-RecordNotFound"
break
elif 'InvalidChangeBatch' in str(err) and 'values provided do not match the current values' in str(err):
- LOGGER.debug("Record do not match current value error: %s", str(err) + lineno())
+ LOGGER.debug("Record do not match current value error: %s",
+ str(err) + lineno())
delete_response = "InvalidChangeBatch-RecordDoNotMatch"
break
elif '(Throttling)' in str(err):
- LOGGER.debug("change_resource_record_sets DELETE throttled due to API limit, retrying: %s", str(err) + lineno())
+ LOGGER.debug("change_resource_record_sets DELETE throttled due to API limit, retrying: %s", str(
+ err) + lineno())
else:
- LOGGER.info("instance: %s, unexpected error. %s\n", instance_id, str(err) + lineno())
-
- i +=1
- LOGGER.info("instance: %s, change_resource_record_sets DELETE returned error, waiting before retry. %s", instance_id, str(i) + lineno())
+ LOGGER.info("instance: %s, unexpected error. %s\n",
+ instance_id, str(err) + lineno())
+
+ i += 1
+ LOGGER.info("instance: %s, change_resource_record_sets DELETE returned error, waiting before retry. %s",
+ instance_id, str(i) + lineno())
time.sleep(i)
if (delete_response == {} or delete_response == "InvalidChangeBatch-RecordDoNotMatch"):
- if SNS_ENABLE.lower() == 'true':
+ if SNS_ENABLE:
try:
sns_msg = {}
sns_msg['instance_id'] = instance_id
sns_msg['client'] = 'route53'
sns_msg['boto3_method'] = 'change_resource_record_sets'
sns_msg['message'] = 'change_resource_record_sets could not DELETE record'
- sns_msg['change_resource_record_sets']= {'HostedZoneId' : zone_id,'ChangeBatch': change_batch}
+ sns_msg['change_resource_record_sets'] = {
+ 'HostedZoneId': zone_id, 'ChangeBatch': change_batch}
publish_to_sns(get_sns_client(), json.dumps(sns_msg))
except:
- LOGGER.info("instance: %s, error: %s", instance_id, str(sys.exc_info()[0]) + lineno())
+ LOGGER.info("instance: %s, error: %s", instance_id,
+ str(sys.exc_info()[0]) + lineno())
return delete_response
@@ -1900,26 +1994,27 @@ def is_valid_zone(route53, instance_id, zonename, hosted_zones, vpc_id, private_
hosted_zone_id + lineno())
private_hosted_zone_properties = new_get_hosted_zone_properties(
- route53,
- instance_id,
- hosted_zone_id
- )
-
+ route53,
+ instance_id,
+ hosted_zone_id
+ )
+
# need to check if the property is empty {}
if private_hosted_zone_properties == {}:
- LOGGER.error("get_private_hosted_zone_properties returned no zone property", hosted_zone_id + lineno())
+ LOGGER.error(
+ "get_private_hosted_zone_properties returned no zone property", hosted_zone_id + lineno())
else:
LOGGER.debug("private_hosted_zone_properties:"
- " %s", str(private_hosted_zone_properties) + lineno())
+ " %s", str(private_hosted_zone_properties) + lineno())
# check if the VPC is associated with the PHZ
if vpc_id in map(lambda x: x['VPCId'], private_hosted_zone_properties['VPCs']):
LOGGER.debug("Privated Hosted Zone associated with VPC: %s",
- zonename + lineno())
+ zonename + lineno())
return True
else:
LOGGER.debug("Private Hosted Zone is NOT associated with vpc: %s",
- zonename + lineno())
+ zonename + lineno())
else:
LOGGER.debug("Domain Name does not match Private Hosted Zones: %s",
zonename + lineno())
@@ -2149,10 +2244,10 @@ def new_get_hosted_zone_properties(client, instance_id, zone_id):
i = 0
hosted_zone_properties = {}
# retry to handle errors in the possible API call
- while i < MAX_API_RETRY:
+ while i < MAX_API_RETRY:
try:
LOGGER.debug('getting hosted zone properties: zone_id: %s',
- str(zone_id) + lineno())
+ str(zone_id) + lineno())
hosted_zone_properties = client.get_hosted_zone(Id=zone_id)
LOGGER.debug('hosted_zone_properties: %s', str(
hosted_zone_properties) + lineno())
@@ -2164,12 +2259,15 @@ def new_get_hosted_zone_properties(client, instance_id, zone_id):
except ClientError as err:
error_message = str(err)
if "(Throttling)" in str(err):
- LOGGER.debug("get_hosted_zone throttled due to API limit, retrying: %s", str(err) + lineno())
+ LOGGER.debug(
+ "get_hosted_zone throttled due to API limit, retrying: %s", str(err) + lineno())
else:
- LOGGER.info("instance: %s, unexpected error. %s\n", instance_id, error_message + lineno())
+ LOGGER.info("instance: %s, unexpected error. %s\n",
+ instance_id, error_message + lineno())
- i +=1
- LOGGER.info("instance: %s, get_hosted_zone returned error, waiting before retry. %s", instance_id, str(i) + lineno())
+ i += 1
+ LOGGER.info("instance: %s, get_hosted_zone returned error, waiting before retry. %s",
+ instance_id, str(i) + lineno())
time.sleep(i)
return hosted_zone_properties
@@ -2291,6 +2389,7 @@ def parse_heritage(info):
except:
return {}
+
def publish_to_sns(client, message):
"""
Publish a simple message to the specified SNS topic
@@ -2301,16 +2400,17 @@ def publish_to_sns(client, message):
:return:
"""
- LOGGER.debug("Sending SNS message: %s to SNSTopic %s", str(message), SNS_TOPIC_ARN + lineno())
+ LOGGER.debug("Sending SNS message: %s to SNSTopic %s",
+ str(message), SNS_TOPIC_ARN + lineno())
if SNS_TOPIC_ARN != '':
try:
response = client.publish(
- TopicArn = SNS_TOPIC_ARN,
- Message = str(message)
- )
- LOGGER.debug("sns response: %s", str(response)+lineno())
+ TopicArn=SNS_TOPIC_ARN,
+ Message=str(message)
+ )
+ LOGGER.debug("sns response: %s", str(response) + lineno())
except ClientError as err:
- LOGGER.debug("Unexpected error: %s", str(err)+lineno())
+ LOGGER.debug("Unexpected error: %s", str(err) + lineno())
else:
LOGGER.debug("No SNS Topic specified, ignoring")
diff --git a/role.tf b/role.tf
index d4bd1f0..bca3db4 100644
--- a/role.tf
+++ b/role.tf
@@ -80,6 +80,16 @@ data "aws_iam_policy_document" "lambda_policy" {
]
resources = [var.create ? aws_dynamodb_table.table[0].arn : null]
}
+ dynamic "statement" {
+ for_each = var.create && var.enable_sns > 0 ? toset(["1"]) : toset([])
+ iterator = s
+ content {
+ sid = "SNSLambdaAccess"
+ effect = "Allow"
+ actions = ["sns:Get*", "sns:Publish*"]
+ resources = [var.create && var.enable_sns ? aws_sns_topic.topic[0].arn : ""]
+ }
+ }
}
data "aws_iam_policy_document" "lambda_assume" {
diff --git a/sns.tf b/sns.tf
new file mode 100644
index 0000000..2992e4f
--- /dev/null
+++ b/sns.tf
@@ -0,0 +1,55 @@
+locals {
+ sns_name = var.sns_topic_name != null ? var.sns_topic_name : local.name
+}
+
+resource "aws_sns_topic" "topic" {
+ count = var.create && var.enable_sns ? 1 : 0
+ name = local.sns_name
+ display_name = "dynr53"
+}
+
+resource "aws_sns_topic_policy" "topic" {
+ count = var.create && var.enable_sns ? 1 : 0
+ arn = var.create && var.enable_sns ? aws_sns_topic.topic[0].arn : ""
+ policy = data.aws_iam_policy_document.topic.json
+}
+
+# is this too much?
+data "aws_iam_policy_document" "topic" {
+ policy_id = local.sns_name
+ statement {
+ sid = "SNSPermissions"
+ effect = "Allow"
+ principals {
+ type = "AWS"
+ identifiers = ["*"]
+ }
+ actions = [
+ "sns:Subscribe",
+ "sns:SetTopicAttributes",
+ "sns:RemovePermission",
+ "sns:Receive",
+ "sns:Publish",
+ "sns:ListSubscriptionsByTopic",
+ "sns:GetTopicAttributes",
+ "sns:DeleteTopic",
+ "sns:AddPermission",
+ ]
+ condition {
+ test = "StringEquals"
+ variable = "AWS:SourceOwner"
+ values = [local.account_id]
+ }
+ resources = [aws_sns_topic.topic.arn]
+ }
+}
+##
+## {
+## "Sid": "sns",
+## "Effect": "Allow",
+## "Action": {
+## "sns:Get*",
+## "sns:Publish"
+## "Resource":