Ejemplo n.º 1
0
        /// <summary>
        /// Format of the R53 spec is:
        /// <code>
        ///     &lt;zone-id&gt; ';' &lt;record-name&gt; [ ';' [ &lt;record-type&gt; ] [ ';' [ &lt;record-ttl&gt; ] [ ';' &lt;record-value&gt; ] ] ]
        /// </code>
        /// </summary>
        R53Spec ResolveR53Spec(Instance inst, Dictionary <string, string> tags)
        {
            var specTag   = _ec2Eval.Evaluate(tags[R53TriggerTagName], inst);
            var specParts = specTag.Split(";", 5);

            if (specParts.Length < 2)
            {
                throw new Exception("invalid R53 spec");
            }

            if (tags.TryGetValue(R53HealthCheckTriggerTagName, out var healthTag))
            {
                healthTag = _ec2Eval.Evaluate(healthTag, inst);
                _logger.LogInformation($"Adjusting R53 Health Check per resolved spec: {healthTag}");
            }

            _logger.LogInformation($"Adjusting Route53 records per resolved spec: {specTag}");

            var zone = specParts[0];
            var name = specParts[1];

            if (!name.EndsWith("."))
            {
                name += ".";
            }

            var typeRaw = specParts.Length > 2 ? specParts[2] : null;
            var ttlRaw  = specParts.Length > 3 ? specParts[3] : null;
            var valRaw  = specParts.Length > 4 ? specParts[4] : null;

            // Resolve Type or default to A
            var type = string.IsNullOrEmpty(typeRaw) ? null : RRType.FindValue(typeRaw);

            if (type == null)
            {
                _logger.LogInformation("Defaulting to record type `A`");
                type = RRType.A;
            }

            // Resolve TTL or default to 0
            if (!long.TryParse(ttlRaw, out var ttl))
            {
                _logger.LogInformation("Defaulting to TTL 60s");
                ttl = 60;
            }

            // Resolve the record value
            string val = string.IsNullOrEmpty(valRaw)
                ? inst.PublicIpAddress ?? inst.PrivateIpAddress
                : valRaw;

            return(new R53Spec
            {
                Zone = zone,
                Name = name,
                Type = type,
                TTL = ttl,
                Value = val,
            });
        }
Ejemplo n.º 2
0
        private IRecord ReadRecord(RRType type, ushort rdLength)
        {
            switch (type)
            {
            case RRType.A:
                return(new ARecord
                {
                    Address = $"{ReadByte()}.{ReadByte()}.{ReadByte()}.{ReadByte()}"
                });

            case RRType.AAAA:
                return(new AAAARecord
                {
                    Address = $"{ReadUint16():x}:{ReadUint16():x}:{ReadUint16():x}:{ReadUint16():x}:{ReadUint16():x}:{ReadUint16():x}:{ReadUint16():x}:{ReadUint16():x}"
                });

            case RRType.TXT:
                return(new TextRecord
                {
                    Text = ReadText(rdLength)
                });

            case RRType.SRV:
                return(new ServiceRecord
                {
                    Priority = ReadUint16(),
                    Weight = ReadUint16(),
                    Port = ReadUint16(),
                    Target = ReadDomainName()
                });

            case RRType.PTR:
                return(new PointerRecord
                {
                    PTRDName = ReadDomainName()
                });

            case RRType.NSEC:
                return(new NSECRecord
                {
                    RData = _bytes.Skip(_index).ToArray()
                });

            case RRType.MF:
                return(new MailForwarderRecord());

            default:
                return(new UnknownRecord
                {
                    Type = type,
                    RData = _bytes.Skip(_index).ToArray()
                });
            }
        }
