From eb114eced3423502c0af60f07a2c2b4670009aec Mon Sep 17 00:00:00 2001 From: Adam Brousseau Date: Fri, 10 Nov 2023 16:45:01 -0500 Subject: [PATCH 1/3] Support for updating multiple A/AAAA records --- update-cloudflare-dns.ps1 | 199 +++++++++++++++++---------------- update-cloudflare-dns_conf.ps1 | 21 +++- 2 files changed, 120 insertions(+), 100 deletions(-) diff --git a/update-cloudflare-dns.ps1 b/update-cloudflare-dns.ps1 index 9d44ee2..a202a9e 100644 --- a/update-cloudflare-dns.ps1 +++ b/update-cloudflare-dns.ps1 @@ -23,19 +23,6 @@ Catch { Exit } -### Check validity of "ttl" parameter -if (( $ttl -lt 60 ) -or ($ttl -gt 7200 ) -and ( $ttl -ne 1 )) { - Write-Output 'Error! ttl out of range (60) or not set to 1' | Tee-Object $File_LOG -Append - Exit -} - -### Check validity of "proxied" parameter -if (!([string]$proxied) -or ($proxied.GetType().Name.Trim() -ne "Boolean")) { - Write-Output 'Error! Incorrect "proxied" parameter choose "$true" or "$false" ' | Tee-Object $File_LOG -Append - Exit -} - - ### Check validity of "what_ip" parameter if ( ($what_ip -ne "external") -and ($what_ip -ne "internal") ) { Write-Output 'Error! Incorrect "what_ip" parameter choose "external" or "internal"' | Tee-Object $File_LOG -Append @@ -128,103 +115,127 @@ if ($proxied -eq $false) { $is_proxed = $proxied } -### Get the dns record id and current proxy status from cloudflare's api when proxied is "true" -if ($proxied -eq $true) { - $dns_record_info = @{ - Uri = "https://api.cloudflare.com/client/v4/zones/$zoneid/dns_records?name=$dns_record" - Headers = @{"Authorization" = "Bearer $cloudflare_zone_api_token"; "Content-Type" = "application/json" } +foreach($current_key in $dns_records.Keys){ + + # Set the variables for the current key + $a_record = $dns_records[$current_key] + $dns_record = $a_record['record'] + $proxied = $a_record['proxied'] + $ttl = $a_record['ttl'] + + Write-Output "Checking $dns_record before processing..." | Tee-Object $File_LOG -Append + + ### Check validity of "ttl" parameter + if (( $ttl -lt 60 ) -or ($ttl -gt 7200 ) -and ( $ttl -ne 1 )) { + Write-Output 'Error! ttl out of range (60) or not set to 1' | Tee-Object $File_LOG -Append + Continue } - $response = Invoke-RestMethod -Proxy $http_proxy -ProxyCredential $proxy_credential @dns_record_info - if ($response.success -ne "True") { - Write-Output "Error! Can't get dns record info from cloudflare's api" | Tee-Object $File_LOG -Append + ### Check validity of "proxied" parameter + if (!([string]$proxied) -or ($proxied.GetType().Name.Trim() -ne "Boolean")) { + Write-Output 'Error! Incorrect "proxied" parameter choose "$true" or "$false" ' | Tee-Object $File_LOG -Append + Continue } - $is_proxed = $response.result.proxied - $dns_record_ip = $response.result.content.Trim() -} + Write-Output "Passed checks, processing $dns_record..." | Tee-Object $File_LOG -Append -### Check if ip or proxy have changed -if (($dns_record_ip -eq $ip) -and ($is_proxed -eq $proxied)) { - Write-Output "==> DNS record IP of $dns_record is $dns_record_ip, no changes needed. Exiting..." | Tee-Object $File_LOG -Append - Exit -} + ### Get the dns record id and current proxy status from cloudflare's api when proxied is "true" + if ($proxied -eq $true) { + $dns_record_info = @{ + Uri = "https://api.cloudflare.com/client/v4/zones/$zoneid/dns_records?name=$dns_record" + Headers = @{"Authorization" = "Bearer $cloudflare_zone_api_token"; "Content-Type" = "application/json" } + } -Write-Output "==> DNS record of $dns_record is: $dns_record_ip. Trying to update..." | Tee-Object $File_LOG -Append + $response = Invoke-RestMethod -Proxy $http_proxy -ProxyCredential $proxy_credential @dns_record_info + if ($response.success -ne "True") { + Write-Output "Error! Can't get dns record info from cloudflare's api" | Tee-Object $File_LOG -Append + } + $is_proxed = $response.result.proxied + $dns_record_ip = $response.result.content.Trim() + } -### Get the dns record information from cloudflare's api -$cloudflare_record_info = @{ - Uri = "https://api.cloudflare.com/client/v4/zones/$zoneid/dns_records?name=$dns_record" - Headers = @{"Authorization" = "Bearer $cloudflare_zone_api_token"; "Content-Type" = "application/json" } -} + ### Check if ip or proxy have changed + if (($dns_record_ip -eq $ip) -and ($is_proxed -eq $proxied)) { + Write-Output "==> DNS record IP of $dns_record is $dns_record_ip, no changes needed. Exiting..." | Tee-Object $File_LOG -Append + Continue + } -$cloudflare_record_info_resposne = Invoke-RestMethod -Proxy $http_proxy -ProxyCredential $proxy_credential @cloudflare_record_info -if ($cloudflare_record_info_resposne.success -ne "True") { - Write-Output "Error! Can't get $dns_record record inforamiton from cloudflare API" | Tee-Object $File_LOG -Append - Exit -} + Write-Output "==> DNS record of $dns_record is: $dns_record_ip. Trying to update..." | Tee-Object $File_LOG -Append + + ### Get the dns record information from cloudflare's api + $cloudflare_record_info = @{ + Uri = "https://api.cloudflare.com/client/v4/zones/$zoneid/dns_records?name=$dns_record" + Headers = @{"Authorization" = "Bearer $cloudflare_zone_api_token"; "Content-Type" = "application/json" } + } -### Get the dns record id from response -$dns_record_id = $cloudflare_record_info_resposne.result.id.Trim() + $cloudflare_record_info_resposne = Invoke-RestMethod -Proxy $http_proxy -ProxyCredential $proxy_credential @cloudflare_record_info + if ($cloudflare_record_info_resposne.success -ne "True") { + Write-Output "Error! Can't get $dns_record record inforamiton from cloudflare API" | Tee-Object $File_LOG -Append + Continue + } -### Push new dns record information to cloudflare's api -$update_dns_record = @{ - Uri = "https://api.cloudflare.com/client/v4/zones/$zoneid/dns_records/$dns_record_id" - Method = 'PUT' - Headers = @{"Authorization" = "Bearer $cloudflare_zone_api_token"; "Content-Type" = "application/json" } - Body = @{ - "type" = switch ($IPv6) { - $true { "AAAA" } - $false { "A" } - } - "name" = $dns_record - "content" = $ip - "ttl" = $ttl - "proxied" = $proxied - } | ConvertTo-Json -} + ### Get the dns record id from response + $dns_record_id = $cloudflare_record_info_resposne.result.id.Trim() -$update_dns_record_response = Invoke-RestMethod -Proxy $http_proxy -ProxyCredential $proxy_credential @update_dns_record -if ($update_dns_record_response.success -ne "True") { - Write-Output "Error! Update Failed" | Tee-Object $File_LOG -Append - Exit -} + ### Push new dns record information to cloudflare's api + $update_dns_record = @{ + Uri = "https://api.cloudflare.com/client/v4/zones/$zoneid/dns_records/$dns_record_id" + Method = 'PUT' + Headers = @{"Authorization" = "Bearer $cloudflare_zone_api_token"; "Content-Type" = "application/json" } + Body = @{ + "type" = switch ($IPv6) { + $true { "AAAA" } + $false { "A" } + } + "name" = $dns_record + "content" = $ip + "ttl" = $ttl + "proxied" = $proxied + } | ConvertTo-Json + } -Write-Output "==> Success!" | Tee-Object $File_LOG -Append -Write-Output "==> $dns_record DNS Record Updated To: $ip, ttl: $ttl, proxied: $proxied" | Tee-Object $File_LOG -Append + $update_dns_record_response = Invoke-RestMethod -Proxy $http_proxy -ProxyCredential $proxy_credential @update_dns_record + if ($update_dns_record_response.success -ne "True") { + Write-Output "Error! Update Failed" | Tee-Object $File_LOG -Append + Continue + } + Write-Output "==> Success!" | Tee-Object $File_LOG -Append + Write-Output "==> $dns_record DNS Record Updated To: $ip, ttl: $ttl, proxied: $proxied" | Tee-Object $File_LOG -Append -if ($notify_me_telegram -eq "no" -And $notify_me_discord -eq "no") { - Exit -} -if ($notify_me_telegram -eq "yes") { - $telegram_notification = @{ - Uri = "https://api.telegram.org/bot$telegram_bot_API_Token/sendMessage?chat_id=$telegram_chat_id&text=$dns_record DNS Record Updated To: $ip" - Method = 'GET' - } - $telegram_notification_response = Invoke-RestMethod -Proxy $http_proxy -ProxyCredential $proxy_credential @telegram_notification - if ($telegram_notification_response.ok -ne "True") { - Write-Output "Error! Telegram notification failed" | Tee-Object $File_LOG -Append - Exit + if ($notify_me_telegram -eq "no" -And $notify_me_discord -eq "no") { + Continue } -} -if ($notify_me_discord -eq "yes") { - $discord_message = "$dns_record DNS Record Updated To: $ip (was $dns_record_ip)" - $discord_payload = [PSCustomObject]@{content = $discord_message} | ConvertTo-Json - $discord_notification = @{ - Uri = $discord_webhook_URL - Method = 'POST' - Body = $discord_payload - Headers = @{ "Content-Type" = "application/json" } + if ($notify_me_telegram -eq "yes") { + $telegram_notification = @{ + Uri = "https://api.telegram.org/bot$telegram_bot_API_Token/sendMessage?chat_id=$telegram_chat_id&text=$dns_record DNS Record Updated To: $ip" + Method = 'GET' + } + $telegram_notification_response = Invoke-RestMethod -Proxy $http_proxy -ProxyCredential $proxy_credential @telegram_notification + if ($telegram_notification_response.ok -ne "True") { + Write-Output "Error! Telegram notification failed" | Tee-Object $File_LOG -Append + Continue + } } - try { - Invoke-RestMethod -Proxy $http_proxy -ProxyCredential $proxy_credential @discord_notification - } catch { - Write-Host "==> Discord notification request failed. Here are the details for the exception:" | Tee-Object $File_LOG -Append - Write-Host "==> Request StatusCode:" $_.Exception.Response.StatusCode.value__ | Tee-Object $File_LOG -Append - Write-Host "==> Request StatusDescription:" $_.Exception.Response.StatusDescription | Tee-Object $File_LOG -Append + + if ($notify_me_discord -eq "yes") { + $discord_message = "$dns_record DNS Record Updated To: $ip (was $dns_record_ip)" + $discord_payload = [PSCustomObject]@{content = $discord_message} | ConvertTo-Json + $discord_notification = @{ + Uri = $discord_webhook_URL + Method = 'POST' + Body = $discord_payload + Headers = @{ "Content-Type" = "application/json" } } - Exit -} + try { + Invoke-RestMethod -Proxy $http_proxy -ProxyCredential $proxy_credential @discord_notification + } catch { + Write-Host "==> Discord notification request failed. Here are the details for the exception:" | Tee-Object $File_LOG -Append + Write-Host "==> Request StatusCode:" $_.Exception.Response.StatusCode.value__ | Tee-Object $File_LOG -Append + Write-Host "==> Request StatusDescription:" $_.Exception.Response.StatusDescription | Tee-Object $File_LOG -Append + } + Continue + } +} \ No newline at end of file diff --git a/update-cloudflare-dns_conf.ps1 b/update-cloudflare-dns_conf.ps1 index 3042ae2..0aa5b97 100644 --- a/update-cloudflare-dns_conf.ps1 +++ b/update-cloudflare-dns_conf.ps1 @@ -3,8 +3,21 @@ ## Which IP should be used for the record: internal/external ## Internal interface will be chosen automaticly as a primary default interface $what_ip = "internal" -## DNS A record to be updated -$dns_record = "ddns.example.com" +## DNS A record or records to be updated +## Each record is required to have the record to update, its proxied status, and cache time (60-7200 in seconds or 1 for Auto) +$dns_records = [ordered]@{ + record1 = @{ + record = "ddns.example.com"; + proxied = $true; + ttl = 1; + } #record 1 + record2 = @{ + record = "ddns2.example.com"; + proxied = $false; + ttl = 1; + } #record 2 +} + ## Use IPv6 $IPv6 = $false ## if use DoH to query the current IP address @@ -13,10 +26,6 @@ $DNS_over_HTTPS = $false $zoneid = "ChangeMe" ## Cloudflare Zone API Token $cloudflare_zone_api_token = "ChangeMe" -## Use Cloudflare proxy on dns record true/false -$proxied = $false -## 60-7200 in seconds or 1 for Auto -$ttl = 120 ## Use proxy when connect to DoH, Cloudflare, Telegram or Discord API # $http_proxy = $null From 0d60fb66c57e7b8d0747aceb14726f13091459d9 Mon Sep 17 00:00:00 2001 From: Adam Brousseau Date: Fri, 10 Nov 2023 18:30:24 -0500 Subject: [PATCH 2/3] Handling of multiple cases between the records (some proxied/some not) --- update-cloudflare-dns.ps1 | 84 +++++++++++++++++++--------------- update-cloudflare-dns_conf.ps1 | 18 +++++--- 2 files changed, 59 insertions(+), 43 deletions(-) diff --git a/update-cloudflare-dns.ps1 b/update-cloudflare-dns.ps1 index a202a9e..c2d2c4b 100644 --- a/update-cloudflare-dns.ps1 +++ b/update-cloudflare-dns.ps1 @@ -23,12 +23,6 @@ Catch { Exit } -### Check validity of "what_ip" parameter -if ( ($what_ip -ne "external") -and ($what_ip -ne "internal") ) { - Write-Output 'Error! Incorrect "what_ip" parameter choose "external" or "internal"' | Tee-Object $File_LOG -Append - Exit -} - ### Get External ip from internet function Get-Ip-External { param ([bool] $IPv6) @@ -38,14 +32,6 @@ function Get-Ip-External { return (Invoke-RestMethod -Uri "https://checkip.amazonaws.com" -TimeoutSec 10).Trim() } } -if ($what_ip -eq 'external') { - $ip = Get-Ip-External -IPv6 $IPv6 - if (!([bool]$ip)) { - Write-Output "Error! Can't get external ip from https://checkip.amazonaws.com" | Tee-Object $File_LOG -Append - Exit - } - Write-Output "==> External IP is: $ip" | Tee-Object $File_LOG -Append -} ### Get Internal ip from primary interface function Get-Ip-Internal { @@ -72,15 +58,6 @@ function Get-Ip-Internal { throw "Get-Internal-IpAddress is not implemented for MacOS." } } -if ($what_ip -eq 'internal') { - $ip = Get-Ip-Internal -IPv6 $IPv6 - if (![bool]$ip) { - Write-Output "==>Error! Can't get internal ip address" | Tee-Object $File_LOG -Append - Exit - } - Write-Output "==> Internal IP is $ip" | Tee-Object $File_LOG -Append -} - ### Get IP address of DNS record from 1.1.1.1 DNS server when proxied is "false" function Resolve-DnsName-From-Cloudflare { @@ -106,28 +83,27 @@ function Resolve-DnsName-From-Cloudflare { return $null } } -if ($proxied -eq $false) { - $dns_record_ip = Resolve-DnsName-From-Cloudflare -DoH $DNS_over_HTTPS -domain $dns_record -IPv6 $IPv6 - if (![bool]$dns_record_ip) { - Write-Output "Error! Can't resolve the ${dns_record} via 1.1.1.1 DNS server" | Tee-Object $File_LOG -Append - Exit - } - $is_proxed = $proxied -} foreach($current_key in $dns_records.Keys){ # Set the variables for the current key $a_record = $dns_records[$current_key] + $what_ip = $a_record['what_ip'] $dns_record = $a_record['record'] $proxied = $a_record['proxied'] $ttl = $a_record['ttl'] Write-Output "Checking $dns_record before processing..." | Tee-Object $File_LOG -Append - ### Check validity of "ttl" parameter - if (( $ttl -lt 60 ) -or ($ttl -gt 7200 ) -and ( $ttl -ne 1 )) { - Write-Output 'Error! ttl out of range (60) or not set to 1' | Tee-Object $File_LOG -Append + ### Check validity of "what_ip" parameter + if ( ($what_ip -ne "external") -and ($what_ip -ne "internal") ) { + Write-Output 'Error! Incorrect "what_ip" parameter choose "external" or "internal"' | Tee-Object $File_LOG -Append + Exit + } + + ### Check validity of "record" parameter + if (!([string]$dns_record) -or $null -eq $dns_record) { + Write-Output 'Error! Need to provide the dns entry to update ' | Tee-Object $File_LOG -Append Continue } @@ -137,8 +113,36 @@ foreach($current_key in $dns_records.Keys){ Continue } + ### Check validity of "ttl" parameter + if (( $ttl -lt 60 ) -or ($ttl -gt 7200 ) -and ( $ttl -ne 1 )) { + Write-Output 'Error! ttl out of range (60) or not set to 1' | Tee-Object $File_LOG -Append + Continue + } + Write-Output "Passed checks, processing $dns_record..." | Tee-Object $File_LOG -Append + ### Variables to hold values from the various if/functions + $ip = $null + $dns_record_ip = $null + $is_proxied = $null + + if ($what_ip -eq 'external') { + $ip = Get-Ip-External -IPv6 $IPv6 + if (!([bool]$ip)) { + Write-Output "Error! Can't get external ip from https://checkip.amazonaws.com" | Tee-Object $File_LOG -Append + Exit + } + Write-Output "==> External IP is: $ip" | Tee-Object $File_LOG -Append + } + elseif ($what_ip -eq 'internal') { + $ip = Get-Ip-Internal -IPv6 $IPv6 + if (![bool]$ip) { + Write-Output "==>Error! Can't get internal ip address" | Tee-Object $File_LOG -Append + Exit + } + Write-Output "==> Internal IP is $ip" | Tee-Object $File_LOG -Append + } + ### Get the dns record id and current proxy status from cloudflare's api when proxied is "true" if ($proxied -eq $true) { $dns_record_info = @{ @@ -150,12 +154,20 @@ foreach($current_key in $dns_records.Keys){ if ($response.success -ne "True") { Write-Output "Error! Can't get dns record info from cloudflare's api" | Tee-Object $File_LOG -Append } - $is_proxed = $response.result.proxied + $is_proxied = $response.result.proxied $dns_record_ip = $response.result.content.Trim() } + elseif ($proxied -eq $false) { + $dns_record_ip = Resolve-DnsName-From-Cloudflare -DoH $DNS_over_HTTPS -domain $dns_record -IPv6 $IPv6 + if (![bool]$dns_record_ip) { + Write-Output "Error! Can't resolve the ${dns_record} via 1.1.1.1 DNS server" | Tee-Object $File_LOG -Append + Exit + } + $is_proxied = $proxied + } ### Check if ip or proxy have changed - if (($dns_record_ip -eq $ip) -and ($is_proxed -eq $proxied)) { + if (($dns_record_ip -eq $ip) -and ($is_proxied -eq $proxied)) { Write-Output "==> DNS record IP of $dns_record is $dns_record_ip, no changes needed. Exiting..." | Tee-Object $File_LOG -Append Continue } diff --git a/update-cloudflare-dns_conf.ps1 b/update-cloudflare-dns_conf.ps1 index 0aa5b97..a924d09 100644 --- a/update-cloudflare-dns_conf.ps1 +++ b/update-cloudflare-dns_conf.ps1 @@ -1,20 +1,24 @@ ##### Config -## Which IP should be used for the record: internal/external -## Internal interface will be chosen automaticly as a primary default interface -$what_ip = "internal" ## DNS A record or records to be updated -## Each record is required to have the record to update, its proxied status, and cache time (60-7200 in seconds or 1 for Auto) +## Each record is required to have the the following: +## 1. Which IP should be used for the record: internal/external +## Internal interface will be chosen automaticly as a primary default interface +## 2. Record to update +## 3. The proxied status +## 4. cache time (60-7200 in seconds or 1 for Auto) $dns_records = [ordered]@{ record1 = @{ + what_ip = "internal"; record = "ddns.example.com"; proxied = $true; ttl = 1; } #record 1 record2 = @{ - record = "ddns2.example.com"; - proxied = $false; - ttl = 1; + what_ip = "external"; + record = "ddns2.example.com"; + proxied = $false; + ttl = 1; } #record 2 } From 2062b30d6decb34fc15a8ac81065ef73adb7d2bd Mon Sep 17 00:00:00 2001 From: Adam Brousseau Date: Fri, 10 Nov 2023 18:42:59 -0500 Subject: [PATCH 3/3] Minor whitespace cleanup (all functions in PowerShell proceed the rest of a script) --- update-cloudflare-dns.ps1 | 63 ++++++++++++++++++++------------------- 1 file changed, 32 insertions(+), 31 deletions(-) diff --git a/update-cloudflare-dns.ps1 b/update-cloudflare-dns.ps1 index c2d2c4b..976ffa6 100644 --- a/update-cloudflare-dns.ps1 +++ b/update-cloudflare-dns.ps1 @@ -1,28 +1,5 @@ # https://github.com/fire1ce/DDNS-Cloudflare-PowerShell -[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 - -### updateDNS.log file of the last run for debug -$File_LOG = "$PSScriptRoot\update-cloudflare-dns.log" -$FileName = "update-cloudflare-dns.log" - -if (!(Test-Path $File_LOG)) { - New-Item -ItemType File -Path $PSScriptRoot -Name ($FileName) | Out-Null -} - -Clear-Content $File_LOG -$DATE = Get-Date -UFormat "%Y/%m/%d %H:%M:%S" -Write-Output "==> $DATE" | Tee-Object $File_LOG -Append - -### Load config file -Try { - . $PSScriptRoot\update-cloudflare-dns_conf.ps1 -} -Catch { - Write-Output "==> Error! Missing update-cloudflare-dns_conf.ps1 or invalid syntax" | Tee-Object $File_LOG -Append - Exit -} - ### Get External ip from internet function Get-Ip-External { param ([bool] $IPv6) @@ -84,6 +61,30 @@ function Resolve-DnsName-From-Cloudflare { } } +[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 + +### updateDNS.log file of the last run for debug +$File_LOG = "$PSScriptRoot\update-cloudflare-dns.log" +$FileName = "update-cloudflare-dns.log" + +if (!(Test-Path $File_LOG)) { + New-Item -ItemType File -Path $PSScriptRoot -Name ($FileName) | Out-Null +} + +Clear-Content $File_LOG +$DATE = Get-Date -UFormat "%Y/%m/%d %H:%M:%S" +Write-Output "==> $DATE" | Tee-Object $File_LOG -Append + +### Load config file +Try { + . $PSScriptRoot\update-cloudflare-dns_conf.ps1 +} +Catch { + Write-Output "==> Error! Missing update-cloudflare-dns_conf.ps1 or invalid syntax" | Tee-Object $File_LOG -Append + Exit +} + +# Iterate across each of the keys in the hash table foreach($current_key in $dns_records.Keys){ # Set the variables for the current key @@ -241,13 +242,13 @@ foreach($current_key in $dns_records.Keys){ Body = $discord_payload Headers = @{ "Content-Type" = "application/json" } } - try { - Invoke-RestMethod -Proxy $http_proxy -ProxyCredential $proxy_credential @discord_notification - } catch { - Write-Host "==> Discord notification request failed. Here are the details for the exception:" | Tee-Object $File_LOG -Append - Write-Host "==> Request StatusCode:" $_.Exception.Response.StatusCode.value__ | Tee-Object $File_LOG -Append - Write-Host "==> Request StatusDescription:" $_.Exception.Response.StatusDescription | Tee-Object $File_LOG -Append - } - Continue + try { + Invoke-RestMethod -Proxy $http_proxy -ProxyCredential $proxy_credential @discord_notification + } catch { + Write-Host "==> Discord notification request failed. Here are the details for the exception:" | Tee-Object $File_LOG -Append + Write-Host "==> Request StatusCode:" $_.Exception.Response.StatusCode.value__ | Tee-Object $File_LOG -Append + Write-Host "==> Request StatusDescription:" $_.Exception.Response.StatusDescription | Tee-Object $File_LOG -Append + } + Continue } } \ No newline at end of file