Comment forcer une synchronisation temporelle avec PowerShell

Un chronométrage précis est essentiel dans les environnements informatiques des entreprises. Qu’il s’agisse d’audit de sécurité, de corrélation de journaux, d’authentification Kerberos ou de planification de tâches d’automatisation, une horloge système correctement synchronisée peut faire la différence entre un fonctionnement sans heurts et une perturbation généralisée. Malheureusement, la dérive temporelle est fréquente sur les systèmes Windows, en particulier ceux qui sont déconnectés d’Active Directory ou dont les services de gestion du temps sont mal configurés. Cet article explique comment les professionnels de l’informatique forcent une synchronisation temporelle avec PowerShell sur Windows, afin de garantir la précision et la fiabilité des opérations.

Contexte

Les appareils Windows s’appuient sur le service Windows Time (W32Time) pour se synchroniser avec une source de temps fiable, généralement à l’aide du protocole NTP (Network Time Protocol). Cependant, W32Time peut parfois ne pas démarrer, dériver de manière significative ou pointer vers des serveurs de temps non valides. Pour les fournisseurs de services gérés (MSP) et les administrateurs informatiques qui gèrent divers parcs de terminaux, en particulier dans les environnements de travail distribués ou hybrides, il est essentiel de pouvoir inspecter et résoudre les problèmes de synchronisation de manière programmatique.

Le script PowerShell fourni répond précisément à ce besoin. Il inspecte la configuration actuelle de la synchronisation du temps, vérifie le service Windows Time, l’active et le démarre éventuellement, puis tente une synchronisation manuelle du temps. En cours de route, il fournit un aperçu détaillé du fuseau horaire du système et de l’état de la synchronisation, aidant ainsi les professionnels de l’informatique à prendre des décisions éclairées ou à résoudre des problèmes de temps plus profonds.

Le script :

#Requires -Version 5.1

<#
.SYNOPSIS
    Force a time synchronization with the current time settings and display the current time zone and time sync settings.
.DESCRIPTION
    Force a time synchronization with the current time settings and display the current time zone and time sync settings.
By using this script, you indicate your acceptance of the following legal terms as well as our Terms of Use at https://www.ninjaone.com/terms-of-use.
    Ownership Rights: NinjaOne owns and will continue to own all right, title, and interest in and to the script (including the copyright). NinjaOne is giving you a limited license to use the script in accordance with these legal terms. 
    Use Limitation: You may only use the script for your legitimate personal or internal business purposes, and you may not share the script with another party. 
    Republication Prohibition: Under no circumstances are you permitted to re-publish the script in any script library or website belonging to or under the control of any other software provider. 
    Warranty Disclaimer: The script is provided “as is” and “as available”, without warranty of any kind. NinjaOne makes no promise or guarantee that the script will be free from defects or that it will meet your specific needs or expectations. 
    Assumption of Risk: Your use of the script is at your own risk. You acknowledge that there are certain inherent risks in using the script, and you understand and assume each of those risks. 
    Waiver and Release: You will not hold NinjaOne responsible for any adverse or unintended consequences resulting from your use of the script, and you waive any legal or equitable rights or remedies you may have against NinjaOne relating to your use of the script. 
    EULA: If you are a NinjaOne customer, your use of the script is subject to the End User License Agreement applicable to you (EULA).

.PARAMETER -EnableAndStartWindowsTimeService
    Enable and start the Windows Time service if it is disabled or not running. This will set the Start Type to 'Automatic.' This service is required to force a time sync.

