PowerShell · 13.09.2024

Верное время в любое время

Всем доброго пятничного вечера! Сегодня поговорим о синхронизации времени, если со службой w32time что-то идёт не так. Заодно попробуем сверить часы с каким-нибудь местным сервером, если служба решила отдохнуть от нас и остановиться.

Очень не хочется откатываться на общение с batch скриптами, поэтому попробуем пообщаться с w32tm напрямую из PowerShell.

Для последующих применений я написал функцию TimeSync с обязательным параметром IP:

function TimeSync
{
    Param([Parameter(Mandatory=$true)]$IP)
    $service = "W32Time"
    $time = (Get-Service -Name $service).Status
    if ($time = "Running")
    { 
       Write-Host "Служба $service была запущена ранее."
       Write-Host "Пытаюсь синхронизироваться..."
       w32tm /resync
       Sleep(5)
       $curdatetime = Get-Date -Format "yyyy.MM.dd HH:mm"
       Write-Host "Сейчас на хосте: $curdatetime"
    }
    else
    { 
       Write-Host "Служба $service остановлена!"
       Write-Host "Пытаюсь запустить $service и установить автоматический тип запуска..."
       Set-Service -Name $service -StartupType Automatic
       Start-Service -Name $service
       Sleep(5)
       if ((Get-Service -Name $service).Status = "Running")
       {
          Write-Host "Служба $service успешно запущена!"
          Write-Host "Пытаюсь добавить узел синхронизации $IP..."
          w32tm /config /syncfromflags:manual /manualpeerlist:$IP
          Write-Host "Пытаюсь синхронизироваться с узлом $IP..."
          w32tm /resync
          $curdatetime = Get-Date -Format "yyyy.MM.dd HH:mm"
          Write-Host "Сейчас на хосте: $curdatetime"
       }
       else
       {
          Write-Host "Не удалось запустить службу $service..."  
       }
    }
}

Принцип работы функции прост: если служба запущена вызываем ручную синхронизацию, если же служба отдыхает — пытаемся её разбудить, задать сервер для синхронизации и синхронизироваться.

Указав IP-адрес сервера для сверки времени, вызываем функцию:

TimeSync -IP 192.168.1.100

Основная проблема функции TimeSync в том, что ответ от команды w32tm возвращается в кодировке cp866. Поэтому без явного объявления этой кодировки никак не обойтись.

Собираю итоговый вариант скрипта, указывая нужную кодировку:

[System.Console]::Title = "Синхронизация времени"
[Console]::OutputEncoding = [System.Text.Encoding]::GetEncoding("cp866")

function TimeSync
{
    Param([Parameter(Mandatory=$true)]$IP)
    $service = "W32Time"
    $time = (Get-Service -Name $service).Status
    if ($time = "Running")
    { 
       Write-Host "Служба $service была запущена ранее."
       Write-Host "Пытаюсь синхронизироваться..."
       w32tm /resync
       Sleep(5)
       $curdatetime = Get-Date -Format "yyyy.MM.dd HH:mm"
       Write-Host "Сейчас на хосте: $curdatetime"
    }
    else
    { 
       Write-Host "Служба $service остановлена!"
       Write-Host "Пытаюсь запустить $service и установить автоматический тип запуска..."
       Set-Service -Name $service -StartupType Automatic
       Start-Service -Name $service
       Sleep(5)
       if ((Get-Service -Name $service).Status = "Running")
       {
          Write-Host "Служба $service успешно запущена!"
          Write-Host "Пытаюсь добавить узел синхронизации $IP..."
          w32tm /config /syncfromflags:manual /manualpeerlist:$IP
          Write-Host "Пытаюсь синхронизироваться с узлом $IP..."
          w32tm /resync
          $curdatetime = Get-Date -Format "yyyy.MM.dd HH:mm"
          Write-Host "Сейчас на хосте: $curdatetime"
       }
       else
       {
          Write-Host "Не удалось запустить службу $service..."  
       }
    }
}

TimeSync -IP 192.168.1.100
Write-Host
Write-Host "Это окно закроется автоматически..."
Sleep(10)

Главное, не забыть запустить всё это дело от имени администратора!

Скрипт можно скомпилировать в исполняемый файл, используя модуль PS2EXE.

А с другой стороны можно всё низвести до базовых команд и пришпандорить к планировщику.

Например, накидаем скрипт и назовём его sync.ps1:

Start-Service w32time -Verbose
w32tm /config /syncfromflags:manual /manualpeerlist:192.168.1.100 # <= IP-адрес Вашего NTP
w32tm /resync
# Get-Date # <= Если требуется просмотреть результат

Теперь создадим ещё один скрипт и наречём его sync_job.ps1:

$trigger = New-ScheduledTaskTrigger -At 0:01 -Daily
$user = "NT AUTHORITY\SYSTEM"
$action= New-ScheduledTaskAction -Execute "PowerShell.exe" -Argument "disk:\path\to\script\sync.ps1" # <= Прописываем путь к файлу sync.ps1
Register-ScheduledTask -TaskName "SyncTime" -Trigger $trigger -User $user -Action $action -RunLevel Highest –Force

Запускаем скрипт sync_job.ps1 для создания ежедневного задания в планировщике и радуемся автоматизации процесса.