Ejemplo n.º 3
0
        public async Task HandleInitR53(Instance inst, Dictionary <string, string> tags)
        {
            _logger.LogInformation("Handling CREATING R53 records");

            var r53Spec    = ResolveR53Spec(inst, tags);
            var r53Routing = ResolveR53RoutingSpec(inst, tags);

            var rrset = new ResourceRecordSet
            {
                Name            = r53Spec.Name,
                Type            = RRType.FindValue(r53Spec.Type),
                TTL             = r53Spec.TTL,
                ResourceRecords = new List <ResourceRecord>
                {
                    new ResourceRecord(r53Spec.Value),
                },
            };

            // Optional routing policy configuration
            r53Routing?.Apply(rrset);

            var changeRequ = new ChangeResourceRecordSetsRequest
            {
                HostedZoneId = r53Spec.Zone,
                ChangeBatch  = new ChangeBatch
                {
                    Changes = new List <Change>
                    {
                        new Change
                        {
                            Action            = ChangeAction.UPSERT,
                            ResourceRecordSet = rrset,
                        }
                    }
                }
            };

            var changeResp = await _r53.ChangeResourceRecordSetsAsync(changeRequ);

            _logger.LogInformation("UPSERT request completed, response:");
            _logger.LogInformation(JsonSerializer.Serialize(changeResp));
        }
        private async Task <IEnumerable <ResourceRecordSet> > FindRecordSetsAsync(HostedZone zone, string dnsName, string recordType)
        {
            _logger.LogDebug("Finding record sets for {RecordType} {DnsName} in zone {Zone}", recordType, dnsName, zone.Name);

            var result        = new List <ResourceRecordSet>();
            var rootedDnsName = dnsName.EndsWith(DomainSegmentSeparator.ToString()) ? dnsName : dnsName + DomainSegmentSeparator;
            var remainder     = dnsName.Replace(zone.Name, String.Empty);

            var recordSets = await _route53Client.ListResourceRecordSetsAsync(
                new ListResourceRecordSetsRequest()
            {
                HostedZoneId    = zone.Id,
                StartRecordType = RRType.FindValue(recordType),
                StartRecordName = dnsName
            });

            do
            {
                foreach (var recordSet in recordSets.ResourceRecordSets)
                {
                    if (recordSet.Name.ToLower().Equals(rootedDnsName.ToLower()))
                    {
                        result.Add(recordSet);
                    }
                }

                recordSets = await _route53Client.ListResourceRecordSetsAsync(
                    new ListResourceRecordSetsRequest()
                {
                    HostedZoneId          = zone.Id,
                    StartRecordType       = recordSets.NextRecordType,
                    StartRecordName       = recordSets.NextRecordName,
                    StartRecordIdentifier = recordSets.NextRecordIdentifier
                });
            } while (recordSets.IsTruncated);

            _logger.LogInformation("{Count} record sets were found for {RecordType} {DnsName} in zone {Zone}", result.Count, recordType, dnsName, zone.Name);

            return(result);
        }