.EXAMPLE
    ### Current time zone settings: ###

    Id                         : Pacific Standard Time
    DisplayName                : (UTC-08:00) Pacific Time (US & Canada)
    StandardName               : Pacific Standard Time
    DaylightName               : Pacific Daylight Time
    BaseUtcOffset              : -08:00:00
    SupportsDaylightSavingTime : True

    ### Current time sync settings: ###

    Sync Type                      : NTP only
    NTP Servers                    : time.windows.com
    Last Sync Time                 : 5/12/2025 1:05:48 PM
    Last Sync Source               : time.windows.com
    Sync Interval (NTP)            : 10 minutes
    Sync Interval Minimum (Domain) : 1 minutes
    Sync Interval Maximum (Domain) : 546 minutes

    [Info] Attempting to force a sync with current time settings...
    Sending resync command to local computer
    The command completed successfully.

    ### Current time: ###

    Monday, May 12, 2025 1:05:55 PM

.NOTES
    Minimum OS Architecture Supported: Windows 10, Windows Server 2016
    Release Notes: Initial Release
#>

[CmdletBinding()]
param (
    [Parameter()]
    [switch]$EnableAndStartWindowsTimeService = [System.Convert]::ToBoolean($env:EnableAndStartWindowsTimeService)
)

begin {
    function Test-IsElevated {
        [CmdletBinding()]
        param ()
        
        # Get the current Windows identity of the user running the script
        $id = [System.Security.Principal.WindowsIdentity]::GetCurrent()
        
        # Create a WindowsPrincipal object based on the current identity
        $p = New-Object System.Security.Principal.WindowsPrincipal($id)
        
        # Check if the current user is in the Administrator role
        # The function returns $True if the user has administrative privileges, $False otherwise
        # 544 is the value for the Built In Administrators role
        # Reference: https://learn.microsoft.com/en-us/dotnet/api/system.security.principal.windowsbuiltinrole
        $p.IsInRole([System.Security.Principal.WindowsBuiltInRole]'544')
    }
    function Get-TimeSettings {
        # Get the time sync status
        try {
            $StatusOutputFilename = "$env:temp\w32tm_status_output_$(Get-Random).txt"
            Start-Process -FilePath "$env:WinDir\system32\w32tm.exe" -ArgumentList "/query /status" -RedirectStandardOutput $StatusOutputFilename -NoNewWindow -Wait
        }
        catch {
            throw (New-Object System.Exception("Unable to retrieve time sync status."))
        }
        
        # Get the time sync configuration
        try {
            $ConfigOutputFilename = "$env:temp\w32tm_config_output_$(Get-Random).txt"
            Start-Process -FilePath "$env:WinDir\system32\w32tm.exe" -ArgumentList "/query /configuration" -RedirectStandardOutput $ConfigOutputFilename -NoNewWindow -Wait
        }
        catch {
            throw (New-Object System.Exception("Unable to retrieve time sync configuration."))
        }
        
        # Attempt to read the status output file
        try {
            $status = Get-Content $StatusOutputFilename -Encoding Oem
        }
        catch {
            throw (New-Object System.Exception("Unable to read time sync status."))
        }

        # Attempt to read the config output file
        try {
            $config = Get-Content $ConfigOutputFilename -Encoding Oem
        }
        catch {
            throw (New-Object System.Exception("Unable to read time sync configuration."))
        }

        $lastSyncTime = ($status[-4] -replace "^\w+:\s" -replace "^.+: " | Out-String).Trim()
        $lastSyncSource = ($status[-3] -replace "^\w+:\s" -replace "^.+: " -replace ",0x\w" | Out-String).Trim()
        $syncType = (($config | Select-String -Pattern "^Type: ") -replace "Type: " -replace "\(.+$" | Out-String).Trim()

        $regPath = "HKLM:\SYSTEM\CurrentControlSet\Services\W32Time\Parameters"
        $NtpServers = (Get-ItemProperty -Path $regPath -Name "NtpServer" -ErrorAction SilentlyContinue).NtpServer.Trim() -replace ",0x\w" -replace "\s",", "

        $syncType = switch ($syncType) {
            "NTP" { "NTP only" }
            "NT5DS" { "Domain only" }
            "AllSync" { "Domain with NTP as fallback" }
            default { "Unknown" }
        }

        # Get the SpecialPollInterval from the config
        $SpecialPollInterval = ($config | Select-String -Pattern "^SpecialPollInterval: ") -replace "SpecialPollInterval: " -replace "\(.+$"
        # Convert the SpecialPollInterval to minutes
        $SpecialPollIntervalInMinutes = [int]$SpecialPollInterval / 60

        $object = [PSCustomObject]@{
            "Sync Type"         = $syncType
            "NTP Servers"       = $NtpServers
            "Last Sync Time"    = $lastSyncTime
            "Last Sync Source"  = $lastSyncSource
            "Sync Interval (NTP)" = "$SpecialPollIntervalInMinutes minutes"
        }

        # Get the Min and Max poll intervals used for domain time sync
        $regPath = "HKLM:\SYSTEM\CurrentControlSet\Services\W32Time\Config"
        $MinPollInterval = (Get-ItemProperty -Path $regPath -Name "MinPollInterval" -ErrorAction SilentlyContinue).MinPollInterval
        $MaxPollInterval = (Get-ItemProperty -Path $regPath -Name "MaxPollInterval" -ErrorAction SilentlyContinue).MaxPollInterval

        if ($MinPollInterval -and $MaxPollInterval) {
            # These values are actually powers of 2, the resulting value is the amount of seconds
            $MinPollInterval = [math]::Pow(2, $MinPollInterval)
            $MaxPollInterval = [math]::Pow(2, $MaxPollInterval)

            # Convert those seconds to minutes
            $MinPollInterval = [math]::Round($MinPollInterval / 60)
            $MaxPollInterval = [math]::Round($MaxPollInterval / 60)

            # Add the intervals to the object
            $object | Add-Member -MemberType NoteProperty -Name "Sync Interval Minimum (Domain)" -Value "$MinPollInterval minutes"
            $object | Add-Member -MemberType NoteProperty -Name "Sync Interval Maximum (Domain)" -Value "$MaxPollInterval minutes"
        }
        else {
            Write-Host -Object "[Warning] Unable to retrieve the minimum and maximum poll intervals from the registry."
            $object | Add-Member -MemberType NoteProperty -Name "Sync Interval Minimum (Domain)" -Value "Unavailable"
            $object | Add-Member -MemberType NoteProperty -Name "Sync Interval Maximum (Domain)" -Value "Unavailable"
        }

        # Attempt to remove the status output file
        try {
            Remove-Item $StatusOutputFilename -ErrorAction Stop
        }
        catch {
            Write-Host -Object "[Warning] Unable to delete the temporary file '$StatusOutputFilename'."
        }

        # Attempt to remove the config output file
        try {
            Remove-Item $ConfigOutputFilename -ErrorAction Stop
        }
        catch {
            Write-Host -Object "[Warning] Unable to delete the temporary file '$ConfigOutputFilename'."
        }

        return $object
    }
}
process {
    # Attempt to determine if the current session is running with Administrator privileges.
    try {
        $IsElevated = Test-IsElevated -ErrorAction Stop
    }
    catch {
        Write-Host -Object "[Error] $($_.Exception.Message)"
        Write-Host -Object "[Error] Unable to determine if the account '$env:Username' is running with Administrator privileges."
        exit 1
    }
    
    if (!$IsElevated) {
        Write-Host -Object "[Error] Access Denied: Please run with Administrator privileges."
        exit 1
    }

    $ExitCode = 0

    # Get status of Windows Time Service
    # This service is required for time sync
    try {
        $WindowsTimeService = Get-Service -Name "w32time" -ErrorAction Stop
    }
    catch {
        Write-Host -Object "[Error] Unable to retrieve Windows Time service status."
        Write-Host -Object "[Error] $($_.Exception.Message)"
        exit 1
    }

    # Enable the Windows Time service if requested
    if ($EnableAndStartWindowsTimeService) {
        Write-Host -Object "`n[Info] Setting Windows Time service to start up automatically..."

        # Set the start type to automatic if it is not already
        if ($WindowsTimeService.StartType -ne "Automatic") {
            try {
                Set-Service -Name "w32time" -StartupType Automatic -ErrorAction Stop
                Write-Host -Object "[Info] Windows Time service enabled successfully."
            }
            catch {
                Write-Host -Object "[Error] Unable to enable Windows Time service."
                Write-Host -Object "[Error] $($_.Exception.Message)"
                # Set flag to true so that we can skip forcing the sync
                $serviceError = $true
                $ExitCode = 1
            }
        }
        else {
            Write-Host "[Info] Windows Time service is already set to start automatically."
        }

        # Start the Windows Time service if it is not already running
        Write-Host -Object "`n[Info] Starting Windows Time service..."
        if ($WindowsTimeService.Status -ne "Running") {
            try {
                Start-Service -Name "w32time" -WarningAction SilentlyContinue -ErrorAction Stop
                Write-Host -Object "[Info] Windows Time service started successfully."
            }
            catch {
                Write-Host -Object "[Error] Unable to enable Windows Time service."
                Write-Host -Object "[Error] $($_.Exception.Message)"
                # Set flag to true so that we can skip forcing the sync
                $serviceError = $true
                $ExitCode = 1
            }
        }
        else {
            Write-Host "[Info] Windows Time service is already running."
        }
    }
    # Otherwise check if the service is running and set to start automatically, error if not
    else {
        if ($WindowsTimeService.StartType -ne "Automatic") {
            Write-Host "`n[Error] Windows Time service is not set to start automatically. Please use the 'Enable and Start Windows Time Service' option to enable it."
            # Set flag to true so that we can skip forcing the sync
            $serviceError = $true
            $ExitCode = 1
        }
        elseif ($WindowsTimeService.Status -ne "Running") {
            Write-Host "`n[Error] Windows Time service is not running. Please use the 'Enable and Start Windows Time Service' option to start it."
            # Set flag to true so that we can skip forcing the sync
            $serviceError = $true
            $ExitCode = 1
        }
    }

    # Show current time zone settings
    try {
        Write-Host -Object "`n### Current time zone settings: ###`n"
        (Get-TimeZone -ErrorAction Stop | Out-String).Trim()
    }
    catch {
        Write-Host -Object "[Error] Unable to retrieve current time zone settings."
        Write-Host -Object "[Error] $($_.Exception.Message)"
        $ExitCode = 1
    }
    
    # Show current time sync settings if the Windows Time service is running
    if (-not $serviceError) {
        try {
            Write-Host -Object "`n### Current time sync settings: ###`n"
            $timeSettings = Get-TimeSettings -ErrorAction Stop
            ($timeSettings | Format-List | Out-String).Trim()
            if ($timeSettings."Sync Type" -eq "Unknown") {
                Write-Host -Object "`n[Error] Unable to determine the sync type. The time sync settings may not be configured correctly."
                $skipSync = $true
                $ExitCode = 1
            }
        }
        catch {
            Write-Host -Object "[Error] Unable to retrieve current time sync settings."
            $skipSync = $true
            Write-Host -Object "[Error] $($_.Exception.Message)"
            $ExitCode = 1
        }
    }
    else {
        Write-Host -Object "`n[Error] Unable to retrieve current time sync settings because the Windows Time service is not running."
    }

    # Force a sync if Windows Time service is running
    if ($serviceError) {
        Write-Host "`n[Error] Unable to force a time sync because the Windows Time service is not running."
        $ExitCode = 1
    }
    elseif ($skipSync) {
        Write-Host "`n[Error] Unable to force a time sync because the sync type is unknown. Please correct the time sync settings using the 'Time Sync - Configure Settings' script in the Template Library."
    }
    else {
        try {
            Write-Host "`n[Info] Attempting to force a sync with current time settings..."

            # Create a temporary file to store the output
            $ResyncOutputFile = "$env:temp\w32tm_resync_output_$(Get-Random).txt"
            
            # Start the process to force a time sync 
            Start-Process -FilePath "$env:Windir\System32\w32tm.exe" -ArgumentList "/resync" -NoNewWindow -Wait -RedirectStandardOutput $ResyncOutputFile -ErrorAction Stop
        }
        catch {
            Write-Host -Object "[Error] Unable to initiate time sync."
            Write-Host -Object "[Error] $($_.Exception.Message)"
            $ExitCode = 1
        }

        # Make sure the output file exists
        if (Test-Path -Path $ResyncOutputFile) {
            # Read the output of the time sync
            try {
                Get-Content $ResyncOutputFile -Encoding Oem -ErrorAction Stop | Out-Host
            }
            catch {
                Write-Host -Object "[Error] Unable to read the output of the time sync."
                Write-Host -Object "[Error] $($_.Exception.Message)"
                $ExitCode = 1
            }

            # Clean up the output file
            try {
                Remove-Item -Path $ResyncOutputFile -Force -ErrorAction Stop
            }
            catch {
                Write-Host -Object "[Error] Unable to delete the output file."
                Write-Host -Object "[Error] $($_.Exception.Message)"
                $ExitCode = 1
            }
        }
        else {
            Write-Host -Object "[Error] Unable to find the output file."
            $ExitCode = 1
        }
    }
    
    # Show current time
    try {
        Write-Host -Object "`n### Current time: ###"
        Get-Date -DisplayHint DateTime -ErrorAction Stop | Out-Host
    }
    catch {
        Write-Host -Object "[Error] Unable to retrieve current time."
        Write-Host -Object "[Error] $($_.Exception.Message)"
        $ExitCode = 1
    }

    exit $ExitCode
}
end {
    
    
    
}

 

