用 powershell 实现 DDNS 功能
最近想实现在外面访问家里的迷你电脑,因为电脑的 IPv6 地址是通过 SLACC 分配的,不能在路由器上获得迷你电脑的 IPv6 地址,所以需要在电脑上实现 DDNS 功能。就写了一个脚本,实现了已下功能:
- 从本地网卡获取 IPv6 的第一个首选地址,通过 cloudfalre api 获取 dns 记录的 ipv6 地址
- 对比获取的 IPv6 地址与 cloudflare 中的 dns 记录是否一致
- 如果比对失败则通过 cloudflare api 更新 dns 解析
- 发生错误则发送 telegram 通知
下面是脚本内容:
## ------------------------------------------------------------------------------
# 脚本名称: UpdateDNS.ps1
# 描述: 遍历所有网卡(排除WiFi),获取本机 IPv6 公网地址,通过 Cloudflare API 更新 DNS 记录,并发送 Telegram 通知
# 作者: [will https://opswill.com]
# 日期: [2024/12/31]
# 版本: 1.3
# ------------------------------------------------------------------------------
# 注意: 请确保已正确配置 API Token 和其他变量
# ------------------------------------------------------------------------------
# 设置 PowerShell 执行策略(如果需要)
# Set-ExecutionPolicy Bypass -Scope Process -Force # 建议仅在测试时使用,生产环境应避免使用 Bypass
# 配置 Cloudflare API 和 Telegram Bot 信息
$apiToken = ""
$zoneId = ""
$recordId = ""
$domain = ""
$botToken = ""
$chatId = ""
# 日志文件路径
$logFilePath = "C:\data\DDNS.log"
# 排除的网卡接口名称(正则表达式)
$excludedInterfaces = "Wi-Fi|Wireless|WLAN|Loopback|Bluetooth"
# 函数:记录日志
function Log-Message {
param(
[string]$Message
)
$timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
$logEntry = "$timestamp - $Message"
Add-Content -Path $logFilePath -Value $logEntry
}
# 函数:发送 Telegram 消息
function Send-TelegramMessage {
param(
[string]$Message
)
$telegramApiUrl = "https://api.telegram.org/bot$botToken/sendMessage"
$params = @{
chat_id = $chatId
text = $Message
}
try {
Invoke-RestMethod -Uri $telegramApiUrl -Method Post -Body $params
}
catch {
Log-Message "发送 Telegram 通知失败: $_"
}
}
# 函数:获取公网 IPv6 地址
function Get-PublicIPv6Address {
$ipv6AddressesWithInterface = Get-NetIPAddress -AddressFamily IPv6 | Where-Object {
$_.InterfaceAlias -notmatch $excludedInterfaces -and
$_.PrefixOrigin -in "RouterAdvertisement", "DHCPv6" -and
$_.SuffixOrigin -ne "Random" -and
$_.AddressState -eq "Preferred" # 确保地址是首选状态
}
if (-not $ipv6AddressesWithInterface) {
throw "未找到有效的公网 IPv6 地址。"
}
foreach ($ipv6AddressInfo in $ipv6AddressesWithInterface) {
Log-Message "网卡 '$($ipv6AddressInfo.InterfaceAlias)' 获取到公网 IPv6 地址: $($ipv6AddressInfo.IPAddress)"
}
# 仍然选择第一个地址作为返回值,但保留了记录所有地址的逻辑
return $ipv6AddressesWithInterface[0].IPAddress
}
# 函数:获取当前 DNS 记录
function Get-CurrentDNSRecord {
$uri = "https://api.cloudflare.com/client/v4/zones/$zoneId/dns_records/$recordId"
$headers = @{ Authorization = "Bearer $apiToken" }
try {
$response = Invoke-RestMethod -Uri $uri -Method Get -Headers $headers
if ($response.success) {
return $response.result.content
} else {
throw "获取 DNS 记录失败: $($response.errors | ConvertTo-Json)"
}
}
catch {
throw "获取当前 DNS 记录失败: $_"
}
}
# 函数:更新 DNS 记录
function Update-DNSRecord {
param(
[string]$ipv6Address
)
$uri = "https://api.cloudflare.com/client/v4/zones/$zoneId/dns_records/$recordId"
$headers = @{
"Content-Type" = "application/json"
Authorization = "Bearer $apiToken"
}
$body = @{
name = $domain
ttl = 60
content = $ipv6Address
type = "AAAA"
} | ConvertTo-Json
try {
$response = Invoke-RestMethod -Uri $uri -Method Patch -Headers $headers -Body $body
if ($response.success) {
$message = "成功更新 DNS 记录: $domain -> $ipv6Address"
Log-Message $message
Send-TelegramMessage $message
} else {
$message = "更新 DNS 记录失败: $($response.errors | ConvertTo-Json)"
Log-Message $message
Send-TelegramMessage "Windows DDNS: $message" # 错误信息前缀
}
}
catch {
$message = "更新 DNS 记录失败: $_"
Log-Message $message
Send-TelegramMessage "Windows DDNS: $message"
}
}
# 脚本主逻辑
Log-Message "脚本开始执行"
try {
$ipv6Address = Get-PublicIPv6Address
$currentDNSRecord = Get-CurrentDNSRecord
Log-Message "当前 $domain 的 DNS 记录内容: $currentDNSRecord"
if ($currentDNSRecord -ne $ipv6Address) {
Log-Message "DNS 记录与本机 IPv6 地址不一致,准备更新 DNS 记录。"
Update-DNSRecord -ipv6Address $ipv6Address
} else {
Log-Message "DNS 记录与本机 IPv6 地址一致,无需更新。"
}
}
catch {
$message = "Windows DDNS: $_" # 错误信息前缀
Send-TelegramMessage $message
Log-Message $message
}
Log-Message "脚本执行结束"
按 win + r 然后输入 taskschd.msc 创建一个新的计划任务,从而实现开机未登录和登录状态定期执行,这样就可以通过域名在外面访问家里的迷你电脑了。