Ejemplo n.º 5
0
 /// <summary>
 /// Instantiates ResourceRecordSet with the parameterized properties
 /// </summary>
 /// <param name="name">The name of the domain you want to perform the action on. Enter a fully qualified domain name, for example, <code>www.example.com</code>. You can optionally include a trailing dot. If you omit the trailing dot, Amazon Route 53 still assumes that the domain name that you specify is fully qualified. This means that Amazon Route 53 treats <code>www.example.com</code> (without a trailing dot) and <code>www.example.com.</code> (with a trailing dot) as identical. For information about how to specify characters other than a-z, 0-9, and - (hyphen) and how to specify internationalized domain names, see <a href="http://docs.aws.amazon.com/Route53/latest/DeveloperGuide/DomainNameFormat.html">DNS Domain Name Format</a> in the <i>Amazon Route 53 Developer Guide</i>. You can use an asterisk (*) character in the name. DNS treats the * character either as a wildcard or as the * character (ASCII 42), depending on where it appears in the name. For more information, see <a href="http://docs.aws.amazon.com/Route53/latest/DeveloperGuide/DomainNameFormat.html#domain-name-format-asterisk">Using an Asterisk (*) in the Names of Hosted Zones and Resource Record Sets</a> in the <i>Amazon Route 53 Developer Guide</i> <important>You can't use the * wildcard for resource records sets that have a type of NS.</important></param>
 /// <param name="type">The DNS record type. For information about different record types and how data is encoded for them, see <a href="http://docs.aws.amazon.com/Route53/latest/DeveloperGuide/ResourceRecordTypes.html">Supported DNS Resource Record Types</a> in the <i>Amazon Route 53 Developer Guide</i>. Valid values for basic resource record sets: <code>A</code> | <code>AAAA</code> | <code>CNAME</code> | <code>MX</code> | <code>NS</code> | <code>PTR</code> | <code>SOA</code> | <code>SPF</code> | <code>SRV</code> | <code>TXT</code> Values for weighted, latency, geolocation, and failover resource record sets: <code>A</code> | <code>AAAA</code> | <code>CNAME</code> | <code>MX</code> | <code>PTR</code> | <code>SPF</code> | <code>SRV</code> | <code>TXT</code>. When creating a group of weighted, latency, geolocation, or failover resource record sets, specify the same value for all of the resource record sets in the group. <note>SPF records were formerly used to verify the identity of the sender of email messages. However, we no longer recommend that you create resource record sets for which the value of <code>Type</code> is <code>SPF</code>. RFC 7208, <i>Sender Policy Framework (SPF) for Authorizing Use of Domains in Email, Version 1</i>, has been updated to say, "...[I]ts existence and mechanism defined in [RFC4408] have led to some interoperability issues. Accordingly, its use is no longer appropriate for SPF version 1; implementations are not to use it." In RFC 7208, see section 14.1, <a href="http://tools.ietf.org/html/rfc7208#section-14.1">The SPF DNS Record Type</a>.</note> Values for alias resource record sets: <ul> <li><b>CloudFront distributions:</b> <code>A</code></li> <li><b>ELB load balancers:</b> <code>A</code> | <code>AAAA</code></li> <li><b>Amazon S3 buckets:</b> A</li> <li><b>Another resource record set in this hosted zone:</b> Specify the type of the resource record set for which you're creating the alias. Specify any value except <code>NS</code> or <code>SOA</code>.</li> </ul></param>
 public ResourceRecordSet(string name, RRType type)
 {
     _name = name;
     _type = type;
 }
Ejemplo n.º 6
0
 private RunnerResult(RRType t, StatementLabel lbl = null)
 {
     ResultType = t;
     JumpLabel  = lbl;
 }
        protected override async Task PersistAsync(string recordName, string recordType, string recordValue)
        {
            _logger.LogDebug("Starting creation or update of {RecordType} {RecordName} with value {RecordValue}", recordType, recordName, recordValue);

            var zone = await FindHostedZoneAsync(recordName);

            if (zone == null)
            {
                _logger.LogDebug("No zone was found");
                return;
            }

            if (recordType == TxtRecordType)
            {
                recordValue = NormalizeTxtValue(recordValue);
            }

            var existingRecordSets = await FindRecordSetsAsync(zone, recordName, recordType);

            var existingRecordSet = existingRecordSets.FirstOrDefault();

            var recordSet = existingRecordSet ?? new ResourceRecordSet
            {
                Name            = recordName,
                TTL             = 60,
                Type            = RRType.FindValue(recordType),
                ResourceRecords = new List <ResourceRecord>()
            };

            if (recordSet.ResourceRecords.Any(x => x.Value == recordValue))
            {
                _logger.LogDebug("Record {RecordType} {RecordName} with value {RecordValue} already exists", recordType, recordName, recordValue);
                return;
            }

            recordSet.ResourceRecords.Add(new ResourceRecord {
                Value = recordValue
            });

            var change1 = new Change
            {
                ResourceRecordSet = recordSet,
                Action            = ChangeAction.UPSERT
            };

            var changeBatch = new ChangeBatch
            {
                Changes = new List <Change> {
                    change1
                }
            };

            var recordsetRequest = new ChangeResourceRecordSetsRequest
            {
                HostedZoneId = zone.Id,
                ChangeBatch  = changeBatch
            };

            _logger.LogInformation("Creating or updating DNS record {RecordType} {RecordName}", recordType, recordName);

            var upsertResponse = await _route53Client.ChangeResourceRecordSetsAsync(
                new ChangeResourceRecordSetsRequest()
            {
                ChangeBatch  = changeBatch,
                HostedZoneId = zone.Id
            });

            var changeRequest = new GetChangeRequest
            {
                Id = upsertResponse.ChangeInfo.Id
            };

            while (await IsChangePendingAsync(changeRequest))
            {
                _logger.LogDebug("Creation/update of {RecordType} {RecordName} with value {RecordValue} is pending. Checking for status update in {StatusPollIntervalSeconds} seconds.", recordType, recordName, recordValue, StatusPollIntervalSeconds);
                Thread.Sleep(TimeSpan.FromSeconds(StatusPollIntervalSeconds));
            }
        }
