Get windows time settings from remote servers

Standard

I recently had to deal with some issues with time on our servers and I wanted to get an overview of our environment. To do that I wrote the script in this post which gets the time, timesource (NTP servers) and time zone information from remote windows servers and exports the information to a handy CSV file.

How does the script work?

1. The script loops through all the computers listed in “servers.txt” which much be created and placed in the script directory.
2. Using the function “Test-PortAlive” (I already covered this in a different blog post) the script checks that the ports 135 (rpc) and 445 (smb) are open on the server. This is to prevent the script from timing out and hanging on servers where these ports are blocked. This allows for a quick script run even with many servers.
3. If the ports are open and queries with .net and wmi are possible the script then gets the time, time source and time zone information from the server.
4. When all the information has been retrieved the results are exported to a CSV file in the script directory.

How did I build the script?

I divided the script into three sections Functions, Variables and Script Main to simplify the structure of the script.
The script has four functions which provide the main functionality.
I. Get-TimeSource
This function uses .net to query the remote registry for the NTP server settings.
II. Get-TimeZone
WMI is used to get the time zone configured on the remote server.
III. Get-Time
WMI is used to get the time on the remote server returned as a datetime object.
IV. Test-PortAlive
A function I wrote a while back which checks if given ports are open on a remote server. Here it is used to check if ports 135 and 445 are open on the remote server as this is needed to perform the queries for time configuration information.

In the #script main section I then loop through the servers and for each one execute the above functions to get the info. At then end the information is exported to CSV.

The script itself!

#############################################################################################
##Get-RemoteTime.ps1
##
##Description:		Loops through all the computers listed in "servers.txt" in the script 
#+					directory and for each one gets the timesource, timezone and the time 
#+					using .NET and WMI. Exports the results to a CSV file at the end of the
#+					script run.
##Created by:		Noam Wajnman
##Creation Date:	July 16, 2013
##Updated:			July 31, 2014
##############################################################################################
#FUNCTIONS
function Get-TimeSource {
	#############################################################################################
	##Function:			Get-RemoteTimeSource
	##
	##Description:		Pipeline function. Gets the configured NTP server timesource from the 
	#+					registry of a remote server.
	##
	##Created by:		Noam Wajnman
	##Creation Date:	31 July, 2014	
	##############################################################################################
	[CmdletBinding()]
	[OutputType([System.String])]
	param (
		[Parameter(ValueFromPipeline=$true)]$server
	)
	begin {        
	    $key = "SYSTEM\\CurrentControlSet\\Services\\W32Time\\Parameters" #path/name of the reg key
    }
	process {
		$timesource = '' | select "computer","NTPServer","Type" #Create a custom object properties "computer", "NTPServer" and "Type"		
		$timesource.computer = $server #set the computer property on the custom object		
		$reg = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey('LocalMachine', $server) #connect to the remote registry
		$regKey= $reg.OpenSubKey($key) #get the registry key		
		$str_value1 = $regKey.GetValue("NTPServer")	#get the value of "NTPServer"	
		$value1 = $str_value1 -split ","		
		$timesource.NTPServer = $value1[0] #split the value and set the NTPServer property on the custom object to the first returned timesource.		
		$value2 = $regKey.GetValue("Type") #get the value of "Type"		
		$timesource.Type = $value2 #set the Type property on the custom object		
		$reg.close() #close the remote registry connection when done.
		return $timesource #return the custom object.
	}
}
function Get-TimeZone {
	#############################################################################################
	##Function:			Get-RemoteTimeZone
	##
	##Description:		Pipeline function. Gets the configured time zone from the 
	#+					remote server using WMI.
	##
	##Created by:		Noam Wajnman
	##Creation Date:	31 July, 2014	
	##############################################################################################
	[CmdletBinding()]
	[OutputType([System.String])]
	param (
		$server
	)
	process {
		$timeZone = (gwmi win32_timeZone -ComputerName $server).caption	
		return $timeZone
	}
	
}
function Get-Time {
	#############################################################################################
	##Function:			Get-RemoteTime
	##
	##Description:		Pipeline function. Gets the time from the remote server using WMI.
	##
	##Created by:		Noam Wajnman
	##Creation Date:	31 July, 2014	
	##############################################################################################
	[CmdletBinding()]
	[OutputType([datetime])]
	param (
		$server
	)
	process {
		$remoteOSInfo = gwmi win32_OperatingSystem -computername $server	
		[datetime]$remoteDateTime = $remoteOSInfo.convertToDatetime($remoteOSInfo.LocalDateTime)	
		return $remoteDateTime
	}
}
function Test-PortAlive {
	#############################################################################################
	##Function:			Test-PortAlive
	##
	##Description:		Tests connection on a given server on a given port.
	##
	##Created by:		Noam Wajnman
	##Creation Date:	April 02, 2014	
	##############################################################################################
	[CmdletBinding()]
	[OutputType([System.boolean])]
	param(
		[Parameter(ValueFromPipeline=$true)][System.String[]]$server,
		[int]$port
	)
	$socket = new-object Net.Sockets.TcpClient
	$connect = $socket.BeginConnect($server, $port, $null, $null)
	$NoTimeOut = $connect.AsyncWaitHandle.WaitOne(500, $false)
	if ($NoTimeOut) {
		$socket.EndConnect($connect) | Out-Null
		return $true				
	}
	else {
		return $false
	}
}
#VARIABLES
$scriptpath = $MyInvocation.MyCommand.Path
$dir = Split-Path $scriptpath
$servers = gc "$dir\servers.txt"
$CSV = "$dir\ServersTimeInfo.csv"
#SCRIPT MAIN
clear
$TimeInfo = @()
$servers | % {
	$alive_rpc = Test-PortAlive -server $_ -port 135
	$alive_smb = Test-PortAlive -server $_ -port 445
	if ($alive_rpc -and $alive_smb) {
		Write-Host "Getting time info from $_"	
		$RemoteTimeInfo = '' | select "computer","NTPServer","NTP_Type","TimeZone","Time"
		$RemoteTimeInfo.computer = $_
		$timesourceInfo = Get-TimeSource -server $_	
		$RemoteTimeInfo.NTPServer = $timesourceInfo.NTPServer
		$RemoteTimeInfo.NTP_Type = $timesourceInfo.Type
		$RemoteTimeInfo.TimeZone = Get-TimeZone -server $_
		$RemoteTimeInfo.Time = Get-Time -server $_		
		$TimeInfo += $RemoteTimeInfo
	}
	else {
		Write-Host "Error - Couldn't get WMI info from $_"
	}
}
$TimeInfo | Export-Csv $CSV -NoTypeInformation

How do I run the script?

Save the script to your computer as Get-RemoteTime.ps1.
Create servers.txt and place it in the same directory as the script. Populate servers.txt with the names of the servers you wish to get time information from and save. You can now open a powershell prompt, browse to the script directory and execute it. You can also simply open the script in an editor such as powershell ISE or PowerGUI and run it by pressing F5.
That’s it! I hope you find the script useful 🙂