ping computers and list logged in user - powershell

i got a question to my existing script.
the script does exactly what it should to do
This is what i got.
<#
Ping mass huge amount of hosts in parallel multithreading
#>
function Parallel-Ping {
[CmdletBinding()]
param(
[parameter(mandatory=$true)][ValidateNotNullOrEmpty()][string[]]$hosts,
[parameter(mandatory=$false)][ValidateNotNullOrEmpty()][int]$timeout = 2000,
[parameter(mandatory=$false)][ValidateNotNullOrEmpty()][ValidateRange(1,255)][int]$ttl = 128,
[parameter(mandatory=$false)][ValidateNotNullOrEmpty()][bool]$DontFragment = $false,
[parameter(mandatory=$false)][ValidateNotNullOrEmpty()][byte[]]$data = ([byte[]]0),
[parameter(mandatory=$false)][ValidateNotNullOrEmpty()][int]$MaxConcurrent = [System.Environment]::ProcessorCount,
[parameter(mandatory=$false)][ValidateNotNullOrEmpty()][switch]$showprogress
)
begin{
$rspool = [runspacefactory]::CreateRunspacePool(1,$MaxConcurrent)
$rspool.ApartmentState = 'STA'
$rspool.Open()
$jobs = New-Object System.Collections.ArrayList
}
process{
foreach ($hostname in $hosts){
$ps = [Powershell]::Create()
$ps.RunspacePool = $rspool
[void]$ps.AddScript({
param($hostname,$timeout,$ttl,$DontFragment,$data)
$result = [ordered]#{Host=$hostname;Address=$null;Status=[System.Net.NetworkInformation.IPStatus]::Unknown;RoundtripTime = -1;TTL=$ttl;DontFragment=$DontFragment;ResponseBuffer='';Time=(get-date)}
$ping = New-Object System.Net.NetworkInformation.Ping
try{
$reply = $ping.Send($hostname,$timeout,$data,(New-Object System.Net.NetworkInformation.PingOptions($ttl,$DontFragment)))
$result.RoundtripTime = $reply.RoundtripTime
$result.Status = $reply.Status
$result.Address = $reply.Address
$result.ResponseBuffer = $reply.Buffer
}catch [System.Net.NetworkInformation.PingException] {
$result.Status = [System.Net.NetworkInformation.IPStatus]::DestinationHostUnreachable
}finally{
$ping.Dispose()
}
[pscustomobject]$result
}).AddParameters(#($hostname,$timeout,$ttl,$DontFragment,$data))
$job = $ps.BeginInvoke()
[void]$jobs.Add(([pscustomobject]#{Handle = $job; Powershell = $ps}))
}
}
end{
write-verbose "Waiting for all jobs to complete."
while(($jobs | ?{!$_.Handle.IsCompleted})){
if ($showprogress.IsPresent){
$completed = ($jobs | ?{$_.Handle.IsCompleted}).Count
Write-Progress -Activity $PSCmdlet.MyInvocation.InvocationName -Status "Pinging a total of $($hosts.Count) hosts." -PercentComplete (($completed / $hosts.Count) * 100) -CurrentOperation "Completed $completed from $($hosts.Count)."
}
}
# get results of jobs
$results = $jobs | %{
$_.Powershell.EndInvoke($_.handle)
$_.Powershell.Dispose()
}
# cleanup
$rspool.Close();$rspool.Dispose()
$results
}
}
$hosts = Get-Content 'E:\ips.txt'
Parallel-Ping -hosts $hosts -timeout 500 -MaxConcurrent 50 -showprogress | ogv
I would like to mention another function in my script.
My goal is it, to add the function that i can see the actual logged in user to the pinged machines?
Unfortunately i dont know where to add.

Related

Coding with powershell

I'm new with powershell and i would like to use a loop to ping several Printers on my network.
My problem is : once i'm in the loop of pinging , i can't go out of the loop ...
I tried several things from google but without success ( start-stop , Timer ) . Does anybody have any idea?
Here is the code :
$BtnStartPingClicked = {
if ($LblFileSelectPing.Text -eq "*.txt") {
Add-Type -AssemblyName PresentationCore,PresentationFramework
$ButtonType = [System.Windows.MessageBoxButton]::OK
$MessageIcon = [System.Windows.MessageBoxImage]::Error
$MessageBody = "Please select a list of printer first"
$MessageTitle = "Error"
$Result = [System.Windows.MessageBox]::Show($MessageBody,$MessageTitle,$ButtonType,$MessageIcon)
Write-Host "Your choice is $Result"
}
else {
do {
$IPList = Get-Content ($LblFileSelectPing.Text)
$snmp = New-Object -ComObject olePrn.OleSNMP
$ping = New-Object System.Net.NetworkInformation.Ping
$i = 11
$j = 1
foreach ($Printer in $IPList) {
try {
$result = $ping.Send($Printer)
} catch {
$result = $null
}
if ($result.Status -eq 'Success') {
$((Get-Variable -name ("GBMachine"+$j+"Ping")).value).Visible = $True
$j++
test-Connection -ComputerName $Printer -Count 1 -Quiet
$printerip = $result.Address.ToString()
# OPEN SNMP CONNECTION TO PRINTER
$snmp.open($Printer, 'public', 2, 3000)
# MODEL
try {
$model = $snmp.Get('.1.3.6.1.2.1.25.3.2.1.3.1')
} catch {
$model = $null
}
# Serial
try {
$serial = $snmp.Get('.1.3.6.1.4.1.1602.1.2.1.8.1.3.1.1').toupper()
} catch {
$Dns = $null
}
# progress
$TBMonitoringPing.SelectionColor = "green"
$TBMonitoringPing.AppendText("$Printer is Pinging")
$TBMonitoringPing.AppendText("`n")
$mac = (arp -a $Printer | Select-String '([0-9a-f]{2}-){5}[0-9a-f]{2}').Matches.Value
# OPEN SNMP CONNECTION TO PRINTER
$((Get-Variable -name ('LblMach' + $i)).value).Text = "IP : $Printerip"
$i++
$((Get-Variable -name ('LblMach' + $i)).value).Text = "Model : $Model"
$i++
$((Get-Variable -name ('LblMach' + $i)).value).Text = "MAC : $mac"
$i++
$((Get-Variable -name ('LblMach' + $i)).value).Text = "Serial : $serial"
$TBAnswerMachine.AppendText("$Model")
$TBAnswerMachine.AppendText("`n")
$TBAnswerMachine.AppendText("$Printer - $Serial")
$TBAnswerMachine.AppendText("`n")
$TBAnswerMachine.AppendText("$Mac")
$TBAnswerMachine.AppendText("`n")
$TBAnswerMachine.AppendText("`n")
Get-Content ($LblFileSelectPing.Text) | Where-Object {$_ -notmatch $Printer} | Set-Content ("C:\_canonsoftware\out.txt")
$i = $i+7
$snmp.Close()
Start-Sleep -milliseconds 1000 # Take a breather!
}
else {
$TBMonitoringPing.selectioncolor = "red"
$TBMonitoringPing.AppendText("$Printer not pinging")
$TBMonitoringPing.AppendText("`n")
Start-Sleep -milliseconds 1000 # Take a breather!
}
}
$LblFileSelectPing.Text = "C:\_canonsoftware\out.txt"
} until($infinity)
}
}
thanks for your answers...
1 - part of my object are indeed not declared in the code because they are in my other PS1 file....
2 - I do a do until infinity because i don't want to stop the code before i decide it...
3 - I didn't explain my problem correctly ( excuse my poor english ) ... i would like to be able to go out of the loop do until at the moment i click on a stop button ... but apprently the windows doens't respond while in the loop ... i have to stop the script with powershell ... which is annoying because i'd like to make an executable with it ... and not have to go out of my program ...
thank you for your ideas

How to download a webstring from multiple web-servers in parallel via Powershell?

I need to download some webcontent from many servers in parallel as part of a scheduled job, but I cannot find a correct way to run the download in parallel/async. How can this be done?
Without any parallelism I can do it this way, but it is very slow:
$web = [System.Net.WebClient]::new()
[Net.ServicePointManager]::ServerCertificateValidationCallback = {$true}
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
# $srvList is a list of servers of viariable length
$allData = ""
foreach ($srv in $srvList) {
$url = "https:\\$srv\MyWebPage"
$data = $web.DownloadString($url)
$allData += $data
}
But how to do this in parallel via "$web.DownloadStringAsync"?
I found this snippet, but I dont see how to get the result of each call and how to concatenate it:
$job = Register-ObjectEvent -InputObject $web -EventName DownloadStringCompleted -Action {
Write-Host 'Download completed'
write-host $EventArgs.Result
}
$web.DownloadString($url)
Does someone know, how to get this solved in a short & smart way?
The best and fastest way is using runspaces:
Add-Type -AssemblyName System.Collections
$GH = [hashtable]::Synchronized(#{})
[System.Collections.Generic.List[PSObject]]$GH.results = [System.Collections.Generic.List[string]]::new()
[System.Collections.Generic.List[string]]$GH.servers = #('server1','server2');
[System.Collections.Generic.List[string]]$GH.functions = #('Download-Content');
[System.Collections.Generic.List[PSObject]]$jobs = #()
#-----------------------------------------------------------------
function Download-Content {
#-----------------------------------------------------------------
# a function which runs parallel
param(
[string]$server
)
$web = [System.Net.WebClient]::new()
[Net.ServicePointManager]::ServerCertificateValidationCallback = {$true}
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
$url = "https:\\$server\MyWebPage"
$data = $web.DownloadString($url)
$GH.results.Add( $data )
}
#-----------------------------------------------------------------
function Create-InitialSessionState {
#-----------------------------------------------------------------
param(
[System.Collections.Generic.List[string]]$functionNameList
)
# Setting up an initial session state object
$initialSessionState = [System.Management.Automation.Runspaces.InitialSessionState]::CreateDefault()
foreach( $functionName in $functionNameList ) {
# Getting the function definition for the functions to add
$functionDefinition = Get-Content function:\$functionName
$functionEntry = New-Object System.Management.Automation.Runspaces.SessionStateFunctionEntry -ArgumentList $functionName, $functionDefinition
# And add it to the iss object
[void]$initialSessionState.Commands.Add($functionEntry)
}
return $initialSessionState
}
#-----------------------------------------------------------------
function Create-RunspacePool {
#-----------------------------------------------------------------
param(
[InitialSessionState]$initialSessionState
)
$runspacePool = [RunspaceFactory]::CreateRunspacePool(1, ([int]$env:NUMBER_OF_PROCESSORS + 1), $initialSessionState, $Host)
$runspacePool.ApartmentState = 'MTA'
$runspacePool.ThreadOptions = "ReuseThread"
[void]$runspacePool.Open()
return $runspacePool
}
#-----------------------------------------------------------------
function Release-Runspaces {
#-----------------------------------------------------------------
$runspaces = Get-Runspace | Where { $_.Id -gt 1 }
foreach( $runspace in $runspaces ) {
try{
[void]$runspace.Close()
[void]$runspace.Dispose()
}
catch {
}
}
}
$initialSessionState = Create-InitialSessionState -functionNameList $GH.functions
$runspacePool = Create-RunspacePool -initialSessionState $initialSessionState
foreach ($server in $GH.servers)
{
Write-Host $server
$job = [System.Management.Automation.PowerShell]::Create($initialSessionState)
$job.RunspacePool = $runspacePool
$scriptBlock = { param ( [hashtable]$GH, [string]$server ); Download-Content -server $server }
[void]$job.AddScript( $scriptBlock ).AddArgument( $GH ).AddArgument( $server )
$jobs += New-Object PSObject -Property #{
RunNum = $jobCounter++
JobObj = $job
Result = $job.BeginInvoke() }
do {
Sleep -Seconds 1
} while( $runspacePool.GetAvailableRunspaces() -lt 1 )
}
Do {
Sleep -Seconds 1
} While( $jobs.Result.IsCompleted -contains $false)
$GH.results
Release-Runspaces | Out-Null
[void]$runspacePool.Close()
[void]$runspacePool.Dispose()
Finally I found a simple solution via events. Here is my code-snippet:
cls
Remove-Variable * -ea 0
[System.Net.ServicePointManager]::ServerCertificateValidationCallback = $null
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
$srvList = #('srv1','srv2','srv3')
$webObjList = [System.Collections.ArrayList]::new()
$eventList = [System.Collections.ArrayList]::new()
$resultList = [System.Collections.ArrayList]::new()
$i=0
foreach ($srv in $srvList) {
$null = $webObjList.add([System.Net.WebClient]::new())
$null = $eventList.add($(Register-ObjectEvent -InputObject $webObjList[$i] -EventName DownloadStringCompleted -SourceIdentifier $srv))
$null = $resultList.add($webObjList[$i].DownloadStringTaskAsync("https://$srv/MyWebPage"))
$i++
}
do {sleep -Milliseconds 10} until ($resultList.IsCompleted -notcontains $false)
foreach ($srv in $srvList) {Unregister-Event $srv}
# show all Results:
$resultList.result

multithread with runspaces instead of foreach cycle

I have a script with a foreach cycle. It has about a dozen functions, each collecting information from remote machines' C$ share (cutting text files, checking file version, etc.)
This is however taking some time, since each machine's data collected after one by one. (sometimes it runs with 500+ input)
Wish to put this into runspaces with parallel execution, but so far no examples worked. I am quite new to the concept.
Current script's outline
$inputfile = c:\temp\computerlist.txt
function 1
function 2
function 3, etc
foreach cycle
function 1
function 2
function 3
All results written to screen with write-host for now.
This example pings a number of server in parallel, so you easily can modify it for your demands:
Add-Type -AssemblyName System.Collections
$GH = [hashtable]::Synchronized(#{})
[System.Collections.Generic.List[PSObject]]$GH.results = #()
[System.Collections.Generic.List[string]]$GH.servers = #('server1','server2','server3');
[System.Collections.Generic.List[string]]$GH.functions = #('Check-Server');
[System.Collections.Generic.List[PSObject]]$jobs = #()
#-----------------------------------------------------------------
function Check-Server {
#-----------------------------------------------------------------
# a function which runs parallel
param(
[string]$server
)
$result = Test-Connection $server -Count 1 -Quiet
$GH.results.Add( [PSObject]#{ 'Server' = $server; 'Result' = $result } )
}
#-----------------------------------------------------------------
function Create-InitialSessionState {
#-----------------------------------------------------------------
param(
[System.Collections.Generic.List[string]]$functionNameList
)
# Setting up an initial session state object
$initialSessionState = [System.Management.Automation.Runspaces.InitialSessionState]::CreateDefault()
foreach( $functionName in $functionNameList ) {
# Getting the function definition for the functions to add
$functionDefinition = Get-Content function:\$functionName
$functionEntry = New-Object System.Management.Automation.Runspaces.SessionStateFunctionEntry -ArgumentList $functionName, $functionDefinition
# And add it to the iss object
[void]$initialSessionState.Commands.Add($functionEntry)
}
return $initialSessionState
}
#-----------------------------------------------------------------
function Create-RunspacePool {
#-----------------------------------------------------------------
param(
[InitialSessionState]$initialSessionState
)
$runspacePool = [RunspaceFactory]::CreateRunspacePool(1, ([int]$env:NUMBER_OF_PROCESSORS + 1), $initialSessionState, $Host)
$runspacePool.ApartmentState = 'MTA'
$runspacePool.ThreadOptions = "ReuseThread"
[void]$runspacePool.Open()
return $runspacePool
}
#-----------------------------------------------------------------
function Release-Runspaces {
#-----------------------------------------------------------------
$runspaces = Get-Runspace | Where { $_.Id -gt 1 }
foreach( $runspace in $runspaces ) {
try{
[void]$runspace.Close()
[void]$runspace.Dispose()
}
catch {
}
}
}
$initialSessionState = Create-InitialSessionState -functionNameList $GH.functions
$runspacePool = Create-RunspacePool -initialSessionState $initialSessionState
foreach ($server in $GH.servers)
{
Write-Host $server
$job = [System.Management.Automation.PowerShell]::Create($initialSessionState)
$job.RunspacePool = $runspacePool
$scriptBlock = { param ( [hashtable]$GH, [string]$server ); Check-Server -server $server }
[void]$job.AddScript( $scriptBlock ).AddArgument( $GH ).AddArgument( $server )
$jobs += New-Object PSObject -Property #{
RunNum = $jobCounter++
JobObj = $job
Result = $job.BeginInvoke() }
do {
Sleep -Seconds 1
} while( $runspacePool.GetAvailableRunspaces() -lt 1 )
}
Do {
Sleep -Seconds 1
} While( $jobs.Result.IsCompleted -contains $false)
$GH.results
Release-Runspaces | Out-Null
[void]$runspacePool.Close()
[void]$runspacePool.Dispose()
This would be concurrent and run in about 10 seconds total. The computer could be localhost three times if you got it working.
invoke-command comp1,comp2,comp3 { sleep 10; 'done' }
Simple attempt at api (threads):
$a = [PowerShell]::Create().AddScript{sleep 5;'a done'}
$b = [PowerShell]::Create().AddScript{sleep 5;'b done'}
$c = [PowerShell]::Create().AddScript{sleep 5;'c done'}
$r1 = $a.BeginInvoke(); $r2 = $b.BeginInvoke() $r3 = $c.BeginInvoke()
$a.EndInvoke($r1); $b.EndInvoke($r2); $c.EndInvoke($r3)
a done
b done
c done

Powershell try block doesn't release file handle for catch block

I'm writing a script that tries to download a file with two file-download methods.
I'm not going to go into great details, but the resulting function looks something like this:
function Download-FileRobust($url, $targetFile) {
try {
Download-File $url $targetFile
}
catch {
Download-FileWget $url $targetFile
}
}
When the Download-File function fails, PowerShell doesn't loosen the handle on the created file at location $targetFile, and Download-FileWget can't write to that location.
I'm used to Python, so it took me quite a while to figure out the problem.
The source-code for the other two functions are as follows:
function Download-FileWget($url, $targetFile){
$wgetDir = (Get-ChildItem -Path "$env:userprofile\Downloads\wget*win32").FullName
if($env:Path -notlike "*$wgetDir*"){
$env:Path = "$wgetDir;$env:Path"
}
Invoke-Expression "wget '$url' -O '$targetFile'"
}
function Download-File($url, $targetFile){
$uri = New-Object "System.Uri" "$url"
$request = [System.Net.HttpWebRequest]::Create($uri)
$request.set_Timeout(15000) #15 second timeout
$response = $request.GetResponse()
$totalLength = [System.Math]::Floor($response.get_ContentLength()/1024)
$responseStream = $response.GetResponseStream()
$targetStream = New-Object -TypeName System.IO.FileStream -ArgumentList $targetFile, Create
$buffer = new-object byte[] 10KB
$count = $responseStream.Read($buffer,0,$buffer.length)
$downloadedBytes = $count
while ($count -gt 0){
$targetStream.Write($buffer, 0, $count)
$count = $responseStream.Read($buffer,0,$buffer.length)
$downloadedBytes = $downloadedBytes + $count
Write-Progress -activity "Downloading file '$($url.split('/') | Select -Last 1)'" -status "Downloaded ($([System.Math]::Floor($downloadedBytes/1024))K of $($totalLength)K): " -PercentComplete ((([System.Math]::Floor($downloadedBytes/1024)) / $totalLength) * 100)
}
Write-Progress -activity "Finished downloading file '$($url.split('/') | Select -Last 1)'"
$targetStream.Flush()
$targetStream.Close()
$targetStream.Dispose()
$responseStream.Dispose()
}
Inside the Download-File function you never actually do anything to ensure the $targetStream is properly flushed and disposed of. You need to wrap the streams in a try/catch/finally blocks:
function Download-File {
param([string]$url, [string]$targetFile)
$uri = New-Object "System.Uri" "$url"
$request = [System.Net.HttpWebRequest]::Create($uri)
$request.set_Timeout(15000) #15 second timeout
$response = $request.GetResponse()
$totalLength = [System.Math]::Floor($response.get_ContentLength()/1024)
$responseStream = $response.GetResponseStream()
try {
$targetStream = New-Object -TypeName System.IO.FileStream -ArgumentList $targetFile, Create
try {
$buffer = new-object byte[] 10KB
$count = $responseStream.Read($buffer,0,$buffer.length)
$downloadedBytes = $count
while ($count -gt 0){
$targetStream.Write($buffer, 0, $count)
$count = $responseStream.Read($buffer,0,$buffer.length)
$downloadedBytes = $downloadedBytes + $count
Write-Progress -activity "Downloading file '$($url.split('/') | Select -Last 1)'" -status "Downloaded ($([System.Math]::Floor($downloadedBytes/1024))K of $($totalLength)K): " -PercentComplete ((([System.Math]::Floor($downloadedBytes/1024)) / $totalLength) * 100)
}
Write-Progress -activity "Finished downloading file '$($url.split('/') | Select -Last 1)'"
}
catch {
throw
}
finally {
if($targetStream){
$targetStream.Flush()
$targetStream.Close()
$targetStream.Dispose()
}
}
}
catch {
throw
}
finally {
$responseStream.Dispose()
}
}

DataGridView. Run Job while entering new row parallel

The script included is to check the replication and repair for each Server that I add. The goal I have in mind is for it to automatically check the connection of each computer as I add them as well as check the uptime.
The current issue I'm having is that it will delay to check the uptime before it allows me to input my next computer.
As you can see in my script I'm trying to use a start-job which is not working at all for me as it says the 'ComputerName' is null, but the ComputerName is set in the Get-Uptime function.
I may be going about this all wrong. Any help would be great!!
function Check-Replication {
#----------------------------------------------
#region Import the Assemblies
#----------------------------------------------
[void][Reflection.Assembly]::Load('System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089')
[void][Reflection.Assembly]::Load('System.Data, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089')
[void][Reflection.Assembly]::Load('System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a')
[void][Reflection.Assembly]::Load('System.DirectoryServices, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a')
[void][Reflection.Assembly]::Load('System.ServiceProcess, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a')
#endregion Import Assemblies
$base64Image = "iVBORw0KGgoAAAANSUhEUgAAABMAAAATCAQAAADYWf5HAAAABGdBTUEAALGPC/xhBQAAAAJiS0dEAP+Hj8y/AAAACXBIWXMAAWG3AAFhtwFW7IyrAAAAB3RJTUUH4goDDxY48JvtEgAAAg9JREFUKM9NkT9MU1EUxn/33kf/APVPYQGaYImLYQAikU6kidFoWBwYjLgw6SZxkLhgnExc0A1ZHDC6uOmMDQ5KLCYuqINxoFgq2Ce0fbx/912GvhDOlzN9X77znXMEAFyhL7Uxmbl9vjjQn1ahrv35Vdp7NbxWc1eJaxFJIX9h+UZ9yZTMF/PVlM1Hs2Jm6yPLl/KwCIinPGZwxHsxPnGTXhQSgSEi4oB3rK7LO1vfFlA75Ib2X54rTJNFIBGAISLE0MdOrjo69GHDlteT9nxXYZQMEWBOQNPBGJmCPX81qZzL/kIuPUwPHVjxSIiI0Pj4/MXOV8pWa6Yzm0XFhIzXimLAGTqzrRkrKnbThSYgxELEwoiQkJCATrppFi09kCKJwSNJgEEgMBhCAjwCEqTQA5ZW7SME+AiiE24BPgaJQiuptI5zaCLMcRs0bSZEaSm3D/HwCdBodCzS6Dibh4valrLUoEGLQ1x8guPoPh6HODRpQkkNOs5UIp0mgUIAEZoQH5cm+/yjym6965GarXwfdC8msOJfhrFPW1Sjgl65tqRsfXrz/4SXkxg0IR4uDg322aPGFo3PPfc399QD3ti95dZYKxfg4+LQ4gCbXapUaKwn7/78MYeA59yjkK8/NNPpsxlSKDQuTRxbvs0++fT7GXOI9g+n6E+VJw9umaLJhcrSoiJKp16Pr1Xd9wAcAYNiEWcBi781AAAAJXRFWHRkYXRlOmNyZWF0ZQAyMDE4LTEwLTAzVDE1OjIyOjU2LTA0OjAwISeC9gAAACV0RVh0ZGF0ZTptb2RpZnkAMjAxOC0xMC0wM1QxNToyMjo1Ni0wNDowMFB6OkoAAAAASUVORK5CYII="
$imageBytes = [Convert]::FromBase64String($base64Image)
$memStream = New-Object IO.MemoryStream($imageBytes, 0, $imageBytes.Length)
$memStream.Write($imageBytes, 0, $imageBytes.Length);
$imageGrey = [System.Drawing.Bitmap]::FromStream($memStream, $true)
$base64Image = "iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsSAAALEgHS3X78AAAAB3RJTUUH4goDDxstKOh3tAAABJVJREFUOMtVlNtvVFUUxn/r3M8w7bSddloYWkpALlqoEBJ9IIQoSUUTEYj6B8CLMSbySOKTvtQYnlDf1ITggwHFiEAIl4CVxPBAAhQKIVwCpXTaTjuXM3PmXOZsH6aA7uTbe2Vl5Vt7rXxrCf85V69epVKpkMvlejVNW2saZtq2HU3XdA+gmcRdYRRKGIalJEkm6vX6M8uy1ObNm19wyHPj2rVrJEliua671bGdnFetPbwwdm7g1v3xVTPFmXbTMLUlqXRheM3wo5HtI0U3neoJgqAahuGYpmn+8PDwS8Lx8XGSJGlzHPtz07SWnT5/ev63yye2VbTqJiujp6wlpogIoR+rZjXxcnbvnd1bd13Z/uZ2I4zDsu/7h3RdXxgaGkKuX79Oo9EwM5n2L0zD+uT4mWP1Hy791FfL1my1VBGkAiIjBAEjNnHqDvqMTrbWGe/fvr/47rb39DAKf/Y876Bpmr5hGAau6+7WNf3T38+e0L85e2h5qb+kq/6EZrYJ7SB2qzMqBKoK3dF5NjlljJ79utc0zOCtN97eZ9nWDdd1fzR83+9LuakDE/fuZL87972aWzYrskpQeZBuDdIKLEAJEivwhKRdoeyEyeYk354/bK/Mr7SX9/Z/Vi6VL2oi8m4QhJuOXfiVh9YjkRUtMpYCvQp6gRyQU5AD1QcsVUgeZFC4pSb4458/iaJoSET2akqpXZNTj+0rD/9G9SXQI0g3SBeoTlDtQBuojEAGJAN0AVmgB5KehL8eXGa2OGskSfK+EUfx6/cfP6DQfIZ0AG0K0kAaJAU4gC6AAmNRY3ErCW2tJE8LT5mcfUrKTW3U6vV6R6FYIDBDcARsWj0zQAxpkT2HAZiAqVAmYCtwFL40KJaL+HU/ozUaDYmiCEFQAiIvJ0ct3uqF9XIe5LlDtcQcxTG+74vWTJrltJPGbtrQWJRG1CpLxQrVBEkUkihoKogXEQEhSAAOLq7h0Gw2K5pK1I2u9i5yeg7KIFXAE1Qd8IEGECyiAcoHaoKqAR6oMuTtPGkrTaKSW5ph6KeWpNqjDd0b0GcENStQBJkHKYFUgEVIGaQksNCKUXOgF3U29W1GFz0xDOOUls12n7Rt8+aWFVsYjAZRTxLUlIJpoADMPoe03gIwrWAKeKxYr69nY+8QmsjdTCZzXM/n85XBFSuCZpS80yEdxuPJJ9TCKpIIhK2yVX3xp0VgRmAK5KGivzTAx2s+JJvqjjq7Or86evToGX10dJSBgYG7Cwvz3SbmlqVmn3hzVYJKiBEYaDUdKYFWFIw5E6dg4046rA3W8MGqPfSlelVHR8eRda+uGx3eOBwKwKVLl2hra+u6ffv2l4Xpwr6F6rxzv/SAp/EkDScAp6UPLdBwohT97gCr21eRttJRNps9svqV1Qc9z5vduXPnywU7NjZGJpNxb9y8+dH83NyBeq021IgC3U8CYmJEwBILx3AxdbPpOs69bHf28Lq1646Uy2Vvx44d/9/YAIVCgVwux4WLFwann03v9TxvTxAEr8VxnBEEXdcrlmVNpNvSJ3t6en4ZGRm5PzU1pfL5/AuOfwFWfAyFldyKLAAAACV0RVh0ZGF0ZTpjcmVhdGUAMjAxOC0xMC0wM1QxNToyNzo0NS0wNDowMDpMU7EAAAAldEVYdGRhdGU6bW9kaWZ5ADIwMTgtMTAtMDNUMTU6Mjc6NDUtMDQ6MDBLEesNAAAAAElFTkSuQmCC"
$imageBytes = [Convert]::FromBase64String($base64Image)
$memStream = New-Object IO.MemoryStream($imageBytes, 0, $imageBytes.Length)
$memStream.Write($imageBytes, 0, $imageBytes.Length);
$imageGreen = [System.Drawing.Bitmap]::FromStream($memStream, $true)
$base64Image = "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"
$imageBytes = [Convert]::FromBase64String($base64Image)
$memStream = New-Object IO.MemoryStream($imageBytes, 0, $imageBytes.Length)
$memStream.Write($imageBytes, 0, $imageBytes.Length);
$imageRed = [System.Drawing.Bitmap]::FromStream($memStream, $true)
#----------------------------------------------
#region Generated Form Objects
#----------------------------------------------
[System.Windows.Forms.Application]::EnableVisualStyles()
$form1 = New-Object 'System.Windows.Forms.Form'
$labelTypeEachComputerName = New-Object 'System.Windows.Forms.Label'
$textbox1 = New-Object 'System.Windows.Forms.TextBox'
$datagridview1 = New-Object 'System.Windows.Forms.DataGridView'
$buttonRun = New-Object 'System.Windows.Forms.Button'
$Online = New-Object 'System.Windows.Forms.DataGridViewImageColumn'
$Uptime = New-Object 'System.Windows.Forms.DataGridViewTextBoxColumn'
$Computer = New-Object 'System.Windows.Forms.DataGridViewTextBoxColumn'
$Status = New-Object 'System.Windows.Forms.DataGridViewTextBoxColumn'
$InitialFormWindowState = New-Object 'System.Windows.Forms.FormWindowState'
#endregion Generated Form Objects
#----------------------------------------------
# Function Get-Uptime
#----------------------------------------------
#region - Get-Uptime
$Uptime1 = {
function Get-Uptime {
Params ($ComputerName)
$global:ComputerName = $row.cells[2].Value
$os = Get-WmiObject win32_operatingsystem -ComputerName $ComputerName -ErrorAction SilentlyContinue
if ($os.LastBootUpTime) {
$uptime = (Get-Date) - $os.ConvertToDateTime($os.LastBootUpTime)
#Write-Output ("Last boot: " + $os.ConvertToDateTime($os.LastBootUpTime) )
Write-Output ("" + $uptime.Days + "d " + $uptime.Hours + "h " + $uptime.Minutes + "m" )
} else {
Write-Warning "Unable to connect to $computername"
}
}
}
#endregion
#----------------------------------------------
# User Generated Script
#----------------------------------------------
$FormEvent_Load = {
}
$textbox1_Validated = {
if ($textbox1.Text -ne "") {
$i = $datagridview1.Rows.Add(1)
$row = $datagridview1.Rows[$i]
$row.SetValues(#($imageGrey,'',$textbox1.Text,'pending'))
$textbox1.Text = ''
$textbox1.Focus()
if ($row.Cells[2].Value -ne "") {
if (Test-Connection -ComputerName $row.cells[2].Value -Count 1 -Quiet) {
$Time = Get-Date
Start-Job -InitializationScript $Uptime1 -scriptblock {(Get-Uptime -ComputerName $args[0])} -Args $row.cells[2].Value |
#Start-Job -InitializationScript $Uptime1 -scriptblock {(Get-Uptime)}|
Wait-Job | Receive-Job
$row.SetValues(#($imageGrey,($Uptime),$row.Cells[2].Value))
#Remove-Job -Name CheckSiteUptime
$Time = Get-date
$row.Cells[0].Value = $imageGreen
} else {
$row.Cells[0].Value = $imageRed}
}
}
}
$buttonRun_Click = {
$datagridview1.Rows | ForEach-Object {
$row = [System.Windows.Forms.DataGridViewRow]$_
$CommandResult = Invoke-Command -ComputerName $row.cells[2].Value -ArgumentList $row -ScriptBlock{
Param($row)
Import-Module Hyper-V
if ((Get-VM -ErrorAction Stop | Where-Object {$_.name -like '*SR*'} | Get-VMReplication -ErrorAction Stop).Replicationhealth -eq 'critical') {
try{
Get-VM -ErrorAction Stop | Where-Object {$_.name -like '*SR*'} | Resume-VMReplication -ErrorAction Stop
if ((Get-VM -ErrorAction Stop | Where-Object {$_.name -like '*SR*'} | Get-VMReplication -ErrorAction Stop).Replicationhealth -eq 'critical') {
throw [System.Exception] "Replicationhealth critical"
}
} catch {
try{
Get-VM -ErrorAction Stop | Where-Object {$_.name -like '*SR*'} | Resume-VMReplication -Resynchronize -ErrorAction Stop
} catch {
return 'FAILED: Resume-VMReplication -Resynchronize'
break
}
return 'Successful: Resume-VMReplication -Resynchronize'
break
}
return 'Successful: Resume-VMReplication'
} else {
return 'Successful: No action replication is NOT critical'
}
}
switch ($CommandResult) {
"FAILED: Resume-VMReplication -Resynchronize" {
$row.Cells | %{$_.Style.BackColor = 'pink'}
$Row.Cells[3].Value = $CommandResult
}
"Successful: Resume-VMReplication -Resynchronize" {
$row.Cells | %{$_.Style.BackColor = 'lightgreen'}
$Row.Cells[3].Value = $CommandResult
}
"Successful: Resume-VMReplication" {
$row.Cells | %{$_.Style.BackColor = 'lightgreen'}
$Row.Cells[3].Value = $CommandResult
}
"Successful: No action replication is NOT critical" {
$row.Cells | %{$_.Style.BackColor = 'lightgreen'}
$Row.Cells[3].Value = $CommandResult
}
}
}
$datagridview1.ReadOnly = $true
}
# --End User Generated Script--
#----------------------------------------------
#region Generated Events
#----------------------------------------------
$Form_StateCorrection_Load = {
#Correct the initial state of the form to prevent the .Net maximized form issue
$form1.WindowState = $InitialFormWindowState
}
$Form_Cleanup_FormClosed = {
#Remove all event handlers from the controls
try {
$textbox1.remove_Validated($textbox1_Validated)
$buttonRun.remove_Click($buttonRun_Click)
$form1.remove_Load($FormEvent_Load)
$form1.remove_Load($Form_StateCorrection_Load)
$form1.remove_FormClosed($Form_Cleanup_FormClosed)
} catch {
Out-Null <# Prevent PSScriptAnalyzer warning #>
}
}
#endregion Generated Events
#----------------------------------------------
#region Generated Form Code
#----------------------------------------------
$form1.SuspendLayout()
#
# form1
#
$form1.Controls.Add($labelTypeEachComputerName)
$form1.Controls.Add($textbox1)
$form1.Controls.Add($datagridview1)
$form1.Controls.Add($buttonRun)
$form1.AutoScaleDimensions = '6, 13'
$form1.AutoScaleMode = 'Font'
$form1.ClientSize = '625, 600'
$form1.FormBorderStyle = 'FixedDialog'
$form1.MaximizeBox = $False
$form1.MinimizeBox = $False
$form1.Name = 'form1'
$form1.StartPosition = 'CenterScreen'
$form1.Text = 'Replication Check'
$form1.add_Load($FormEvent_Load)
#
# labelTypeEachComputerName
#
$labelTypeEachComputerName.Location = '20, 18'
$labelTypeEachComputerName.Name = 'labelTypeEachComputerName'
$labelTypeEachComputerName.Size = '240, 49'
#$labelTypeEachComputerName.TabIndex = 5
$labelTypeEachComputerName.Text = 'Type each computer name ending with a <tab> it will be added to the list. Click run when alll have been added.'
$labelTypeEachComputerName.UseCompatibleTextRendering = $True
#
# textbox1
#
$textbox1.CharacterCasing = 'Upper'
$textbox1.Location = '20, 81'
$textbox1.Name = 'textbox1'
$textbox1.Size = '285, 20'
#$textbox1.TabIndex = 1
$textbox1.add_Validated($textbox1_Validated)
#
# datagridview1
#
$datagridview1.AllowUserToAddRows = $False
$datagridview1.AllowUserToDeleteRows = $False
$datagridview1.AllowUserToResizeColumns = $True
$datagridview1.AllowUserToResizeRows = $False
$datagridview1.ColumnHeadersHeightSizeMode = 'AutoSize'
[void]$datagridview1.Columns.Add($Online)
[void]$datagridview1.Columns.Add($Uptime)
[void]$datagridview1.Columns.Add($Computer)
[void]$datagridview1.Columns.Add($Status)
$datagridview1.columns[0].Width = '40'
$datagridview1.columns[3].Width = '250'
$datagridview1.Location = '20, 113'
$datagridview1.Name = 'datagridview1'
$datagridview1.ReadOnly = $True
$datagridview1.Size = '583, 470'
$datagridview1.TabIndex = 3
$datagridview1.DefaultCellStyle.WrapMode = "True"
#
# buttonRun
#
$buttonRun.Location = '325, 80'
$buttonRun.Name = 'buttonRun'
$buttonRun.Size = '75, 23'
$buttonRun.TabIndex = 2
$buttonRun.TabStop = $False
$buttonRun.Text = 'Run'
$buttonRun.UseCompatibleTextRendering = $True
$buttonRun.UseVisualStyleBackColor = $True
$buttonRun.add_Click($buttonRun_Click)
#
# Online
#
$Online.HeaderText = 'Online'
$Online.Name = 'Online'
$Online.DataPropertyName = 'Online'
#
# Uptime
#
$Uptime.HeaderText = 'Uptime'
$Uptime.Name = 'Uptime'
$Uptime.ReadOnly = $True
#
# Computer
#
$Computer.HeaderText = 'Server'
$Computer.Name = 'Server'
$Computer.ReadOnly = $True
#
# Status
#
$Status.HeaderText = 'Status'
$Status.Name = 'Status'
$Status.ReadOnly = $True
$form1.ResumeLayout()
#endregion Generated Form Code
#----------------------------------------------
#Save the initial state of the form
$InitialFormWindowState = $form1.WindowState
#Init the OnLoad event to correct the initial state of the form
$form1.add_Load($Form_StateCorrection_Load)
#Clean up the control events
$form1.add_FormClosed($Form_Cleanup_FormClosed)
#Show the Form
return $form1.ShowDialog()
} #End Function
#Call the form
Check-Replication | Out-Null
Because PowerShell jobs run in a separate runspace, even declaring a variable as $global: does not make it available inside your job. Instead, you need to pass the value in to your job when you create it.
For example, you could change your code as follows:
...
Function Get-Uptime {
params ($ComputerName)
$global:ComputerName = $ComputerName
...
Start-Job -InitializationScript $Uptime1 -scriptblock {(Get-Uptime -ComputerName $args[0])} -Args $row.cells[2].Value
A similar issue is described here: https://social.technet.microsoft.com/Forums/ie/en-US/5b369c15-d2ad-4ee8-a1fc-3f0ca8df230a/powershell-jobs-and-global-variables?forum=ITCG
Another resource on some options other than PS jobs, which tend to be relatively inefficient: https://randombrainworks.com/2018/01/28/powershell-background-jobs-runspace-jobs-thread-jobs/
Another way around your base issue of de-coupling your form/GUI from your backend code is to use runspaces and a synchronized hashtable. The advantage of this model is that your can have many threads simultaneously performing work and actively updating your form, and instead of passing variables around, you have a single copy and pass references.
This is a great article to get started: https://learn-powershell.net/2012/10/14/powershell-and-wpf-writing-data-to-a-ui-from-a-different-runspace/