Ejemplo n.º 8
0
 public static async Task <string> CreateResourceRecordSet(this ICakeContext context, string hostedZoneId, string name, RRType type, Route53Settings settings)
 {
     return(await context.CreateManager().CreateResourceRecordSet(hostedZoneId, name, type, EC2InstanceMetadata.PrivateIpAddress, 300, settings));
 }
Ejemplo n.º 9
0
 public static async Task <string> DeleteResourceRecordSet(this ICakeContext context, string hostedZoneId, string name, RRType type, string value, long ttl, Route53Settings settings)
 {
     return(await context.CreateManager().DeleteResourceRecordSet(hostedZoneId, name, type, value, ttl, settings));
 }
Ejemplo n.º 10
0
        public async Task HandleTermR53(Instance inst, Dictionary <string, string> tags)
        {
            _logger.LogInformation("Handling REMOVING R53 records");

            var r53Spec    = ResolveR53Spec(inst, tags);
            var r53Routing = ResolveR53RoutingSpec(inst, tags);

            var rrset = new ResourceRecordSet
            {
                Name            = r53Spec.Name,
                Type            = RRType.FindValue(r53Spec.Type),
                TTL             = r53Spec.TTL,
                ResourceRecords = new List <ResourceRecord>
                {
                    new ResourceRecord(r53Spec.Value),
                },
            };

            // Optional routing policy configuration
            r53Routing?.Apply(rrset);

            var listRequ = new ListResourceRecordSetsRequest
            {
                HostedZoneId          = r53Spec.Zone,
                StartRecordName       = r53Spec.Name,
                StartRecordType       = r53Spec.Type,
                StartRecordIdentifier = null,
            };

            var listResp = await _r53.ListResourceRecordSetsAsync(listRequ);

            var rr = listResp.ResourceRecordSets.FirstOrDefault();

            if (rr == null ||
                rr.Name != r53Spec.Name ||
                rr.Type != r53Spec.Type)
            {
                _logger.LogWarning("No existing resource records found; SKIPPING");
                _logger.LogInformation("First returned record for query:");
                _logger.LogInformation(JsonSerializer.Serialize(rr));
                return;
            }

            var changeRequ = new ChangeResourceRecordSetsRequest
            {
                HostedZoneId = r53Spec.Zone,
                ChangeBatch  = new ChangeBatch
                {
                    Changes = new List <Change>
                    {
                        new Change
                        {
                            Action            = ChangeAction.DELETE,
                            ResourceRecordSet = rrset,
                        }
                    }
                }
            };

            var changeResp = await _r53.ChangeResourceRecordSetsAsync(changeRequ);

            _logger.LogInformation("DELETE request completed, response:");
            _logger.LogInformation(JsonSerializer.Serialize(changeResp));
        }
