Work an Array in Batches of 5

This topic contains 5 replies, has 2 voices, and was last updated by Profile photo of Dave Wyatt Dave Wyatt 3 years, 2 months ago.

  • Author
    Posts
  • #14604
    Profile photo of Daniel buksh
    Daniel buksh
    Participant

    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
    }}}

  • #14609
    Profile photo of Daniel buksh
    Daniel buksh
    Participant

    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)

  • #14610
    Profile photo of Dave Wyatt
    Dave Wyatt
    Moderator

    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
    }
    
  • #14611
    Profile photo of Daniel buksh
    Daniel buksh
    Participant

    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?

  • #14607
    Profile photo of Daniel buksh
    Daniel buksh
    Participant

    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)

  • #14613
    Profile photo of Dave Wyatt
    Dave Wyatt
    Moderator

    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
    }
    

You must be logged in to reply to this topic.