<# #> [CmdletBinding()] param ( # Array of strings listing computer name(s) to wake up. Names will be matched using RegEx [Alias("Computer")] [String[]] $TargetComputer = "", # XML database file containing computer details (Default: WOLDatabase.xml) [Alias("List", "Database")] [String] $WolDatabase = "WOLDatabase.xml", # Skip connectivity check [Alias("NoCheck", "NoReadiness")] [Switch] $NoConnectivityCheck, # Delay before running first connectivity check (Default: 10) [Alias("Delay")] [Int] $ConnectivityCheckDelay = 10, # Seconds before aborting connectivity check (Default: 300) [Alias("Timeout")] [Int] $ConnectivityCheckTimeout = 300, # Seconds between connectivity checks (Default: 30) [Alias("Interval")] [Int] $ConnectivityCheckInterval = 30, # Port to use for connectivity check (Default: 3389-RDP) [Alias("CheckPort")] [Int] $ConnectivityCheckPort = 3389 ) function exitError($errMessage, $PSItem, $exitCode = 1) { if ( [String]::IsNullOrEmpty($errMessage)) { Write-Host -ForegroundColor Red "`n`rABNORMAL EXIT: exitError function called without exit message!`n`r" exit 99 } Write-Host -ForegroundColor Red "`r`nERROR: ${errMessage}." if ($PSItem) { Write-Host -ForegroundColor Red "`r`nAdditional information:`r`n$PSItem" } Write-Host Exit $exitCode } function exitGracefully() { Write-Host -ForegroundColor Green "`r`nFinished.`r`n" Exit 0 } # constants Set-Variable -Name wolModuleName -Value "wol-magicPacket" -Option Constant # exit on error if Send-MagicPacket module is not loaded if (!(Get-Module -Name $wolModuleName)) { try { Import-Module -Name $wolModuleName -ErrorAction Stop } catch { $errMessage = "Unable to load '$wolModuleName'" exitError $errMessage $PSItem } } # exit if database cannot be found/read if (!(Test-Path -Path $WolDatabase -PathType Leaf)) { $errMessage = "Unable to find or read Wake-On-LAN database file ($WolDatabase)" exitError $errMessage } # get target computer name if not already specified if ( [String]::IsNullOrWhiteSpace($TargetComputer)) { do { $TargetComputer = Read-Host -Prompt 'Computer to wake-up' } while ( [String]::IsNullOrWhiteSpace($TargetComputer)) } Write-Host # read database and assemble list of target computers [xml]$wolDb = Get-Content -Path $WolDatabase $broadcastIP = $wolDb.WOLDatabase.Configuration.BroadcastAddress $port = $wolDb.WOLDatabase.Configuration.Port $dnsSuffix = $wolDb.WOLDatabase.Configuration.DnsSuffix $targetComputers = [System.Collections.Generic.List[PSObject]]::new() $TargetComputer | ForEach-Object { $tgt = $_ $wolDb.WOLDatabase.Computers.Computer | Where-Object { $_.name -match $tgt } | ForEach-Object { $targetComputers.Add($_) } } $removeFromTargetComputers = [System.Collections.Generic.List[String]]::new() # exit if nothing to do (i.e. empty targetComputers list) if ($targetComputers.Count -eq 0) { Write-Host -ForegroundColor Yellow "No computers found matching '$TargetComputer'. Nothing to do.`r`n" exit 0 } # send WOL magic packets $targetComputers | ForEach-Object { $wolError = @() $name = $_.name $friendlyName = $_.friendlyName if ( [String]::IsNullOrWhiteSpace($friendlyName)) { Write-Host "Processing request to wake-up '$name'... " -NoNewline } else { Write-Host "Processing request to wake-up '$friendlyName'... " -NoNewline } # send magic packet $_.mac | Send-MagicPacket -BroadcastIP $broadcastIP -Port $port -ErrorAction SilentlyContinue -ErrorVariable +wolError if ($wolError.Count -eq 0) { Write-Host -ForegroundColor Green "[OK]" } else { Write-Host -ForegroundColor Red "[ERROR]" # queue for removal from computers on which to run a connectivity check $removeFromTargetComputers.Add($name) } } # remove computers from targetComputers if WOL packet was not successfully sent $removeFromTargetComputers | ForEach-Object { $removeName = $_ $targetComputers.Remove(($targetComputers | Where-Object { $_.name -eq $removeName })) | Out-Null } # exit if connectivity check skipped or if no computers to test if ($NoConnectivityCheck) { Write-Host "`r`nSkipping connectivity checks." exitGracefully } elseif ($targetComputers.Count -eq 0) { exitError "No wake-up packets sent successfully" -exitCode 5 } # wait for initial delay seconds to let computer(s) wake up for ($i = $ConnectivityCheckDelay; $i -gt 0; $i--) { Write-Progress -Activity "Waiting for computer(s) to wake-up..." -SecondsRemaining $i Start-Sleep 1 } Write-Progress -Activity "Waiting for computer(s) to wake-up..." -Completed # iterate computers and test connectivity # TODO: run connectivity test as background job $connCheckTotalTime = 0 $removeFromTargetComputers.Clear() do { $targetComputers | ForEach-Object { $name = $_.name $friendlyName = $_.friendlyName $fqdn = -join ($name, $dnsSuffix) $connectionError = @() if ( [String]::IsNullOrWhiteSpace($friendlyName)) { Write-Host "Testing connection readiness of '$name'... " -NoNewline } else { Write-Host "Testing connection readiness of '$friendlyName'... " -NoNewline } if (!(Test-NetConnection -ComputerName $fqdn -Port $ConnectivityCheckPort -InformationLevel Quiet -WarningAction SilentlyContinue -ErrorAction SilentlyContinue -ErrorVariable +connectionError)) { if (($connCheckTotalTime + $ConnectivityCheckInterval) -gt $ConnectivityCheckTimeout) { # last iteration, display 'error' Write-Host -ForegroundColor Red "[ERROR]" $connectionError | ForEach-Object { $errMsg = $_.ToString() Write-Verbose "`t(Additional information: $errMsg)" } } else { # more iterations pending, display 'retrying' Write-Host -ForegroundColor Yellow "[RETRYING]" } } else { Write-Host -ForegroundColor Green "[OK]" # queue for removal from computers to check next round $removeFromTargetComputers.Add($name) } } # remove computers from targetComputers if connectivity check already successful $removeFromTargetComputers | ForEach-Object { $removeName = $_ $targetComputers.Remove(($targetComputers | Where-Object { $_.name -eq $removeName })) | Out-Null } # sleep until next test interval and increase time counter $connCheckTotalTime += $ConnectivityCheckInterval if ($connCheckTotalTime -le $ConnectivityCheckTimeout -and $targetComputers.Count -gt 0) { for ($i = $ConnectivityCheckInterval; $i -gt 0; $i--) { Write-Progress -Activity "Waiting for next connectivity check..." -SecondsRemaining $i Start-Sleep 1 } Write-Progress -Activity "Waiting for next connectivity check..." -Completed } } while ($connCheckTotalTime -le $ConnectivityCheckTimeout -and $targetComputers.Count -gt 0) # exit gracefully exitGracefully