Description détaillée

Le script est modulaire et complet, ce qui le rend bien adapté à l’exécution autonome et à l’intégration dans des outils tels que NinjaOne. Voici une description étape par étape :

1. Contrôle des autorisations

Il commence par vérifier si le script est exécuté avec des privilèges administratifs, car les actions de synchronisation temporelle nécessitent une élévation.

2. Validation du service Windows Time

Il interroge le service w32time :

  • Si l’option -EnableAndStartWindowsTimeService est passée (ou définie par une variable d’environnement), le service démarre automatiquement et s’assure qu’il est en cours d’exécution.
  • Dans le cas contraire, il vérifie que le service est actif et avertit l’utilisateur s’il ne l’est pas.

3. Affichage du fuseau horaire et des paramètres de synchronisation

Le texte alors :

  • Affiche le fuseau horaire actuel en utilisant Get-TimeZone.
  • Utilise les commandes w32tm.exe pour recueillir l’état et la configuration de la synchronisation.
  • Parses :
    • Serveurs NTP utilisés
    • Dernière heure de synchronisation et source
    • Intervalles de synchronisation (NTP et spécifiques au domaine)
    • Type de synchronisation : NTP, basé sur un domaine (NT5DS), ou fallback (AllSync)

4. Forcer la synchronisation de l’heure

