Example #1
0
        public void OnTimer(object sender, System.Timers.ElapsedEventArgs args)
        {
            eventLog.WriteEntry(LogMessaging.SERVICE_MONITORING, EventLogEntryType.Information);

            string date      = Utility.GetRoute53Date();
            string authValue = Utility.GetAWSR53_SHA1AuthorizationValue(Keys.ACCESS_KEY_ID, Keys.SECRET_KEY, date);

            string externalIp = Utility.GetExternalIp();

            ListHostedZonesResponse listHostedZonesResponse = API.GetListHostedZones(date, authValue);

            var hostedZone = listHostedZonesResponse.HostedZones.Where(x => x.Name == "pierrethelusma.com.").SingleOrDefault();

            ListResourceRecordSetsResponse listResourceRecordSetsResponse = API.GetListResourceRecordSets(date, authValue, hostedZone.Id);

            var resourceRecordSet = listResourceRecordSetsResponse.ResourceRecordSets.Where(x => x.Name == "home.pierrethelusma.com." && x.Type == "A").SingleOrDefault();

            ChangeResourceRecordSetsResponse changeResourceRecordSetsResponse = new ChangeResourceRecordSetsResponse();

            if (resourceRecordSet == null)
            {
                eventLog.WriteEntry(LogMessaging.SERVICE_CREATING_DNS_ENTRY, EventLogEntryType.Information);
                changeResourceRecordSetsResponse = API.PostChangeResourceRecordSets(date, authValue, externalIp, hostedZone.Id, "home.pierrethelusma.com.", "A", "600", "CREATE");
            }
            else
            {
                if (!resourceRecordSet.ResourceRecords.Any(x => x.Value == externalIp))
                {
                    eventLog.WriteEntry(LogMessaging.SERVICE_UPDATING_DNS_ENTRY, EventLogEntryType.Information);
                    changeResourceRecordSetsResponse = API.PostChangeResourceRecordSets(date, authValue, externalIp, hostedZone.Id, "home.pierrethelusma.com.", "A", "600", "UPSERT");
                }
            }
        }
Example #2
0
        public async Task <bool> UpdateIpAddressForSubdomain(string hostedZoneId, string fqdn, string newIpAddress)
        {
            using (AmazonRoute53Client route53Client = GetAmazonRoute53Client())
            {
                ListResourceRecordSetsResponse records = await route53Client.ListResourceRecordSetsAsync(new ListResourceRecordSetsRequest(hostedZoneId));

                // Look for an A record matching the FQDN that was passed in
                ResourceRecordSet matchingRecordSet = records?.ResourceRecordSets.FirstOrDefault(prop => prop.Name == fqdn && prop.Type == RRType.A);

                if (matchingRecordSet != null && matchingRecordSet.ResourceRecords.Any())
                {
                    if (matchingRecordSet.ResourceRecords.First().Value != newIpAddress)
                    {
                        matchingRecordSet.ResourceRecords.First().Value = newIpAddress;
                        ChangeBatch change = new ChangeBatch();
                        change.Changes.Add(new Change(ChangeAction.UPSERT, matchingRecordSet));

                        ChangeResourceRecordSetsResponse changeRequest = await route53Client.ChangeResourceRecordSetsAsync(new ChangeResourceRecordSetsRequest(hostedZoneId, change));

                        Log.Information("[Runtime = {StartTime}] Change request submitted to change subdomain {Subdomain} IP address to {IPAddress}.", Settings.StartTime, fqdn, newIpAddress);

                        return(changeRequest.HttpStatusCode == System.Net.HttpStatusCode.OK);
                    }
                    else
                    {
                        Log.Information("[Runtime = {StartTime}] Subdomain {Subdomain} found, but the IP address was already {IPAddress}.", Settings.StartTime, fqdn, newIpAddress);
                    }
                }

                return(false);
            }
        }
Example #3
0
        /// <summary>
        /// To retrieve a list of record sets for a particular hosted zone.
        /// </summary>
        /// <param name="hostedZoneId">The ID of the hosted zone whos record sets you want to list</param>
        /// <param name="settings">The <see cref="Route53Settings"/> required to connect to Route53.</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 <IList <ResourceRecordSet> > GetResourceRecordSets(string hostedZoneId, Route53Settings settings, CancellationToken cancellationToken = default(CancellationToken))
        {
            if (String.IsNullOrEmpty(hostedZoneId))
            {
                throw new ArgumentNullException("hostedZoneId");
            }



            ListResourceRecordSetsRequest request = new ListResourceRecordSetsRequest(hostedZoneId);

            AmazonRoute53Client            client   = this.GetClient(settings);
            ListResourceRecordSetsResponse response = await client.ListResourceRecordSetsAsync(request);

            if (response.HttpStatusCode == HttpStatusCode.OK)
            {
                _Log.Verbose("Listing record sets");
                return(response.ResourceRecordSets);
            }
            else
            {
                _Log.Error("Could not list record sets");
                return(null);
            }
        }
