diff --git a/update-cloudflare-dns.ps1 b/update-cloudflare-dns.ps1 index 9d44ee2..976ffa6 100644 --- a/update-cloudflare-dns.ps1 +++ b/update-cloudflare-dns.ps1 @@ -1,47 +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 -} - -### 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 - Exit -} - ### Get External ip from internet function Get-Ip-External { param ([bool] $IPv6) @@ -51,14 +9,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 { @@ -85,15 +35,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 { @@ -119,106 +60,188 @@ 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 -} -### 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" } - } +[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 - $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() +### 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 -### 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 +### 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 } -Write-Output "==> DNS record of $dns_record is: $dns_record_ip. Trying to update..." | Tee-Object $File_LOG -Append +# Iterate across each of the keys in the hash table +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 "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 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 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 + } -$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 -} + ### 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 + } -### Get the dns record id from response -$dns_record_id = $cloudflare_record_info_resposne.result.id.Trim() + ### 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 + } -### 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" } + 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 } - "name" = $dns_record - "content" = $ip - "ttl" = $ttl - "proxied" = $proxied - } | ConvertTo-Json -} + 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 + } -$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 -} + ### 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 "==> Success!" | Tee-Object $File_LOG -Append -Write-Output "==> $dns_record DNS Record Updated To: $ip, ttl: $ttl, proxied: $proxied" | 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_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_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 + } -if ($notify_me_telegram -eq "no" -And $notify_me_discord -eq "no") { - 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" } + } -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' + $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 } - $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 + + ### Get the dns record id from response + $dns_record_id = $cloudflare_record_info_resposne.result.id.Trim() + + ### 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 } -} -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" } + $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") { + Continue + } + + 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 + } + } + + 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" } + } try { Invoke-RestMethod -Proxy $http_proxy -ProxyCredential $proxy_credential @discord_notification } catch { @@ -226,5 +249,6 @@ if ($notify_me_discord -eq "yes") { 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 } - Exit -} + Continue + } +} \ No newline at end of file diff --git a/update-cloudflare-dns_conf.ps1 b/update-cloudflare-dns_conf.ps1 index 3042ae2..a924d09 100644 --- a/update-cloudflare-dns_conf.ps1 +++ b/update-cloudflare-dns_conf.ps1 @@ -1,10 +1,27 @@ ##### 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 to be updated -$dns_record = "ddns.example.com" +## DNS A record or records to be updated +## 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 = @{ + what_ip = "external"; + 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 +30,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