Ejemplo n.º 11
0
        public async Task HandleInitR53(Instance inst, Dictionary <string, string> tags, string ec2State)
        {
            _logger.LogInformation("Handling CREATING R53 records");

            var r53Spec    = ResolveR53RecordSpec(inst, tags);
            var r53Routing = ResolveR53RoutingSpec(inst, tags);
            var r53Health  = await ResolveR53HealthCheckSpec(inst, tags);

            HealthCheck existingHealth = await FindExistingHealthCheck(r53Health);

            // We apply the Health Check if any, first because
            // the subsequent Route policy may depend on it
            string healthCheckId = null;

            if (r53Health != null)
            {
                if (existingHealth == null)
                {
                    var createRequ = new CreateHealthCheckRequest
                    {
                        CallerReference   = r53Health.RefName,
                        HealthCheckConfig = r53Health.Config,
                    };
                    var createResp = await _r53.CreateHealthCheckAsync(createRequ);

                    _logger.LogInformation("CREATE Health Check request completed, response:");
                    _logger.LogInformation(JsonSerializer.Serialize(createResp));
                    if (createResp.HealthCheck == null)
                    {
                        throw new Exception("failed to create Health Check");
                    }
                    healthCheckId = createResp.HealthCheck.Id;

                    var tagRequ = new ChangeTagsForResourceRequest
                    {
                        ResourceType = TagResourceType.Healthcheck,
                        ResourceId   = healthCheckId,
                        AddTags      = new List <Amazon.Route53.Model.Tag>
                        {
                            new Amazon.Route53.Model.Tag
                            {
                                Key   = "Name",
                                Value = r53Health.RefName,
                            },
                            new Amazon.Route53.Model.Tag
                            {
                                Key   = "vmbot:memo",
                                Value = $"Managed by VMBot {nameof(R53Trigger)}",
                            },
                        },
                    };
                    var tagResp = await _r53.ChangeTagsForResourceAsync(tagRequ);

                    _logger.LogInformation("CHANGED TAGS for Health Check:");
                    _logger.LogInformation(JsonSerializer.Serialize(tagResp));
                }
                else
                {
                    var updateRequ = new UpdateHealthCheckRequest
                    {
                        HealthCheckId      = existingHealth.Id,
                        HealthCheckVersion = existingHealth.HealthCheckVersion,
                    };
                    CopyOrReset(existingHealth.HealthCheckConfig, r53Health.Config, updateRequ);
                    _logger.LogInformation("Resolved Health Check delta:");
                    _logger.LogInformation(JsonSerializer.Serialize(updateRequ));
                    var updateResp = await _r53.UpdateHealthCheckAsync(updateRequ);

                    _logger.LogInformation("UPDATE Health Check request completed, response:");
                    _logger.LogInformation(JsonSerializer.Serialize(updateResp));
                    healthCheckId = updateResp.HealthCheck.Id;
                }
            }

            if (r53Spec != null)
            {
                var rrset = new ResourceRecordSet
                {
                    Name            = r53Spec.Name,
                    Type            = RRType.FindValue(r53Spec.Type),
                    TTL             = r53Spec.TTL,
                    ResourceRecords = new List <ResourceRecord>
                    {
                        new ResourceRecord(r53Spec.Value),
                    },
                };
                if (healthCheckId != null)
                {
                    rrset.HealthCheckId = healthCheckId;
                }

                // Optional routing policy configuration
                r53Routing?.Apply(rrset);

                var changeRequ = new ChangeResourceRecordSetsRequest
                {
                    HostedZoneId = r53Spec.Zone,
                    ChangeBatch  = new ChangeBatch
                    {
                        Changes = new List <Change>
                        {
                            new Change
                            {
                                Action            = ChangeAction.UPSERT,
                                ResourceRecordSet = rrset,
                            }
                        },
                        Comment = "change request applied by VMBot",
                    }
                };

                var changeResp = await _r53.ChangeResourceRecordSetsAsync(changeRequ);

                _logger.LogInformation("UPSERT Resource Record request completed, response:");
                _logger.LogInformation(JsonSerializer.Serialize(changeResp));
            }
        }
Ejemplo n.º 12
0
 RRTemplate()
 {
     this.RRType   = RRType.None;
     this.RRObject = null;
 }
Ejemplo n.º 13
0
 public RRTemplate(RRType requestType, object requestObject)
 {
     this.RRType   = requestType;
     this.RRObject = requestObject;
 }
Ejemplo n.º 14
0
 public RRSampleAttribute(RRType Type, String Description, AggregationFunction AggregationFunction)
 {
     this.Type                = Type;
     this.Description         = Description;
     this.AggregationFunction = AggregationFunction;
 }
Ejemplo n.º 15
0
 public RRSampleAttribute(RRType Type)
 {
     this.Type                = Type;
     this.Description         = String.Empty;
     this.AggregationFunction = AggregationFunction.AVERAGE;
 }