Example #4
0
        public IEnumerable <ResourceRecordSet> GetResourceRecordSets(HostedZone zone)
        {
            var request = new ListResourceRecordSetsRequest(zone.Id);
            ListResourceRecordSetsResponse response = _route53Client.ListResourceRecordSets(request);

            return(response.ResourceRecordSets);
        }
Example #5
0
        private static void UnmarshallResult(XmlUnmarshallerContext context, ListResourceRecordSetsResponse response)
        {
            int originalDepth = context.CurrentDepth;
            int targetDepth   = originalDepth + 1;

            if (context.IsStartOfDocument)
            {
                targetDepth += 1;
            }

            while (context.Read())
            {
                if (context.IsStartElement || context.IsAttribute)
                {
                    if (context.TestExpression("ResourceRecordSets/ResourceRecordSet", targetDepth))
                    {
                        var unmarshaller = ResourceRecordSetUnmarshaller.Instance;
                        response.ResourceRecordSets.Add(unmarshaller.Unmarshall(context));
                        continue;
                    }
                    if (context.TestExpression("IsTruncated", targetDepth))
                    {
                        var unmarshaller = BoolUnmarshaller.Instance;
                        response.IsTruncated = unmarshaller.Unmarshall(context);
                        continue;
                    }
                    if (context.TestExpression("NextRecordName", targetDepth))
                    {
                        var unmarshaller = StringUnmarshaller.Instance;
                        response.NextRecordName = unmarshaller.Unmarshall(context);
                        continue;
                    }
                    if (context.TestExpression("NextRecordType", targetDepth))
                    {
                        var unmarshaller = StringUnmarshaller.Instance;
                        response.NextRecordType = unmarshaller.Unmarshall(context);
                        continue;
                    }
                    if (context.TestExpression("NextRecordIdentifier", targetDepth))
                    {
                        var unmarshaller = StringUnmarshaller.Instance;
                        response.NextRecordIdentifier = unmarshaller.Unmarshall(context);
                        continue;
                    }
                    if (context.TestExpression("MaxItems", targetDepth))
                    {
                        var unmarshaller = StringUnmarshaller.Instance;
                        response.MaxItems = unmarshaller.Unmarshall(context);
                        continue;
                    }
                }
                else if (context.IsEndElement && context.CurrentDepth < originalDepth)
                {
                    return;
                }
            }

            return;
        }
Example #6
0
        /// <summary>
        /// Unmarshaller the response from the service to the response class.
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        public override AmazonWebServiceResponse Unmarshall(XmlUnmarshallerContext context)
        {
            ListResourceRecordSetsResponse response = new ListResourceRecordSetsResponse();

            UnmarshallResult(context, response);

            return(response);
        }