Si la validation réussit, il appelle w32tm /resync et capture la sortie, indiquant le succès ou détaillant les erreurs.

5. Courant de sortie Temps

Il se termine par l’affichage de l’heure actualisée du système.

Cas d’utilisation potentiels

Étude de cas : Dérive des terminaux de vente distant

Un fournisseur de services gérés (MSP) prend en charge 300 systèmes de point de vente (POS) au détail, dont beaucoup ne sont pas reliés à un domaine et dépendent de serveurs NTP publics. Au fil du temps, plusieurs terminaux sont désynchronisés de plusieurs minutes, ce qui entraîne des décalages entre l’horodatage des transactions et le système central de facturation.

En utilisant ce script via le module de script de NinjaOne, l’équipe informatique l’envoie à tous les systèmes concernés. Le script :

  • Valide et démarre le service Windows Time,
  • Confirme la configuration du serveur NTP,
  • Resynchronise l’horloge, et
  • Enregistre les paramètres de synchronisation actuels à des fins d’audit.

Cette solution proactive permet d’éviter les retards ayant un impact sur les recettes et les erreurs de réconciliation lors du traitement de fin de journée.

Comparaisons

MéthodeAvantagesInconvénients
Commandes du manuel w32tmDes répliques rapidesPas de validation, de traitement des erreurs ou de contrôle des services
Politique de groupe / Contrôle de domaineAutomatique pour les appareils reliés à ADInefficace pour les groupes de travail ou les systèmes distants
Ce script PowerShellVisibilité totale, automatisation possible, prise en charge des appareils AD et non ADAccès administrateur et PowerShell 5.1+ requis

