Author Posts

April 16, 2014 at 11:08 pm

Morning folks (UK Time)

I have looked everywhere and i cannnot find anything to do with this admittedly im fairly new so i'l explain the problem first...then my potential solution...

I am writing a script to install Visio silently onto remote machines. i have tested this out on one machine and it works fine and tweaked my coding for it to work on two machines. I am doing this via WINRM and sessions.

The problem im facing is that AFAIK theres is a maximum of 5 remote sessions at a time? which isnt so bad as i think if i ran multiple sessions id kill my network. So im happy to work say 98 computers in batches of 5.

SO my solution was to take an array of say 98 computers and work it in batches of 5 creating sessions for each machine installing Visio and waiting until setup.exe has finished before moving onto the next machine...

Problems :

I dont know how to work an array in batches...
if one machine takes longer than the other or fails the code might get stuck waiting for setup.exe to finish when it hasnt started...I'l post my two codes to show you sort of what ive done..
Original script was used on a one to one basis where WINRM isnt enabled (hoping to enable this for through GPO)

sorry if its awfully written!

Current script to work with parallel sessions (not finished)

$computers = @("GB002783","GB002824")
$sessions = new-PSSession -computername $computers
invoke-command -session $sessions -scriptblock {$net = new-object -ComObject WScript.Network}
invoke-command -session $sessions -scriptblock {$drive = ls function:[g-z]: -n | ?{ !(test-path $_) } | select -first 1}
invoke-command -session $sessions -scriptblock {$net.MapNetworkDrive($drive, "\\Mapped Network Location", $false, "Domain\Username", "Password")}
invoke-command -session $sessions -scriptblock {set-location $drive}
$program = "Setup"

Previous script for installing Visio and waiting for setup.exe

Function Start-InstallVisio($computers){
foreach ($comp in $computers){
$cpath = "\\"+($comp)+"\c$\Installer"
If (Test-Connection -comp ($comp) -count 1 -quiet) {
copy-item -path "Mapped Network Location\wirm.bat" -destination $cpath -force
c:\pstools\psexec \\$comp -u administrator -p $passadmin "c:\installer\wirm.bat"
remove-item $cpath\wirm.bat -force
$s = new-PSSession $comp
invoke-command -session $s -scriptblock {$net = new-object -ComObject WScript.Network}
invoke-command -session $s -scriptblock {$drive = ls function:[g-z]: -n | ?{ !(test-path $_) } | select -first 1}
invoke-command -session $s -scriptblock {$net.MapNetworkDrive($drive, "\\Mapped Network Location\Visio 2010", $false, "Domain\Username", "Password")}
invoke-command -session $s -scriptblock {set-location $drive}
invoke-command -session $s -scriptblock {.\setup.exe}
$program = "Setup"
get-service remoteregistry -computername $comp | set-service -status running
#$process = Invoke-WmiMethod -Class Win32_Process -Name create -ArgumentList $Program -ComputerName $computer
#$processId = $process.ProcessId
$runningCheck = { (Get-WmiObject -Class Win32_Process -ComputerName $comp -ErrorAction SilentlyContinue) | ? { ($_.ProcessName -eq "$program.exe") } }
$Time = [System.Diagnostics.Stopwatch]::StartNew()
$CurrentTime = $Time.Elapsed
while ($null -ne (& $runningCheck))
{Write-Host "$([string]::Format("`rUpdate: $program Is Still Running...Please Wait! Time Running: {0:d2}:{1:d2}",$CurrentTime.minutes,$CurrentTime.seconds))"
Start-Sleep -s 30
$CurrentTime = $Time.Elapsed
}
Remove-PSSession $s
}}}

April 17, 2014 at 1:44 am

EDIT :

Think ive found a way to do it in batches of 5....ish... its using this do while loop

to be honest its just an attempt im not quite sure how im going to implement this into my script!


$i = 0
$t = 0
$x = 0
$ary = 1..200
do
{
$x++
do
{
$ary[$t]
$i++
$t++
}
while ($i -lt 5)
write-host "test"
$i = 0
$x=$x+4
}
while ($x -lt $ary.count)

April 17, 2014 at 3:52 am

Wohooo ive managed to do it! Took me sometime but if anyone wants to do this my below code was this...


$i = 0
$t = 0
$x = 0
$ary = import-csv c:\script\testGB.csv
$Batch = @()
do
{
$x++
do
{
#$ary[$t]
$i++
$batch += $ary.computername[$t]
$t++
}
while ($i -lt 5)
write-host $batch
$sessions = new-PSSession -computername $batch
invoke-command -session $sessions -scriptblock {get-process "explo*"}
$i = 0
$x=$x+4
write-host "#Do More Stuff#"
remove-pssession $batch
$Batch = @()
}
while ($x -lt $ary.count)

April 17, 2014 at 5:16 am

You could simply pass your entire array to Invoke-Command without using New-PSSession first. It will handle the queue behind the scenes for you. For example, instead of this:

invoke-command -session $s -scriptblock {$net = new-object -ComObject WScript.Network}
invoke-command -session $s -scriptblock {$drive = ls function:[g-z]: -n | ?{ !(test-path $_) } | select -first 1}
invoke-command -session $s -scriptblock {$net.MapNetworkDrive($drive, "\\Mapped Network Location\Visio 2010", $false, "Domain\Username", "Password")}
invoke-command -session $s -scriptblock {set-location $drive}
invoke-command -session $s -scriptblock {.\setup.exe}

You could combine those separate calls to invoke-command into a single, multi-statement script block that is called against many computers:

Invoke-Command -ComputerName $hugeComputerArray -ScriptBlock {
    $net = New-Object -ComObject WScript.Network
    $drive = ls function:[g-z]: -n | ?{ !(Test-Path $_) } | select -first 1
    $net.MapNetworkDrive($drive, "\\Mapped Network Location\Visio 2010", $false, "Domain\Username", "Password")
    Set-Location $drive
    .\setup.exe
}

April 17, 2014 at 6:53 am

Ahh brilliant! i had thought of that earlier in the week i had tried it and failed! but that works brilliantly! i must of been using it as -session instead of -computer! 😀

thanks

any ideas on how to collect the error if the computer is unavailable?

April 17, 2014 at 6:58 am

You should get non-terminating errors for any computer that couldn't be contacted. I tend to use -ErrorVariable to capture those:

Invoke-Command -ComputerName $listOfComputers -ScriptBlock { Do-Something } -ErrorAction SilentlyContinue -ErrorVariable myErrors

foreach ($errorRecord in $myErrors)
{
    # Handle $errorRecord
}