Example #7
0
        internal void UpdateInfo()
        {
            ListResourceRecordSetsResponse resp = myController.route53.ListResourceRecordSets(new ListResourceRecordSetsRequest()
                                                                                              .WithHostedZoneId(this.ResponseData.Id));

            //Check if there is a change
            bool Change = (currentRecordSet.Count != resp.ListResourceRecordSetsResult.ResourceRecordSets.Count);

            for (int i = 0; (!Change) && i < currentRecordSet.Count && i < resp.ListResourceRecordSetsResult.ResourceRecordSets.Count; i++)
            {
                Change =
                    currentRecordSet[i].Name != resp.ListResourceRecordSetsResult.ResourceRecordSets[i].Name ||
                    currentRecordSet[i].SetIdentifier != resp.ListResourceRecordSetsResult.ResourceRecordSets[i].SetIdentifier ||
                    currentRecordSet[i].TTL != resp.ListResourceRecordSetsResult.ResourceRecordSets[i].TTL ||
                    currentRecordSet[i].Type != resp.ListResourceRecordSetsResult.ResourceRecordSets[i].Type ||
                    currentRecordSet[i].Weight != resp.ListResourceRecordSetsResult.ResourceRecordSets[i].Weight ||
                    currentRecordSet[i].ResourceRecords.Count != resp.ListResourceRecordSetsResult.ResourceRecordSets[i].ResourceRecords.Count
                ;

                if (resp.ListResourceRecordSetsResult.ResourceRecordSets[i].AliasTarget == null)
                {
                    Change |= currentRecordSet[i].AliasTarget != null;
                }
                else
                {
                    Change |= currentRecordSet[i].AliasTarget == null;
                    Change |= currentRecordSet[i].AliasTarget.DNSName != resp.ListResourceRecordSetsResult.ResourceRecordSets[i].AliasTarget.DNSName;
                    Change |= currentRecordSet[i].AliasTarget.HostedZoneId != resp.ListResourceRecordSetsResult.ResourceRecordSets[i].AliasTarget.HostedZoneId;
                }

                if (Change)
                {
                    break;
                }

                foreach (var rr in currentRecordSet[i].ResourceRecords)
                {
                    bool found = false;
                    foreach (var r in resp.ListResourceRecordSetsResult.ResourceRecordSets[i].ResourceRecords)
                    {
                        if (rr.Value == r.Value)
                        {
                            found = true;
                            break;
                        }
                    }
                    Change |= !found;
                }
            }


            if (Change)
            {
                RecordsAvailable = true;
                currentRecordSet = resp.ListResourceRecordSetsResult.ResourceRecordSets;
                TriggerStatusChanged();
            }
        }
Example #8
0
        private async Task <ListResourceRecordSetsResponse> getAwsResourceRecordsAsync(string hostedZoneId)
        {
            logger.LogInformation($"Getting List of Resource Records for hosted zone ID { hostedZoneId }...");

            ListResourceRecordSetsRequest request = new ListResourceRecordSetsRequest
            {
                HostedZoneId = hostedZoneId
            };

            ListResourceRecordSetsResponse records = await TheRoute53Client().ListResourceRecordSetsAsync(request);

            return(records);
        }
Example #9
0
        public override AmazonWebServiceResponse Unmarshall(XmlUnmarshallerContext context)
        {
            ListResourceRecordSetsResponse response = new ListResourceRecordSetsResponse();

            while (context.Read())
            {
                if (context.IsStartElement)
                {
                    response.ListResourceRecordSetsResult = ListResourceRecordSetsResultUnmarshaller.GetInstance().Unmarshall(context);
                    break;
                }
            }

            return(response);
        }
Example #10
0
        public static void ListHostNames()
        {
            if (!quiet)
            {
                Console.WriteLine($"Domain = {domain}");
            }
            string zoneId = FindHostedZoneID(domain);

            if (zoneId == null)
            {
                throw new Exception("Zone not found");
            }
            bool   more  = true;
            bool   first = true;
            string start = "";

            while (more)
            {
                ListResourceRecordSetsRequest r = new ListResourceRecordSetsRequest(zoneId);
                r.MaxItems = "150";
                if (!first)
                {
                    r.StartRecordName = start;
                }
                first = false;
                ListResourceRecordSetsResponse res = c.ListResourceRecordSetsAsync(r).Result;
                foreach (var s in res.ResourceRecordSets)
                {
                    string firstVal = (s.ResourceRecords != null && s.ResourceRecords.Count > 0) ? s.ResourceRecords[0].Value : "";
                    Console.WriteLine($"{s.Name.PadRight(60)}" + (quiet ? "" : $"\t{s.Type}\t{firstVal}"));

                    /*
                     * if (!quiet)
                     * {
                     *      foreach(var rr in s.ResourceRecords)
                     *      {
                     *              Console.WriteLine($"\t{rr.Value}");
                     *      }
                     * }
                     */
                }
                more = res.IsTruncated;
                if (more)
                {
                    start = res.NextRecordName;
                }
            }
        }