Ejemplo n.º 16
0
        /// <summary>
        /// Delete a DNS record for a hosted zone.
        /// </summary>
        /// <param name="hostedZoneId">The ID of the hosted zone that contains the resource record sets that you want to change</param>
        /// <param name="name">The name of the DNS record set.</param>
        /// <param name="type">The type of the DNS record set.</param>
        /// <param name="value">The value of the record set.</param>
        /// <param name="ttl">The time to live of the record set.</param>
        /// <param name="settings">The <see cref="Route53Settings"/> required to upload to Amazon S3.</param>
        /// <param name="cancellationToken">A cancellation token that can be used by other objects or threads to receive notice of cancellation.</param>
        public async Task <string> DeleteResourceRecordSet(string hostedZoneId, string name, RRType type, string value, long ttl, Route53Settings settings, CancellationToken cancellationToken = default(CancellationToken))
        {
            var recordSet = new ResourceRecordSet()
            {
                Name            = name,
                Type            = type,
                TTL             = ttl,
                ResourceRecords = new List <ResourceRecord> {
                    new ResourceRecord(value)
                }
            };

            var change1 = new Change()
            {
                ResourceRecordSet = recordSet,
                Action            = ChangeAction.DELETE
            };

            var changeBatch = new ChangeBatch()
            {
                Changes = new List <Change> {
                    change1
                }
            };

            var recordsetRequest = new ChangeResourceRecordSetsRequest()
            {
                HostedZoneId = hostedZoneId,
                ChangeBatch  = changeBatch
            };



            AmazonRoute53Client client = this.GetClient(settings);
            ChangeResourceRecordSetsResponse response = await client.ChangeResourceRecordSetsAsync(recordsetRequest);

            if (response.HttpStatusCode == HttpStatusCode.OK)
            {
                await this.WaitForChange(client, response.ChangeInfo.Id, 10000, 60);

                _Log.Verbose("Updated record set");
                return(response.ChangeInfo.Id);
            }
            else
            {
                _Log.Error("Could not change resource records");
                return("");
            }
        }
Ejemplo n.º 17
0
        private static void CreateDNSChangeAction(string domain, string hostName, string value, RRType type, ChangeAction action, int ttl)
        {
            string zoneId = FindHostedZoneID(domain);

            if (zoneId == null)
            {
                throw new Exception("Zone not found");
            }

            ResourceRecord resourceRecord = null;

            if (value != null)
            {
                resourceRecord = new ResourceRecord()
                {
                    Value = value
                };
            }

            var change = new Change
            {
                Action            = action,
                ResourceRecordSet = new ResourceRecordSet
                {
                    Name            = $"{hostName}.{domain}",
                    Type            = type,
                    TTL             = ttl,
                    ResourceRecords = resourceRecord != null ? new List <ResourceRecord>()
                    {
                        resourceRecord
                    } : null
                },
            };

            var changeBatch = new ChangeBatch();

            changeBatch.Changes.Add(change);
            var changeResourceRecordSetsRequest = new ChangeResourceRecordSetsRequest
            {
                ChangeBatch  = changeBatch,
                HostedZoneId = zoneId
            };

            var changeResourceResponse = c.ChangeResourceRecordSetsAsync(changeResourceRecordSetsRequest).Result;

            Console.WriteLine($"{changeResourceResponse.ChangeInfo.Status} {changeResourceResponse.ChangeInfo.Comment}");
        }
Ejemplo n.º 18
0
 /// <summary>
 ///
 /// </summary>
 /// <param name="r53h"></param>
 /// <param name="zoneId"></param>
 /// <param name="Name"></param>
 /// <param name="Value"></param>
 /// <param name="Type"></param>
 /// <param name="TTL"></param>
 /// <param name="failover"> PRIMARY or SECONDARY</param>
 /// <param name="healthCheckId">Required if failover is set to PRIMARY</param>
 /// <returns></returns>
 public static Task UpsertRecordAsync(this Route53Helper r53h,
                                      string zoneId, string Name, string Value, RRType Type, long TTL = 0,
                                      string failover      = null,
                                      string healthCheckId = null,
                                      string setIdentifier = null)
 => r53h.UpsertResourceRecordSetsAsync(zoneId, new ResourceRecordSet()
 {
     Name            = Name,
     TTL             = TTL,
     Type            = Type,
     ResourceRecords = new List <ResourceRecord>()
     {
         new ResourceRecord()
         {
             Value = Value
         }
     },
     Failover      = failover == null ? null : new ResourceRecordSetFailover(failover),
     SetIdentifier = setIdentifier,
     HealthCheckId = healthCheckId
 });