Ce script comble le fossé entre le dépannage manuel et l’automatisation basée sur le domaine, en particulier pour les environnements hybrides et MSP.

FAQ

Question 1 : Ce script peut-il fonctionner sur des systèmes qui ne sont pas des domaines ?

Oui. Il détecte le type de synchronisation et prend en charge les configurations NTP uniquement.

Question 2 : Que se passe-t-il si la synchronisation échoue ?

Le script fournit des erreurs spécifiques, telles qu’un service qui ne fonctionne pas, une mauvaise configuration ou un type de synchronisation inconnu, ce qui permet d’orienter les mesures correctives.

Question 3 : Le script peut-il être exécuté plusieurs fois en toute sécurité ?

Absolument. Il vérifie l’état des services et évite les changements redondants.

Question 4 : Puis-je programmer ce script ?

Oui. Il peut être programmé via le planificateur de tâches, des outils RMM tels que NinjaOne, ou intégré dans des flux de travail de remédiation plus larges.

Implications

Un service de gestion du temps mal configuré n’est pas seulement un désagrément, il peut entraîner de graves défaillances informatiques. Les services d’authentification (Kerberos), les journaux d’événements, la surveillance du système et même la validation des certificats SSL/TLS dépendent de l’exactitude des horloges du système. En utilisant ce script pour inspecter et appliquer la synchronisation, les équipes informatiques réduisent les risques :

  • Risques pour la sécurité dus à des défaillances d’audit,
  • Les erreurs d’application dues à la logique temporelle, et
  • Lacunes en matière de conformité dans des environnements où le temps est compté.