Example #11
0
        private async static Task <bool> UpdateIpAddressForSubdomain(string hostedZoneId, string fqdn, string newIpAddress)
        {
            AmazonRoute53Config config = new AmazonRoute53Config()
            {
                RegionEndpoint = RegionEndpoint.GetBySystemName(Settings.AWSRegion) // TODO: inject
            };

            using (AmazonRoute53Client route53Client = new AmazonRoute53Client(Settings.AWSAccessKeyId, Settings.AWSAccessKeySecret, config))
            {
                ListResourceRecordSetsResponse records = await route53Client.ListResourceRecordSetsAsync(new ListResourceRecordSetsRequest(hostedZoneId));

                ResourceRecordSet matchingRecordSet = records?.ResourceRecordSets.FirstOrDefault(prop => prop.Name == fqdn && prop.Type == RRType.A);

                if (matchingRecordSet != null && matchingRecordSet.ResourceRecords.FirstOrDefault() != null)
                {
                    if (matchingRecordSet.ResourceRecords.FirstOrDefault().Value != newIpAddress)
                    {
                        matchingRecordSet.ResourceRecords.FirstOrDefault().Value = newIpAddress;
                        ChangeBatch change = new ChangeBatch();
                        change.Changes.Add(new Change(ChangeAction.UPSERT, matchingRecordSet));

                        ChangeResourceRecordSetsResponse changeRequest = await route53Client.ChangeResourceRecordSetsAsync(new ChangeResourceRecordSetsRequest(hostedZoneId, change));

                        Log.Information("[Runtime = {StartTime}] Change request submitted to change subdomain {Subdomain} IP address to {IPAddress}.", startTime, fqdn, newIpAddress);

                        return(changeRequest.HttpStatusCode == System.Net.HttpStatusCode.OK);
                    }
                    else
                    {
                        Log.Information("[Runtime = {StartTime}] Subdomain {Subdomain} found, but the IP address was already {IPAddress}.", startTime, fqdn, newIpAddress);
                    }
                }
                else
                {
                    // New subdomain
                    Log.Information("[Runtime = {StartTime}] Subdomain {Subdomain} record not found.", startTime, fqdn);
                }

                return(false);
            }
        }
Example #12
0
        public override Task <ListResourceRecordSetsResponse> ListResourceRecordSetsAsync(ListResourceRecordSetsRequest request, CancellationToken cancellationToken = default(CancellationToken))
        {
            string ipAddress = string.Empty;

            if (request.HostedZoneId == TestConstants.FakeHostedZoneId)
            {
                if (request.StartRecordName == TestConstants.FakePublicDNSName)
                {
                    ipAddress = TestConstants.FakePublicIpAddress;
                    ListResourceRecordSetsPublicCalled = true;
                }
                else if (request.StartRecordName == TestConstants.FakePrivateDNSName)
                {
                    ipAddress = TestConstants.FakePrivateIpAddress;
                    ListResourceRecordSetsPrivateCalled = true;
                }
                TimesListResourceRecordSetsCalled++;
            }
            var fake = new ListResourceRecordSetsResponse
            {
                IsTruncated        = false,
                ResourceRecordSets = new List <ResourceRecordSet>
                {
                    new ResourceRecordSet
                    {
                        Type            = RRType.A,
                        Name            = request.StartRecordName,
                        ResourceRecords = new List <ResourceRecord>
                        {
                            new ResourceRecord {
                                Value = ipAddress
                            }
                        }
                    }
                }
            };

            return(Task.Run(() => fake));
        }