Ejemplo n.º 19
0
 /// <summary>
 /// Instantiates ResourceRecordSet with the parameterized properties
 /// </summary>
 /// <param name="name">The domain name of the current resource record set.</param>
 /// <param name="type">The type of the current resource record set.</param>
 public ResourceRecordSet(string name, RRType type)
 {
     _name = name;
     _type = type;
 }
Ejemplo n.º 20
0
        public async Task HandleTermR53(Instance inst, Dictionary <string, string> tags, string ec2State)
        {
            _logger.LogInformation("Handling REMOVING R53 records");

            var r53Record  = ResolveR53RecordSpec(inst, tags);
            var r53Routing = ResolveR53RoutingSpec(inst, tags);
            var r53Health  = await ResolveR53HealthCheckSpec(inst, tags);

            HealthCheck existingHealth = await FindExistingHealthCheck(r53Health);

            if (r53Record != null)
            {
                var rrset = new ResourceRecordSet
                {
                    Name            = r53Record.Name,
                    Type            = RRType.FindValue(r53Record.Type),
                    TTL             = r53Record.TTL,
                    ResourceRecords = new List <ResourceRecord>
                    {
                        new ResourceRecord(r53Record.Value),
                    },
                };
                // Optional routing policy configuration
                r53Routing?.Apply(rrset);

                if (existingHealth != null)
                {
                    rrset.HealthCheckId = existingHealth.Id;
                }

                var listRequ = new ListResourceRecordSetsRequest
                {
                    HostedZoneId          = r53Record.Zone,
                    StartRecordName       = r53Record.Name,
                    StartRecordType       = r53Record.Type,
                    StartRecordIdentifier = r53Routing?.SetIdentifier,
                };

                var listResp = await _r53.ListResourceRecordSetsAsync(listRequ);

                var rr = listResp.ResourceRecordSets.FirstOrDefault();

                if (rr == null ||
                    rr.Name != r53Record.Name ||
                    rr.Type != r53Record.Type ||
                    (r53Routing != null && !string.Equals(rr.SetIdentifier, r53Routing.SetIdentifier)))
                {
                    _logger.LogWarning("No existing resource records found; SKIPPING");
                    _logger.LogInformation("First returned record for query:");
                    _logger.LogInformation(JsonSerializer.Serialize(rr));
                }
                else
                {
                    var changeRequ = new ChangeResourceRecordSetsRequest
                    {
                        HostedZoneId = r53Record.Zone,
                        ChangeBatch  = new ChangeBatch
                        {
                            Changes = new List <Change>
                            {
                                new Change
                                {
                                    Action            = ChangeAction.DELETE,
                                    ResourceRecordSet = rrset,
                                }
                            }
                        }
                    };

                    var changeResp = await _r53.ChangeResourceRecordSetsAsync(changeRequ);

                    _logger.LogInformation("DELETE request completed, response:");
                    _logger.LogInformation(JsonSerializer.Serialize(changeResp));
                }
            }

            // We delete the Health Check if any, second because
            // the preceding Route policy may depend on it
            if (existingHealth != null &&
                (ec2State == EC2StateChangeStates.ShuttingDown ||
                 ec2State == EC2StateChangeStates.Terminated))
            {
                if (existingHealth != null)
                {
                    _logger.LogInformation($"Found existing Health Check record [{existingHealth.Id}]");
                    var deleteRequ = new DeleteHealthCheckRequest
                    {
                        HealthCheckId = existingHealth.Id,
                    };
                    var deleteResp = await _r53.DeleteHealthCheckAsync(deleteRequ);

                    _logger.LogInformation("DELETE Health Check request completed, response:");
                    _logger.LogInformation(JsonSerializer.Serialize(deleteResp));
                }
            }
        }