Recommandations

  • Exécutez toujours le programme avec les privilèges d’administrateur.
  • Utilisez le drapeau -EnableAndStartWindowsTimeService dans les scripts non surveillés ou les outils RMM.
  • Surveillez les paramètres de synchronisation de manière proactive sur les terminaux distants et non-domaines.
  • La sortie du journal de ce script est enregistrée dans des plates-formes de journalisation centralisées à des fins d’audit.

Pour une automatisation plus large, envisagez de l’intégrer à une logique de journalisation/rapport et de la déployer par le biais de votre RMM de prédilection.

Conclusion

La synchronisation des horloges des systèmes est fondamentale pour la stabilité et la sécurité informatiques. Ce script PowerShell offre un moyen clair, fiable et facile à automatiser d’appliquer la synchronisation de l’heure dans les environnements, qu’ils soient reliés à un domaine ou autonomes.

Avec NinjaOne les professionnels de l’informatique peuvent aller plus loin. En déployant des scripts de ce type via le moteur d’automatisation de NinjaOne, en surveillant les rapports de réussite/d’échec et en remédiant aux dérives à l’échelle, les MSP peuvent assurer la conformité de la synchronisation des temps sans avoir à lever le petit doigt. Combinée à des champs personnalisés et à des alertes, la gestion du temps devient un atout à ne pas négliger.

Next Steps

Building an efficient and effective IT team requires a centralized solution that acts as your core service delivery tool. NinjaOne enables IT teams to monitor, manage, secure, and support all their devices, wherever they are, without the need for complex on-premises infrastructure.

Learn more about NinjaOne Remote Script Deployment, check out a live tour, or start your free trial of the NinjaOne platform.

Catégories :

Vous pourriez aussi aimer