# gitea.RdzeN.net # DDNS Cloudflare - KeepUpIP # <# The script checks the current external IP based on defined websites. In case of a change in the external IP, it updates the CloudFlare service using a dedicated API. It requires providing 'api_key' and 'dns_zone' IDs. Parameters, such as the number of logs set to 10k lines, can be customized. The frequency of execution should be defined in cron/schedule. #> # Variable section $location = Get-Location $path_ip = "$($location)\cloudflare_ddns_ip" # store last set IP $path_log = "$($location)\cloudflare_ddns_log" # store logs $path_sites = "$($location)\cloudflare_ddns_sites" $log_limit = 10000 $ip = $null $ip_actuall = $null $ip_ddns = $null $api_key = 'enter_api_key' # https://dash.cloudflare.com/profile/api-tokens $dns_zone = 'enter_dns_zone_id' # cloudflare -> websites -> your site -> dns_zone # if as Page list; # Alternatively, it checks whether the file already exists. # The file changes the order of pages by rotating like a die. # The first element becomes the last, and so on, in a rolling cube fashion. if (-not (Test-Path $path_sites)) { $ip_sites = @( 'http://ipinfo.io/ip' 'http://ifconfig.me/ip' 'http://icanhazip.com' 'http://ident.me' 'https://api.ipify.org' 'https://ip2location.io/ip' ) } else { [array]$ip_sites = Get-Content $path_sites } # function for logs. $logs = @() function log { param ( $l_level, $l_message ) $l_timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss.ffff K" switch ($l_level) { 0 { $log_message = "$($l_timestamp) | [INFO] | $($l_message)" } 1 { $log_message = "$($l_timestamp) | [WARNING] | $($l_message)" } 2 { $l_error = $Error[0].Exception $log_message = "$($l_timestamp) | [ERROR] | $($l_message) | $l_error" } } return $log_message } # function for storing logs. function log_store { $logs >> $path_log if (Test-Path $path_log) { $tmp = Get-Content $path_log -Tail $log_limit $tmp | Set-Content $path_log } } # 01 - Get the actual IP function get-ip { for ($i = 0; $i -lt $ip_sites.Count; $i++) { switch ($ip_sites[$i]) { 'http://ipinfo.io/ip' { $uri = $ip_sites[$i] try { $test = Test-Connection -TargetName $uri.Replace("http://","").Split("/")[0] -Count 2 $ip = (Invoke-RestMethod -Uri $uri -Method Get).Trim() } catch { $global:logs += log -l_level 1 -l_message "IP: $($Error[0].Exception) | URL: $($uri)" continue } } 'http://ifconfig.me/ip' { $uri = $ip_sites[$i] try { $test = Test-Connection -TargetName $uri.Replace("http://","").Split("/")[0] -Count 1 $ip = (Invoke-RestMethod -Uri $uri -Method Get).Trim() } catch { $global:logs += log -l_level 1 -l_message "IP: $($Error[0].Exception) | URL: $($uri)" continue } } 'http://icanhazip.com' { $uri = $ip_sites[$i] try { $test = Test-Connection -TargetName $uri.Replace("http://","").Split("/")[0] -Count 1 $ip = (Invoke-RestMethod -Uri $uri -Method Get).Trim() } catch { $global:logs += log -l_level 1 -l_message "IP: $($Error[0].Exception) | URL: $($uri)" continue } } 'http://ident.me' { $uri = $ip_sites[$i] try { $test = Test-Connection -TargetName $uri.Replace("http://","").Split("/")[0] -Count 1 $ip = (Invoke-RestMethod -Uri $uri -Method Get).Trim() } catch { $global:logs += log -l_level 1 -l_message "IP: $($Error[0].Exception) | URL: $($uri)" continue } } 'https://api.ipify.org' { $uri = $ip_sites[$i] try { $test = Test-Connection -TargetName $uri.Replace("https://","").Split("/")[0] -Count 1 $ip = (Invoke-RestMethod -Uri $uri -Method Get).Trim() } catch { $global:logs += log -l_level 1 -l_message "IP: ERROR | URL: $($uri)" continue } } 'https://ip2location.io/ip' { $uri = $ip_sites[$i] try { $test = Test-Connection -TargetName $uri.Replace("https://","").Split("/")[0] -Count 1 $ip = (Invoke-RestMethod -Uri $uri -Method Get).Trim() } catch { $global:logs += log -l_level 1 -l_message "IP: ERROR | URL: $($uri)" continue } } } if ($null -ne $ip) { $global:logs += log -l_level 0 -l_message "IP: $($ip) | URL: $($uri)" $ip_sites = $ip_sites[1..($ip_sites.Count)] + $ip_sites[0] $ip_sites > $path_sites break } } return $ip } $ip_actuall = get-ip # If the retrieved IP is NULL or does not match the regex, then... if (($null -eq $ip_actuall) -or ($ip_actuall -notmatch "^((25[0-5]|(2[0-4]|1\d|[1-9]|)\d)\.?\b){4}$")) { $logs += log -l_level 2 -l_message "Error durring geting public IP | EXIT" log_store exit } # Verification of the existence of a file with the saved IP; # If the file exists, then verify if the received IP matches the saved one. If yes, close. if (Test-Path $path_ip) { $tmp = Get-Content $path_ip if ($tmp -eq $ip_actuall) { $logs += log -l_level 0 -l_message "IP MATCH! | EXIT" log_store exit } } # 02 - Get the actuall DNS record from cloudflare $headers= @{ "Content-Type" = "application/json" "Authorization" = "Bearer $api_key" } # "Try" for fetching a list of DNS records from Cloudflare. try { $response = Invoke-RestMethod -Uri "https://api.cloudflare.com/client/v4/zones/$($dns_zone)/dns_records" -Method GET -Headers $headers $cloudflare_dns = $response.result $logs += log -l_level 0 -l_message "Downloaded $([array]$cloudflare_dns.count) records." } # In case of an error, log the details to a file and close. catch { $logs += log -l_level 2 -l_message "Error durring geting dns_records from cloud_flare | EXIT" log_store exit } # "Processing each entry from Cloudflare. # If the script encounters the current IP, it will terminate with log recording. # The script is designed to update all entries with a consistent IP in case of a change. for ($i = 0; $i -lt $cloudflare_dns.Count; $i++) { $id = $cloudflare_dns[$i].id $address = $cloudflare_dns[$i].name $ip_ddns = $cloudflare_dns[$i].content $proxied = $cloudflare_dns[$i].proxied if ($ip_ddns -eq $ip_actuall) { $logs += log -l_level 0 -l_message "IP MATCH! | EXIT" log_store exit } $body = @{ content = $ip_actuall name = $address type = "A" proxied = $proxied comment = log -l_level 0 -l_message "Updated from $($ip_ddns)" } $body = $body | ConvertTo-Json $url = "https://api.cloudflare.com/client/v4/zones/$($dns_zone)/dns_records/$($id)" try { $response = Invoke-RestMethod -Uri $url -Method Put -Headers $headers -Body $body $logs += log -l_level 0 -l_message "Updated from $($ip_ddns) to $($ip_actuall) at $($address)" } catch { $logs += log -l_level 2 -l_message "Not updated from $($ip_ddns) to $($ip_actuall) at $($address)" } } # Saving the new IP to a file for future verification. $ip_actuall > $path_ip log_store exit