Example #13
0
        public async Task <ActionResult> CreateRecord(DnsRecord request)
        {
            // https://docs.aws.amazon.com/sdk-for-net/v2/developer-guide/route53-apis-intro.html
            // find zone
            var zone = await ResolveMatchingZone(request);

            if (zone != null)
            {
                // get existing record set for current TXT records with this name
                ListResourceRecordSetsResponse response = await _route53Client.ListResourceRecordSetsAsync(
                    new ListResourceRecordSetsRequest
                {
                    StartRecordName = request.RecordName,
                    StartRecordType = "TXT",
                    MaxItems        = "1",
                    HostedZoneId    = zone.Id
                }
                    );

                var targetRecordSet = response.ResourceRecordSets.FirstOrDefault(r => (r.Name == request.RecordName || r.Name == request.RecordName + ".") && r.Type.Value == "TXT");

                if (targetRecordSet != null)
                {
                    targetRecordSet.ResourceRecords.Add(
                        new ResourceRecord {
                        Value = "\"" + request.RecordValue + "\""
                    }
                        );
                }
                else
                {
                    targetRecordSet = new ResourceRecordSet()
                    {
                        Name            = request.RecordName,
                        TTL             = 5,
                        Type            = RRType.TXT,
                        ResourceRecords = new List <ResourceRecord>
                        {
                            new ResourceRecord {
                                Value = "\"" + request.RecordValue + "\""
                            }
                        }
                    };
                }

                try
                {
                    // requests for *.domain.com + domain.com use the same TXT record name, so we
                    // need to allow multiple entires rather than doing Upsert
                    var result = await ApplyDnsChange(zone, targetRecordSet, ChangeAction.UPSERT);

                    return(new ActionResult {
                        IsSuccess = true, Message = $"Dns Record Created/Updated: {request.RecordName}"
                    });
                }
                catch (AmazonRoute53Exception exp)
                {
                    return(new ActionResult {
                        IsSuccess = false, Message = $"Dns Record Create/Update: {request.RecordName} - {exp.Message}"
                    });
                }
            }
            else
            {
                return(new ActionResult {
                    IsSuccess = false, Message = "DNS Zone match could not be determined."
                });
            }
        }
        public List <MultiClientEntry> GetClients()
        {
            //Output
            Output($"MCS: Starting Get Clients");

            //1) Get the current data-text from R53
            //New the Route 53 Client
            AmazonRoute53Client r53Client =
                new AmazonRoute53Client(
                    Config.BaseSettings.AWSAccessKeyID,
                    Config.BaseSettings.AWSSecretAccessKey,
                    new AmazonRoute53Config
            {
                RegionEndpoint = RegionEndpoint
            });

            //Define the request
            ListResourceRecordSetsRequest listResourceRecordSetsRequest =
                new ListResourceRecordSetsRequest
            {
                HostedZoneId    = Config.MultiClientSettings.Route53.R53ZoneId,
                MaxItems        = 1.ToString(),
                StartRecordName = Config.MultiClientSettings.Route53.Name
            };

            //Get the response
            ListResourceRecordSetsResponse listResourceRecordSetsResponse =
                r53Client
                .ListResourceRecordSetsAsync(listResourceRecordSetsRequest)
                .GetAwaiter()
                .GetResult();

            //Get the Data Text (if any)
            //listResourceRecordSetsResponse.ResourceRecordSets[0].ResourceRecords[0].Value
            string dataText =
                listResourceRecordSetsResponse
                .ResourceRecordSets?
                .Where(rrsi => string.Equals(rrsi.Name, Config.MultiClientSettings.Route53.Name, StringComparison.InvariantCultureIgnoreCase))
                .FirstOrDefault()?
                .ResourceRecords?
                .FirstOrDefault()?
                .Value?
                .Trim('"');

            if (string.IsNullOrWhiteSpace(dataText) || string.IsNullOrWhiteSpace(dataText.Trim('"')))
            {
                //Since there somehow were no existing data in R53
                //We just return an empty list

                //Output
                Output($"MCS: Found no existing record");

                //return
                return(new List <MultiClientEntry>());
            }

            //2) Decode the data string to a JSON string (decompress, decrypt)
            //Decrypt and Decompress
            dataText = dataText.DecryptThenDecompress(Config.MultiClientSettings.Route53.EncryptionPassword);

            //3) Turn the Data Text into List
            //De-serialize the JSON to the return value
            List <MultiClientEntry> multiClientEntries =
                JsonConvert.DeserializeObject <List <MultiClientEntry> >(dataText);

            //Output
            Output($"MCS: Completed Get Clients. Got {multiClientEntries.Count}");

            //return
            return(multiClientEntries);
        }
        /// <summary>
        /// This method does all of the work to update the DNS records with our current WAN IP. This is separated from the main
        /// method to make it easier in the future to perform batch updates (if needed).
        /// </summary>
        /// <param name="id">The AWS Access key ID</param>
        /// <param name="secret">The AWS Access key secret</param>
        /// <param name="domain">The domain name we are going to be updating records for (e.g. shawnlehner.com)</param>
        /// <param name="subdomain">The subdomain we would like to update (e.g. "house" if we were updating house.shawnlehner.com)</param>
        private static async Task PerformDnsUpdate(string id, string secret, string domain, string subdomain)
        {
            #region Lookup Current WAN IP

            // We use ipify.org to quickly/easily get our external WAN IP address which we can later use
            // for updating our DNS records.

            string ip = null;

            HttpWebRequest request = (HttpWebRequest)WebRequest.Create("https://api.ipify.org");
            using (Stream s = (await request.GetResponseAsync()).GetResponseStream())
                using (StreamReader r = new StreamReader(s))
                {
                    ip = r.ReadToEnd();
                }

            #endregion

            // Combine our domain and subdomain to get the full record name
            string recordName = subdomain.Trim('.') + "." + domain.Trim('.');

            // Create our AWS API client for sending requests to the Route53 API
            AmazonRoute53Client client = new AmazonRoute53Client(id, secret, RegionEndpoint.USEast1);

            #region Lookup current state of the domain on Route53

            // Lookup the zone for our domain
            HostedZone zone = (await client.ListHostedZonesByNameAsync(new ListHostedZonesByNameRequest
            {
                DNSName = domain
            })).HostedZones.First();

            // Lookup our current records to see if we need to make an update
            ListResourceRecordSetsResponse recordSet = await client.ListResourceRecordSetsAsync(new ListResourceRecordSetsRequest
            {
                HostedZoneId    = zone.Id,
                MaxItems        = "1",
                StartRecordName = recordName
            });

            #endregion

            // Check to see if our IP is already up to date. No sense making a change if we don't need to.
            if (recordSet.ResourceRecordSets.Count > 0 &&
                recordSet.ResourceRecordSets[0].Name.Trim('.') == recordName &&
                recordSet.ResourceRecordSets[0].ResourceRecords[0].Value == ip)
            {
                return;
            }

            #region Request DNS record update with new IP

            // Our IP address is not up-to-date so we need to make a change request. We use UPSERT action which
            // will work whether or not a record already exists.
            ChangeResourceRecordSetsRequest changeRequest = new ChangeResourceRecordSetsRequest
            {
                HostedZoneId = zone.Id,
                ChangeBatch  = new ChangeBatch
                {
                    Changes = new List <Change>
                    {
                        new Change
                        {
                            ResourceRecordSet = new ResourceRecordSet
                            {
                                Name            = recordName,
                                TTL             = 60,
                                Type            = RRType.A,
                                ResourceRecords = new List <ResourceRecord> {
                                    new ResourceRecord {
                                        Value = ip
                                    }
                                }
                            },
                            Action = ChangeAction.UPSERT
                        }
                    }
                }
            };

            // Send our change request to the API
            ChangeResourceRecordSetsResponse response =
                await client.ChangeResourceRecordSetsAsync(changeRequest);

            // Check our response code to verify everything worked
            if (response.HttpStatusCode != HttpStatusCode.OK)
            {
                throw new Exception("API request to update DNS record has failed.");
            }

            #endregion
        }
