private static ChangeResourceRecordSetsResponse SetARecord(AmazonRoute53Client route53Client, string publicIp, AppSettings appConfig) { var ret = route53Client.ChangeResourceRecordSetsAsync(new ChangeResourceRecordSetsRequest() { HostedZoneId = appConfig.HostedZoneId, ChangeBatch = new ChangeBatch() { Changes = new List <Change>() { new Change() { Action = ChangeAction.UPSERT, ResourceRecordSet = new ResourceRecordSet() { Name = appConfig.RecordName, Type = appConfig.Type, TTL = appConfig.TTL, ResourceRecords = new List <ResourceRecord>() { new ResourceRecord() { Value = publicIp } } } } } } }).Result; return(ret); }
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); } }
/// <summary> /// Create or change 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> CreateResourceRecordSet(string hostedZoneId, string name, RRType type, string value, long ttl, Route53Settings settings, CancellationToken cancellationToken = default(CancellationToken)) { var recordSet = new ResourceRecordSet() { Name = name, TTL = ttl, Type = type, ResourceRecords = new List <ResourceRecord> { new ResourceRecord { Value = value } } }; var change1 = new Change() { ResourceRecordSet = recordSet, Action = ChangeAction.UPSERT }; 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(""); } }
public async Task CreateTxtRecordAsync(DnsZone zone, string relativeRecordName, IEnumerable <string> values) { var recordName = $"{relativeRecordName}.{zone.Name}."; var change = new Change { Action = ChangeAction.CREATE, ResourceRecordSet = new ResourceRecordSet { Name = recordName, Type = RRType.TXT, TTL = 60, ResourceRecords = values.Select(x => new ResourceRecord($"\"{x}\"")).ToList() } }; var request = new ChangeResourceRecordSetsRequest(zone.Id, new ChangeBatch(new List <Change> { change })); await _amazonRoute53Client.ChangeResourceRecordSetsAsync(request); }
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}"); }
private async Task <bool> ApplyDnsChange(HostedZone zone, ResourceRecordSet recordSet, ChangeAction action) { // prepare change var changeDetails = new Change() { ResourceRecordSet = recordSet, Action = action }; var changeBatch = new ChangeBatch() { Changes = new List <Change> { changeDetails } }; // Update the zone's resource record sets var recordsetRequest = new ChangeResourceRecordSetsRequest() { HostedZoneId = zone.Id, ChangeBatch = changeBatch }; _log?.Debug($"Route53 :: ApplyDnsChange : ChangeResourceRecordSetsAsync: {JsonConvert.SerializeObject(recordsetRequest.ChangeBatch)} "); var recordsetResponse = await _route53Client.ChangeResourceRecordSetsAsync(recordsetRequest); _log?.Debug($"Route53 :: ApplyDnsChange : ChangeResourceRecordSetsAsync Response: {JsonConvert.SerializeObject(recordsetResponse)} "); // Monitor the change status var changeRequest = new GetChangeRequest() { Id = recordsetResponse.ChangeInfo.Id }; while (ChangeStatus.PENDING == (await _route53Client.GetChangeAsync(changeRequest)).ChangeInfo.Status) { System.Diagnostics.Debug.WriteLine("DNS change is pending."); await Task.Delay(1500); } _log?.Information("DNS change completed."); return(true); }
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); } }
public async Task <ChangeResourceRecordSetsResponse> ChangeResourceRecordSetsAsync(string zoneId, ResourceRecordSet resourceRecordSet, Change change) { var sw = Stopwatch.StartNew(); int _timeout = 5 * 60 * 10000; PriorRequestNotCompleteException exception = null; do { try { return(await _stopWatch.TimeLock(_rateLimit, _locker, async() => { return await _client.ChangeResourceRecordSetsAsync( new ChangeResourceRecordSetsRequest() { ChangeBatch = new ChangeBatch() { Changes = new List <Change>() { change } }, HostedZoneId = zoneId }).EnsureSuccessAsync(); })); } catch (PriorRequestNotCompleteException ex) //requires client reconnection { exception = ex; await Task.Delay(5000); _locker.Lock(() => { Initialize(); }); } } while (sw.ElapsedMilliseconds < _timeout); throw exception; }
public async Task <string> UpdateHostZoneRecordSetValue(string action, List <KeyValuePair <string, string> > recordsetChanges) { List <Change> changes = new List <Change>(); foreach (var recordsetChange in recordsetChanges) { changes.Add( new Change( new ChangeAction(action), //"UPSERT" new ResourceRecordSet() { Name = recordsetChange.Key, TTL = 900, Type = RRType.CNAME, ResourceRecords = new List <ResourceRecord>() { new ResourceRecord() { Value = recordsetChange.Value } } } ) ); } ; ChangeBatch batch = new ChangeBatch(changes); var request = new ChangeResourceRecordSetsRequest() { HostedZoneId = await GetHostZoneId(), ChangeBatch = batch }; var response = await client.ChangeResourceRecordSetsAsync(request); return(response.HttpStatusCode.ToString()); }
public void SaveClients() { //Output Output($"MCS: Starting Save Clients"); string dataText = ""; //Validate if (Clients == null || !Clients.Any()) { //Output Output($"MCS: Had no Clients."); //Will update with an empty string - no clients } else { //Output Output($"MCS: Found {Clients.Count} Clients."); //1) Serialize to JSON text dataText = JsonConvert.SerializeObject(Clients); //2) Prepare the string (encrypt, compress) //Encrypt and Compress dataText = dataText.CompressThenEncrypt(Config.MultiClientSettings.Route53.EncryptionPassword); } //Add quotation marks dataText = "\"" + dataText + "\""; //3) Update to R53 //New the Route 53 Client AmazonRoute53Client r53Client = new AmazonRoute53Client( Config.BaseSettings.AWSAccessKeyID, Config.BaseSettings.AWSSecretAccessKey, new AmazonRoute53Config { RegionEndpoint = RegionEndpoint }); //New the Change Record to push Change change = new Change { Action = ChangeAction.UPSERT, //Insert or Update ResourceRecordSet = new ResourceRecordSet { Name = Config.MultiClientSettings.Route53.Name, TTL = 300, Type = RRType.TXT, ResourceRecords = new List <ResourceRecord> { new ResourceRecord(dataText) } } }; //New the Change Request ChangeResourceRecordSetsRequest recordSetsRequest = new ChangeResourceRecordSetsRequest { HostedZoneId = Config.MultiClientSettings.Route53.R53ZoneId, ChangeBatch = new ChangeBatch(new List <Change> { change }) }; //Submitting the Change Request to the API and receiving back the ID ChangeResourceRecordSetsResponse recordSetResponse = r53Client .ChangeResourceRecordSetsAsync(recordSetsRequest) .GetAwaiter() .GetResult(); //The ID of the response string changeId = recordSetResponse.ChangeInfo.Id; //Output Output($"MCS-R53: Change ID \"{changeId}\": ChangeRequest was submitted."); //Enquire with R53 the status of the change (R53 updates can be VERY slow business) GetChangeRequest changeRequest = new GetChangeRequest { Id = changeId }; while (r53Client.GetChangeAsync(changeRequest).GetAwaiter().GetResult().ChangeInfo.Status == ChangeStatus.PENDING) { //Output Output($"MCS-R53: Change ID \"{changeId}\": Change is still pending. (Can take a while.)"); //Wait Thread.Sleep(10 * 1000); //Wait ten seconds } //Output DONE Output($"MCS-R53: Change ID \"{changeId}\": Change IN SYNC. Done."); Output($"MCS: Completed Save of {Clients.Count} Clients"); }
/// <summary> /// Run a single item /// </summary> /// <param name="item">The item to run</param> public void RunItem(Route53Host item) { //Output Output($"R53: Starting item: {item}, new IP address: {IpState.NewIP}"); //New the Route 53 Client AmazonRoute53Client r53Client = new AmazonRoute53Client( Config.BaseSettings.AWSAccessKeyID, Config.BaseSettings.AWSSecretAccessKey, new AmazonRoute53Config { RegionEndpoint = RegionEndpoint }); //New the Change Record to push Change change = new Change { Action = ChangeAction.UPSERT, //Insert or Update ResourceRecordSet = new ResourceRecordSet { Name = item.Name, TTL = item.TTL, Type = RRType.A, ResourceRecords = new List <ResourceRecord> { new ResourceRecord(IpState.NewIP) } } }; //New the Change Request ChangeResourceRecordSetsRequest recordSetsRequest = new ChangeResourceRecordSetsRequest { HostedZoneId = item.ZoneId, ChangeBatch = new ChangeBatch(new List <Change> { change }) }; //Submitting the Change Request to the API and receiving back the ID ChangeResourceRecordSetsResponse recordSetResponse = r53Client.ChangeResourceRecordSetsAsync(recordSetsRequest).GetAwaiter().GetResult(); //The ID of the response string changeId = recordSetResponse.ChangeInfo.Id; //Output Output($"R53: Change ID \"{changeId}\": ChangeRequest was submitted."); //Enquire with R53 the status of the change (R53 updates can be VERY slow business) GetChangeRequest changeRequest = new GetChangeRequest { Id = changeId }; while (r53Client.GetChangeAsync(changeRequest).GetAwaiter().GetResult().ChangeInfo.Status == ChangeStatus.PENDING) { //Output Output($"R53: Change ID \"{changeId}\": Change is still pending. (Can take a while.)"); //Wait Thread.Sleep(10 * 1000); //Wait ten seconds } //Output DONE Output($"R53: Change ID \"{changeId}\": Change IN SYNC. Done."); }
/// <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 }
/// <summary> /// Used to modify recordset with value from api gateway code deploy in another account. /// </summary> /// <param name="jobEvent">CodePipeline event.</param> /// <param name="context">Lambda Context</param> /// <returns>Job success of the Lambda</returns> public async Task <AmazonWebServiceResponse> UpdateApiGatewayAliasRecordSet(CodePipelineJobEvent jobEvent, ILambdaContext context) { var jobId = jobEvent.CodePipelineJob.Id; context.Logger.LogLine($"JobId: {jobId}"); using (var codePipelineClient = new AmazonCodePipelineClient()) using (var s3Client = new AmazonS3Client()) using (var dnsClient = new AmazonRoute53Client()) { // Prep the user parameters var userParameters = jobEvent.CodePipelineJob.Data.ActionConfiguration.Configuration["UserParameters"]; context.Logger.LogLine("UserParameters:"); context.Logger.LogLine(userParameters); var paramDict = JsonConvert.DeserializeObject <Dictionary <string, string> >(userParameters); try { DnsSettings dnsSettings = null; // Get the first (only) input artifact var artifact = jobEvent.CodePipelineJob.Data.InputArtifacts.First(); var artifactBucketName = artifact.Location.S3Location.BucketName; var artifactBucketKey = artifact.Location.S3Location.ObjectKey; // Unzip and get json using (var getResponse = await s3Client.GetObjectAsync(artifactBucketName, artifactBucketKey)) { var memoryStream = new MemoryStream(); using (var objectStream = getResponse.ResponseStream) { objectStream.CopyTo(memoryStream); } var searchFile = paramDict["DnsJson"]; context.Logger.LogLine("Unziping artifact"); context.Logger.LogLine($"Searching for {searchFile}"); using (var archive = new ZipArchive(memoryStream, ZipArchiveMode.Read)) { foreach (var entry in archive.Entries) { var fileName = entry.Name; context.Logger.LogLine("Checking File: " + entry.Name); if (searchFile == fileName) { using (var fileStream = entry.Open()) using (var streamReader = new StreamReader(fileStream)) { var dnsJson = await streamReader.ReadToEndAsync(); dnsSettings = JsonConvert.DeserializeObject <DnsSettings>(dnsJson); } break; } } } } // Get the hosted zones in the tools region context.Logger.LogLine($"Searching for {dnsSettings.HostedZoneLookupDomainName}"); var hostedZones = await dnsClient.ListHostedZonesAsync(); var myHostedZone = hostedZones.HostedZones.FirstOrDefault(z => z.Name == dnsSettings.HostedZoneLookupDomainName); if (myHostedZone == null) { var noZoneMessage = $"Hosted Zone {dnsSettings.HostedZoneLookupDomainName} could not be found"; context.Logger.LogLine(noZoneMessage); var failureDetails = new FailureDetails { Message = noZoneMessage, Type = FailureType.ConfigurationError }; return(await codePipelineClient.PutJobFailureResultAsync(jobId, failureDetails)); } // The record set we need to create var recordSet = new ResourceRecordSet { Name = dnsSettings.WebApiDnsDomain, Type = RRType.A, ResourceRecords = new List <ResourceRecord> { new ResourceRecord { Value = $"ALIAS {dnsSettings.RegionalDomainName}" } } }; var rsChange = new Change { Action = ChangeAction.UPSERT, ResourceRecordSet = recordSet }; var rsChangeBatch = new ChangeBatch { Changes = new List <Change> { rsChange } }; // Create/Update the recordset var changeDnsRequest = new ChangeResourceRecordSetsRequest { ChangeBatch = rsChangeBatch, HostedZoneId = myHostedZone.Id }; var changeResponse = await dnsClient.ChangeResourceRecordSetsAsync(changeDnsRequest); // Log and send the success response context.Logger.LogLine($"Request Id {changeResponse.ChangeInfo.Id} submitted"); context.Logger.LogLine($"{dnsSettings.WebApiDnsDomain} => A ALIAS {dnsSettings.RegionalDomainName}"); var successResultRequest = new PutJobSuccessResultRequest { JobId = jobId }; return(await codePipelineClient.PutJobSuccessResultAsync(successResultRequest)); } catch (Exception ex) { // Send the failure response and log return(await DoException(codePipelineClient, ex, jobId, context)); } } }