#requires -version 2
<#
.SYNOPSIS
Script can be used to report or setup NTP configuration on all vSphere hosts in given cluster
.DESCRIPTION
Script takes vCenter Server name and host cluster name as mandatory parameters, NTPSources parameter is optional.
If only mandatory parameters are provided script generates report aobut NTP settings for all vSphere hosts that are
connected to cluster. Report include ntp service status, policy, up to 5 ntp servers configured and calculated time difference
between host and system where the script is invoked. For consistent results script should be run from vCenter Server.
Optional parameter NTPSources is a comma-separated list of ntp servers that will be configured in cluster.
If NTPSources paramter is provided the script will configure ntp service to start together with host ("on" policy),
configure ntp servers provided, set the time manually (to avoid drift problems) and restart ntpd.
.PARAMETER vCenterServer
Mandatory parameter indicating vCenter server to connect to (FQDN or IP address)
.PARAMETER ClusterName
Mandatory parameter indicating host cluster name where vms need to be reconfigured
.PARAMETER NTPSources
Optional parameter indicating NTP servers that will be used
.EXAMPLE
To configure two NTP servers provide all parameters.
vmhost-timekeeping.ps1 -vCenterServer vcenter.seba.local -ClusterName Production-Cluster -NTPSources "time01.seba.local,time02.seba.local,10.0.0.1"
.EXAMPLE
If you provide -NTPSources only the script will ask for mandatory parameters
vmhost-timekeeping.ps1 -NTPSources "time01.seba.local,time02.seba.local,10.0.0.1"
.EXAMPLE
To generate report about NTP service status provide only mandatory parameters.
vmhost-timekeeping.ps1 -vcenter 10.0.0.1 -cluster tdq-cluster
.EXAMPLE
Script will interactively ask for two mandatory parameters, no changes will be made, only report will be created.
vmhost-timekeeping.ps1
#>
[CmdletBinding()]
Param(
[Parameter(Mandatory=$True,Position=1)]
[string]$vCenterServer,
[Parameter(Mandatory=$True, Position=2)]
[string]$ClusterName,
[Parameter(Mandatory=$False, Position=3)]
[string]$NTPSources=""
)
Function Write-And-Log {
[CmdletBinding()]
Param(
[Parameter(Mandatory=$True,Position=1)]
[string]$LogFile,
[Parameter(Mandatory=$True,Position=2)]
[string]$line,
[Parameter(Mandatory=$False,Position=3)]
[int]$ErrorCount=0,
[Parameter(Mandatory=$False,Position=4)]
[string]$type="terse"
)
$LogEntry = (Get-Date -Format ("[yyyy-MM-dd HH:mm:ss] ")) + $line
$ui = (Get-Host).UI.RawUI
if ($ErrorCount) {
$ui.ForegroundColor = "red"
$LogEntry = ">>> ERROR <<< " + $LogEntry
Write-Output $LogEntry
$LogEntry | Out-File $LogFile -Append
}
else {
$ui.ForegroundColor = "green"
if ($type -ne "terse"){
Write-Output $LogEntry
$LogEntry | Out-file $LogFile -Append
}
else {
Write-Output $LogEntry
}
}
$ui.ForegroundColor = "white"
}
#constans
$maxtimedrift = 1
#variables
$ScriptRoot = Split-Path $MyInvocation.MyCommand.Path
$StartTime = Get-Date -Format "yyyyMMddHHmmss_"
$csvoutfile = $ScriptRoot + "\" + $StartTime+ "time_config_report_for_$($ClusterName)_cluster.csv"
$logfilename = $ScriptRoot + "\" + $StartTime + "vmhost-timekeeping.log"
$transcriptfilename = $ScriptRoot + "\" + $StartTime + "vmhost-timekeeping_Transcript.log"
$all_vmhosts_timeconfig_info = @()
$total_errors = 0
$total_vmhosts = 0
#start PowerShell transcript
Start-Transcript -Path $transcriptfilename
#load PowerCLI snap-in
$vmsnapin = Get-PSSnapin VMware.VimAutomation.Core -ErrorAction SilentlyContinue
$Error.Clear()
if ($vmsnapin -eq $null)
{
Add-PSSnapin VMware.VimAutomation.Core
if ($error.Count -eq 0)
{
write-and-log $logfilename "PowerCLI VimAutomation.Core Snap-in was successfully enabled." 0 "full"
}
else
{
write-and-log $logfilename "Could not enable PowerCLI VimAutomation.Core Snap-in, exiting script" 1 "full"
Exit
}
}
else
{
write-and-log $logfilename "PowerCLI VimAutomation.Core Snap-in is already enabled" 0 "full"
}
#check PowerCLI version
if (($vmsnapin.Version.Major -gt 5) -or (($vmsnapin.version.major -eq 5) -and ($vmsnapin.version.minor -ge 1))) {
#assume everything is OK at this point
$Error.Clear()
#connect vCenter from parameter
Connect-VIServer -Server $vCenterServer -ErrorAction SilentlyContinue | Out-Null
#execute only if connection successful
if ($error.Count -eq 0){
#measuring execution time is really hip these days
$stop_watch = [Diagnostics.Stopwatch]::StartNew()
#use previously defined function to inform what is going on, anything else than "terse" will cause the message to be written both in logfile and to screen
Write-And-Log $logfilename "vCenter $vCenterServer successfully connected" $error.count "full"
#get all reachable vmhosts in cluster
$vmhosts_in_cluster = get-vmhost -location $ClusterName | where-object { ($_.connectionstate -eq "connected") -or ($_.connectionstate -eq "maintenance") }
#only if we've found some vmhosts
if ($vmhosts_in_cluster){
#if no NTP server given - create report only
if ($NTPSources -eq ""){
$mode = "checked"
foreach ($vmhost in $vmhosts_in_cluster){
#all OK here
$error.Clear()
$total_vmhosts += 1
#display nice progress bar in PowerCLI window
write-progress -Activity "Gathering host NTP config report" -Status "Percent complete" -PercentComplete (($total_vmhosts / $vmhosts_in_cluster.count) * 100) -CurrentOperation "$("{0:N2}" -f (($total_vmhosts / $vmhosts_in_cluster.count) * 100))% complete"
#retrieve NTPD information
$single_vmhosts_timeconfig_info = New-Object PSObject
$single_vmhosts_timeconfig_info | Add-Member -Name "VmHostName" -Value $vmhost.name -MemberType NoteProperty
$single_vmhosts_timeconfig_info | Add-Member -Name "VmHostTZ" -Value $vmhost.TimeZone -MemberType NoteProperty
$ntpservice = $vmhost | get-vmhostservice | Where-Object {$_.key -eq "ntpd"}
$single_vmhosts_timeconfig_info | Add-Member -Name "NTPDisRunning" -Value $ntpservice.running -MemberType NoteProperty
$single_vmhosts_timeconfig_info | Add-Member -Name "NTPDPolicy" -Value $ntpservice.policy -MemberType NoteProperty
#retrieve NTP Servers configured, report only first 5
$ntpserver = @($vmhost | get-vmhostntpserver)
for ($index = 0; $index -lt 5; $index++){
if ($ntpserver[$index]){
$single_vmhosts_timeconfig_info | Add-Member -Name "NTPServer$($index)" -Value $ntpserver[$index] -MemberType NoteProperty
}
else{
$single_vmhosts_timeconfig_info | Add-Member -Name "NTPServer$($index)" -Value "none" -MemberType NoteProperty
}
}
#calculate time difference between host and system this script is invoked from
$hosttimesystem = get-view $vmhost.ExtensionData.ConfigManager.DateTimeSystem
$timedrift = ($hosttimesystem.QueryDateTime() - [DateTime]::UtcNow).TotalSeconds
#raise alarm if difference bigger than acceptable
if([math]::abs($timedrift) -gt $maxtimedrift){
Write-And-Log $logfilename "Time difference exceeded for host $($vmhost.name)!" 1 "full"
Write-And-Log $logfilename "Acceptable difference: $("{0:N2}" -f $maxtimedrift)s Current difference: $("{0:N2}" -f $timedrift)s" 1 "full"
$total_errors++
}
$single_vmhosts_timeconfig_info | Add-Member -Name "TimeDrift" -Value $timedrift -MemberType NoteProperty
$all_vmhosts_timeconfig_info += $single_vmhosts_timeconfig_info
$total_errors += $error.Count
Write-And-Log $logfilename "Host $($vmhost.name) added to report" $error.Count "terse"
}
#export to CSV
$all_vmhosts_timeconfig_info | Export-Csv -Path $csvoutfile -NoTypeInformation
Write-And-Log $logfilename "Report created in $($csvoutfile)" $total_errors "full"
}
#if NTP servers provided - configure them
else {
#give the engineer invoking the script chance to abort
Write-And-Log $logfilename "NTP configuration for all vSphere hosts in cluster $ClusterName will be RESET" 1 "full"
Write-And-Log $logfilename "This is your LAST CHANCE TO ABORT" 1 "full"
Write-And-Log $logfilename "Press Y + ENTER to continue" 0 "full"
Write-And-Log $logfilename "Press any other key + ENTER to ABORT..." 1 "full"
$response = read-host
if ( $response -ne "Y" ) {
write-and-log $logfilename "Operation ABORTED, no changes have been made to NTP settings" 1 "full"
#exit
} else {
#let's sanitize input a little and leave only NTP servers that respond to ping (from system where this script is invoked!)
$NTPSourcesArray = $NTPSources.Split(",") | Where-Object { Test-Connection -ComputerName $_ -Quiet -Count 1}
$mode = "configured"
#make sure we've got some NTP servers left
if ($NTPSourcesArray){
foreach ($vmhost in $vmhosts_in_cluster){
#all OK here
$error.Clear()
$total_vmhosts += 1
#display nice progress bar in PowerCLI window
write-progress -Activity "Configuring NTP for hosts" -Status "Percent complete" -PercentComplete (($total_vmhosts / $vmhosts_in_cluster.count) * 100) -CurrentOperation "$("{0:N2}" -f (($total_vmhosts / $vmhosts_in_cluster.count) * 100))% complete"
#stop ntp service on host
$ntpservice = $vmhost | get-vmhostservice | Where-Object {$_.key -eq "ntpd"}
stop-vmhostservice -HostService $ntpservice -confirm:$False | out-null
#clear current NTP servers
$current_NTPSources = @($vmhost | get-vmhostntpserver)
foreach ($current_NTPSource in $current_NTPSources){
remove-vmhostntpserver -ntpserver $current_NTPSource -vmhost $vmhost -confirm:$false | Out-Null
}
#and set new NTP servers
foreach ($NTPSource in $NTPSourcesArray) {
add-vmhostntpserver -ntpserver $NTPSource -vmhost $vmhost -confirm:$False | out-null
}
#set service policy to start and stop with host
set-vmhostservice -HostService $ntpservice -Policy "on" -confirm:$False | out-null
#set vmhost time manually (to avoid problem with too big drift) to match time of system where script is invoked
$hosttimesystem = get-view $vmhost.ExtensionData.ConfigManager.DateTimeSystem
$hosttimesystem.UpdateDateTime([DateTime]::UtcNow)
#finally - start NTP on vmhost
start-vmhostservice -HostService $ntpservice -confirm:$False | out-null
$total_errors += $error.Count
Write-And-Log $logfilename "Host $($vmhost.name) NTP configuration changed" $error.Count "terse"
}
}
else{
Write-And-Log $logfilename "None of NTP servers provided ($NTPSources) is responding, exiting" 1 "full"
$total_errors++
}
}
}
}
else {
$total_errors += $Error.Count
}
$stop_watch.Stop()
$elapsed_seconds = ($stop_watch.elapsedmilliseconds)/1000
#farewell message before disconnect
Write-And-Log $logfilename "Total of $total_vmhosts hosts $mode in $("{0:N2}" -f $elapsed_seconds)s, $total_errors ERRORS reported, exiting" $total_errors "full"
#disconnect vCenter
Disconnect-VIServer -Confirm:$false -Force:$true
}
else {
Write-And-Log $logfilename "Error connecting vCenter server $vCenterServer, exiting" $error.count "full"
}
}
else {
write-and-log $logfilename "This script requires PowerCLI 5.1 or greater to run properly" 1 "full"
}
Stop-Transcript
<#
.SYNOPSIS
Script can be used to report or setup NTP configuration on all vSphere hosts in given cluster
.DESCRIPTION
Script takes vCenter Server name and host cluster name as mandatory parameters, NTPSources parameter is optional.
If only mandatory parameters are provided script generates report aobut NTP settings for all vSphere hosts that are
connected to cluster. Report include ntp service status, policy, up to 5 ntp servers configured and calculated time difference
between host and system where the script is invoked. For consistent results script should be run from vCenter Server.
Optional parameter NTPSources is a comma-separated list of ntp servers that will be configured in cluster.
If NTPSources paramter is provided the script will configure ntp service to start together with host ("on" policy),
configure ntp servers provided, set the time manually (to avoid drift problems) and restart ntpd.
.PARAMETER vCenterServer
Mandatory parameter indicating vCenter server to connect to (FQDN or IP address)
.PARAMETER ClusterName
Mandatory parameter indicating host cluster name where vms need to be reconfigured
.PARAMETER NTPSources
Optional parameter indicating NTP servers that will be used
.EXAMPLE
To configure two NTP servers provide all parameters.
vmhost-timekeeping.ps1 -vCenterServer vcenter.seba.local -ClusterName Production-Cluster -NTPSources "time01.seba.local,time02.seba.local,10.0.0.1"
.EXAMPLE
If you provide -NTPSources only the script will ask for mandatory parameters
vmhost-timekeeping.ps1 -NTPSources "time01.seba.local,time02.seba.local,10.0.0.1"
.EXAMPLE
To generate report about NTP service status provide only mandatory parameters.
vmhost-timekeeping.ps1 -vcenter 10.0.0.1 -cluster tdq-cluster
.EXAMPLE
Script will interactively ask for two mandatory parameters, no changes will be made, only report will be created.
vmhost-timekeeping.ps1
#>
[CmdletBinding()]
Param(
[Parameter(Mandatory=$True,Position=1)]
[string]$vCenterServer,
[Parameter(Mandatory=$True, Position=2)]
[string]$ClusterName,
[Parameter(Mandatory=$False, Position=3)]
[string]$NTPSources=""
)
Function Write-And-Log {
[CmdletBinding()]
Param(
[Parameter(Mandatory=$True,Position=1)]
[string]$LogFile,
[Parameter(Mandatory=$True,Position=2)]
[string]$line,
[Parameter(Mandatory=$False,Position=3)]
[int]$ErrorCount=0,
[Parameter(Mandatory=$False,Position=4)]
[string]$type="terse"
)
$LogEntry = (Get-Date -Format ("[yyyy-MM-dd HH:mm:ss] ")) + $line
$ui = (Get-Host).UI.RawUI
if ($ErrorCount) {
$ui.ForegroundColor = "red"
$LogEntry = ">>> ERROR <<< " + $LogEntry
Write-Output $LogEntry
$LogEntry | Out-File $LogFile -Append
}
else {
$ui.ForegroundColor = "green"
if ($type -ne "terse"){
Write-Output $LogEntry
$LogEntry | Out-file $LogFile -Append
}
else {
Write-Output $LogEntry
}
}
$ui.ForegroundColor = "white"
}
#constans
$maxtimedrift = 1
#variables
$ScriptRoot = Split-Path $MyInvocation.MyCommand.Path
$StartTime = Get-Date -Format "yyyyMMddHHmmss_"
$csvoutfile = $ScriptRoot + "\" + $StartTime+ "time_config_report_for_$($ClusterName)_cluster.csv"
$logfilename = $ScriptRoot + "\" + $StartTime + "vmhost-timekeeping.log"
$transcriptfilename = $ScriptRoot + "\" + $StartTime + "vmhost-timekeeping_Transcript.log"
$all_vmhosts_timeconfig_info = @()
$total_errors = 0
$total_vmhosts = 0
#start PowerShell transcript
Start-Transcript -Path $transcriptfilename
#load PowerCLI snap-in
$vmsnapin = Get-PSSnapin VMware.VimAutomation.Core -ErrorAction SilentlyContinue
$Error.Clear()
if ($vmsnapin -eq $null)
{
Add-PSSnapin VMware.VimAutomation.Core
if ($error.Count -eq 0)
{
write-and-log $logfilename "PowerCLI VimAutomation.Core Snap-in was successfully enabled." 0 "full"
}
else
{
write-and-log $logfilename "Could not enable PowerCLI VimAutomation.Core Snap-in, exiting script" 1 "full"
Exit
}
}
else
{
write-and-log $logfilename "PowerCLI VimAutomation.Core Snap-in is already enabled" 0 "full"
}
#check PowerCLI version
if (($vmsnapin.Version.Major -gt 5) -or (($vmsnapin.version.major -eq 5) -and ($vmsnapin.version.minor -ge 1))) {
#assume everything is OK at this point
$Error.Clear()
#connect vCenter from parameter
Connect-VIServer -Server $vCenterServer -ErrorAction SilentlyContinue | Out-Null
#execute only if connection successful
if ($error.Count -eq 0){
#measuring execution time is really hip these days
$stop_watch = [Diagnostics.Stopwatch]::StartNew()
#use previously defined function to inform what is going on, anything else than "terse" will cause the message to be written both in logfile and to screen
Write-And-Log $logfilename "vCenter $vCenterServer successfully connected" $error.count "full"
#get all reachable vmhosts in cluster
$vmhosts_in_cluster = get-vmhost -location $ClusterName | where-object { ($_.connectionstate -eq "connected") -or ($_.connectionstate -eq "maintenance") }
#only if we've found some vmhosts
if ($vmhosts_in_cluster){
#if no NTP server given - create report only
if ($NTPSources -eq ""){
$mode = "checked"
foreach ($vmhost in $vmhosts_in_cluster){
#all OK here
$error.Clear()
$total_vmhosts += 1
#display nice progress bar in PowerCLI window
write-progress -Activity "Gathering host NTP config report" -Status "Percent complete" -PercentComplete (($total_vmhosts / $vmhosts_in_cluster.count) * 100) -CurrentOperation "$("{0:N2}" -f (($total_vmhosts / $vmhosts_in_cluster.count) * 100))% complete"
#retrieve NTPD information
$single_vmhosts_timeconfig_info = New-Object PSObject
$single_vmhosts_timeconfig_info | Add-Member -Name "VmHostName" -Value $vmhost.name -MemberType NoteProperty
$single_vmhosts_timeconfig_info | Add-Member -Name "VmHostTZ" -Value $vmhost.TimeZone -MemberType NoteProperty
$ntpservice = $vmhost | get-vmhostservice | Where-Object {$_.key -eq "ntpd"}
$single_vmhosts_timeconfig_info | Add-Member -Name "NTPDisRunning" -Value $ntpservice.running -MemberType NoteProperty
$single_vmhosts_timeconfig_info | Add-Member -Name "NTPDPolicy" -Value $ntpservice.policy -MemberType NoteProperty
#retrieve NTP Servers configured, report only first 5
$ntpserver = @($vmhost | get-vmhostntpserver)
for ($index = 0; $index -lt 5; $index++){
if ($ntpserver[$index]){
$single_vmhosts_timeconfig_info | Add-Member -Name "NTPServer$($index)" -Value $ntpserver[$index] -MemberType NoteProperty
}
else{
$single_vmhosts_timeconfig_info | Add-Member -Name "NTPServer$($index)" -Value "none" -MemberType NoteProperty
}
}
#calculate time difference between host and system this script is invoked from
$hosttimesystem = get-view $vmhost.ExtensionData.ConfigManager.DateTimeSystem
$timedrift = ($hosttimesystem.QueryDateTime() - [DateTime]::UtcNow).TotalSeconds
#raise alarm if difference bigger than acceptable
if([math]::abs($timedrift) -gt $maxtimedrift){
Write-And-Log $logfilename "Time difference exceeded for host $($vmhost.name)!" 1 "full"
Write-And-Log $logfilename "Acceptable difference: $("{0:N2}" -f $maxtimedrift)s Current difference: $("{0:N2}" -f $timedrift)s" 1 "full"
$total_errors++
}
$single_vmhosts_timeconfig_info | Add-Member -Name "TimeDrift" -Value $timedrift -MemberType NoteProperty
$all_vmhosts_timeconfig_info += $single_vmhosts_timeconfig_info
$total_errors += $error.Count
Write-And-Log $logfilename "Host $($vmhost.name) added to report" $error.Count "terse"
}
#export to CSV
$all_vmhosts_timeconfig_info | Export-Csv -Path $csvoutfile -NoTypeInformation
Write-And-Log $logfilename "Report created in $($csvoutfile)" $total_errors "full"
}
#if NTP servers provided - configure them
else {
#give the engineer invoking the script chance to abort
Write-And-Log $logfilename "NTP configuration for all vSphere hosts in cluster $ClusterName will be RESET" 1 "full"
Write-And-Log $logfilename "This is your LAST CHANCE TO ABORT" 1 "full"
Write-And-Log $logfilename "Press Y + ENTER to continue" 0 "full"
Write-And-Log $logfilename "Press any other key + ENTER to ABORT..." 1 "full"
$response = read-host
if ( $response -ne "Y" ) {
write-and-log $logfilename "Operation ABORTED, no changes have been made to NTP settings" 1 "full"
#exit
} else {
#let's sanitize input a little and leave only NTP servers that respond to ping (from system where this script is invoked!)
$NTPSourcesArray = $NTPSources.Split(",") | Where-Object { Test-Connection -ComputerName $_ -Quiet -Count 1}
$mode = "configured"
#make sure we've got some NTP servers left
if ($NTPSourcesArray){
foreach ($vmhost in $vmhosts_in_cluster){
#all OK here
$error.Clear()
$total_vmhosts += 1
#display nice progress bar in PowerCLI window
write-progress -Activity "Configuring NTP for hosts" -Status "Percent complete" -PercentComplete (($total_vmhosts / $vmhosts_in_cluster.count) * 100) -CurrentOperation "$("{0:N2}" -f (($total_vmhosts / $vmhosts_in_cluster.count) * 100))% complete"
#stop ntp service on host
$ntpservice = $vmhost | get-vmhostservice | Where-Object {$_.key -eq "ntpd"}
stop-vmhostservice -HostService $ntpservice -confirm:$False | out-null
#clear current NTP servers
$current_NTPSources = @($vmhost | get-vmhostntpserver)
foreach ($current_NTPSource in $current_NTPSources){
remove-vmhostntpserver -ntpserver $current_NTPSource -vmhost $vmhost -confirm:$false | Out-Null
}
#and set new NTP servers
foreach ($NTPSource in $NTPSourcesArray) {
add-vmhostntpserver -ntpserver $NTPSource -vmhost $vmhost -confirm:$False | out-null
}
#set service policy to start and stop with host
set-vmhostservice -HostService $ntpservice -Policy "on" -confirm:$False | out-null
#set vmhost time manually (to avoid problem with too big drift) to match time of system where script is invoked
$hosttimesystem = get-view $vmhost.ExtensionData.ConfigManager.DateTimeSystem
$hosttimesystem.UpdateDateTime([DateTime]::UtcNow)
#finally - start NTP on vmhost
start-vmhostservice -HostService $ntpservice -confirm:$False | out-null
$total_errors += $error.Count
Write-And-Log $logfilename "Host $($vmhost.name) NTP configuration changed" $error.Count "terse"
}
}
else{
Write-And-Log $logfilename "None of NTP servers provided ($NTPSources) is responding, exiting" 1 "full"
$total_errors++
}
}
}
}
else {
$total_errors += $Error.Count
}
$stop_watch.Stop()
$elapsed_seconds = ($stop_watch.elapsedmilliseconds)/1000
#farewell message before disconnect
Write-And-Log $logfilename "Total of $total_vmhosts hosts $mode in $("{0:N2}" -f $elapsed_seconds)s, $total_errors ERRORS reported, exiting" $total_errors "full"
#disconnect vCenter
Disconnect-VIServer -Confirm:$false -Force:$true
}
else {
Write-And-Log $logfilename "Error connecting vCenter server $vCenterServer, exiting" $error.count "full"
}
}
else {
write-and-log $logfilename "This script requires PowerCLI 5.1 or greater to run properly" 1 "full"
}
Stop-Transcript
No comments:
Post a Comment