Example #16
0
        public bool PrepareChallengeForValidation(string dnsKeyName, string dnsKeyValue)
        {
            try
            {
                route53Client = new AmazonRoute53Client();
                HostedZone zone = null;
                if (zoneId != null)
                {
                    GetHostedZoneResponse zoneResp = route53Client.GetHostedZone(new Amazon.Route53.Model.GetHostedZoneRequest {
                        Id = zoneId
                    });
                    zone = zoneResp.HostedZone;
                }
                else
                {
                    ListHostedZonesResponse zones = route53Client.ListHostedZones();
                    string recordToZone           = dnsKeyName;
                    while (recordToZone.IndexOf('.') > 0)
                    {
                        recordToZone = recordToZone.Substring(recordToZone.IndexOf('.') + 1);
                        zone         = zones.HostedZones.Where(z => z.Name.Contains(recordToZone)).FirstOrDefault();
                        if (zone != null)
                        {
                            break;
                        }
                    }
                }
                if (zone == null)
                {
                    logger.Error("Could not find DNS zone");
                    return(false);
                }

                ListResourceRecordSetsResponse txtRecordsResponse = route53Client.ListResourceRecordSets(new ListResourceRecordSetsRequest
                {
                    StartRecordName = dnsKeyName,
                    StartRecordType = "TXT",
                    MaxItems        = "1",
                    HostedZoneId    = zone.Id
                });
                ResourceRecordSet txtRecord = txtRecordsResponse.ResourceRecordSets.FirstOrDefault(r => (r.Name == dnsKeyName || r.Name == dnsKeyName + ".") && r.Type.Value == "TXT");

                if (txtRecord != null)
                {
                    ApplyDnsChange(zone, txtRecord, ChangeAction.DELETE);
                }

                txtRecord = new ResourceRecordSet()
                {
                    Name            = dnsKeyName,
                    TTL             = 5,
                    Type            = RRType.TXT,
                    ResourceRecords = new List <ResourceRecord>
                    {
                        new ResourceRecord {
                            Value = "\"" + dnsKeyValue + "\""
                        }
                    }
                };

                ApplyDnsChange(zone, txtRecord, ChangeAction.UPSERT);
            }
            catch (AmazonRoute53Exception exp)
            {
                logger.Error($"Could not update AWS Route53 record: ", exp);
                return(false);
            }
            return(true);
        }