proxb/PoshRSJob

RAM usage continues to spiral up

Opened this issue · 2 comments

I am running PoshRSJob version 1.7.3.8 and I am having trouble with ever-increasing RAM usage when running my multithreaded jobs from my machine. The script that I am having an issue with is listed below. It's a patching script that compares patch files from my server repository against what has already been installed on the target machines. If the target machines don't have a specific patch, it is installed remotely.

Please let me know if you can think of the reason why my RAM usage continues to go up to the point where it freezes my machine. Thanks for your help and for the awesome module!

Here is the code for my patching script. It works pretty well for the most part, but like I said, I can watch the RAM usage on my machine continue to increase until I run out of memory and freeze my machine.

Please provide a code example showing the issue, if applicable:

#region - Load Prereqs
#Elevate if not running as Admin
Param([switch]$Elevated)
Function Check-Admin {
$currentUser = New-Object Security.Principal.WindowsPrincipal $([Security.Principal.WindowsIdentity]::GetCurrent())
$currentUser.IsInRole([Security.Principal.WindowsBuiltinRole]::Administrator)}
if ((Check-Admin) -eq $false){
if ($elevated)
{# could not elevate, quit
}
else {Start-Process powershell.exe -Verb RunAs -ArgumentList ('-noprofile -noexit -file "{0}" -elevated' -f ($myinvocation.MyCommand.Definition))}
exit}

Function Install-RSJob{

if (Get-Module -ListAvailable -Name PoshRSJob) {Write-Host "PoshRSJob module already installed" -ForegroundColor Green }
else {Write-Host "PoshRSJob Module was not found, installing now.... " -ForegroundColor Yellow
Copy-Item -Path "$PSScriptRoot\PoshRSJob" -Recurse -Destination "\$env:COMPUTERNAME\c$\Program Files\WindowsPowerShell\Modules\PoshRSJob" -Force
Import-Module –Name PoshRSJob
cls
if (Get-Module -ListAvailable -Name PoshRSJob) {Write-Host "PoshRSJob successfully installed." -ForegroundColor Green}
else {"ERROR, PoshRSJob unable to be installed...."}}
Get-ChildItem "C:\Program Files\WindowsPowerShell\Modules\PoshRSJob" -Recurse | Unblock-File}
Install-RSJob
#endregion - Prereqs Loaded

$Computers = Get-Content "$PSScriptRoot\computers.txt"
$ServerRepo = "\PatchServer\Patch Repository"
$Computers | Start-RSJob -Name {"$($_)"} -Throttle 15 -ScriptBlock{
Param($computer)

#region ---------- Ping Check
$starttimer= Get-Date
$ConnCheck = Test-Connection $Computer -Count 1 -Quiet -ErrorAction SilentlyContinue
if ($ConnCheck -eq $false)
{Write-Verbose "$computer is offline" -Verbose

$stoptimer = Get-Date #------Collects end time
$ReportDate = Get-Date -Format ("yyyy-MM-dd")
$resultsarray = @()

$eachcomputer = [ordered]@{ "Computer" = $computer
"SysArch" = "offline"
"OS Version" = "offline"
"OS Build" = "offline"
"Start Time" = $starttimer
"Stop Time" = $stoptimer
"Elapsed Time in Hours" = [math]::round(($stoptimer - $starttimer).TotalHours , 2)
"New Patches Installed" = "offline"
"Previously Installed" = "offline"
"Skipped/Doesn't Apply" = "offline" }

$newobj = New-Object psobject -Property $eachcomputer
$resultsarray += $newobj

Write-Verbose "Attempting to grab mutex" -Verbose
$mtx = New-Object System.Threading.Mutex($false, "Global\TestMutex")
If ($mtx.WaitOne(500))
{
Write-Verbose "Recieved mutex!" -Verbose
$log = "$using:psscriptroot\Logs$ReportDate -PatchReport.csv"
Write-Verbose "Writing data to $log" -Verbose
$resultsarray | Export-Csv -Path $log -NoTypeInformation -Append
Write-Verbose "Releasing mutex" -Verbose
[void]$mtx.ReleaseMutex() }
Else { Write-Warning "Timed out acquiring mutex!" }
Return
}
#endregion - Ping Check

Else{

psexec 2> $null \$computer -s -d powershell Enable-PSRemoting -force
Start-Sleep -Seconds 5

$remotearray = @()
$InstalledPatches = @()
$SkippedCounter = 0
$SkippedArray = @()
$PrevInstallPatches = @()
$kbArticlearray = @()

Function mAdSwitch{
Param(
    [String]$Switch,
    [String]$InstallLine,
    [String]$InstallPath,
    [String]$File
    )

#------Exit command for installation commands
$exitline = "exit"

#------Passed Switch parameter to remove files within Local Repository
if($Switch -eq "Cleanup"){
    Remove-Item $InstallPath\mAd.cmd -Force -ErrorAction SilentlyContinue
    Remove-Item $InstallPath\$File -ErrorAction SilentlyContinue
    }

#------Passed Switch parameter to prepare Local Repository for an installation
if($Switch -eq "Install"){
    
    #------Checks if mAd.cmd exists on target computer
    $CMDchecker = Test-Path $InstallPath\mAd.cmd

    if($CMDchecker -eq $True){
        
        #------Clears computers Local Repository for installation
        Clear-Content -Path $InstallPath\mAd.cmd -ea SilentlyContinue
        }
    else{
        
        #------Creates cmd file if it does not exist
        New-Item -Path "$InstallPath\mAd.cmd" -ItemType File
        }

    #------Populates cmd file with installation commands
    Add-Content -Value $InstallLine -Path $InstallPath\mAd.cmd
    Add-Content -Value $exitline -Path $InstallPath\mAd.cmd
    }
}

Function Check-Mutex{
#check for existing Mutex file
$Mutex = "\\$computer\c$\Local_Repository\Mutex.csv"
if((Test-Path $Mutex) -eq $false){New-Item -ItemType File -Path $Mutex -ErrorAction SilentlyContinue}

#Attempt to grab Mutex


    Write-Verbose "Attempting to grab mutex" -Verbose
    $checkmutex = (Get-Content "\\$computer\c$\Local_Repository\Mutex.csv")
    $timestart = Get-Date
    if($checkmutex -eq "ON")
    {Write-Verbose "Mutex currently ON, waiting 5 seconds..." -Verbose
        do{ sleep -Seconds 5
            $timecheck = Get-Date
            $timealive = ($timecheck - $timestart).Minutes
            $checkmutex = Get-Content "\\$computer\c$\Local_Repository\Mutex.csv"}
        until(($checkmutex -eq "OFF") -or ($timealive -ge "15") -eq $true)
        $timealive = ($timecheck - $timestart).seconds
    if($timealive -ge "10" -eq $true){Write-Verbose "Timed out waiting on Mutex" -Verbose}}
    else{
    Write-Verbose "Turning Mutex ON" -Verbose
    Set-Content -Path $Mutex -Value ON
    Write-Verbose "Recieved mutex!" -Verbose}
    }
    
Function Close-Mutex{
    #Closing Mutex
    $checkmutex = Get-Content "\\$computer\c$\Local_Repository\Mutex.csv"
    $checkmutex | Set-Content -Value "OFF"
    Write-Verbose "Turned Mutex OFF" -Verbose}

#region - KBCollector

Hotfix Collector

#===============================#
$KBArray = @()
$Hotfix = Get-Hotfix -ComputerName $Computer -ErrorAction SilentlyContinue | Select -expandProperty HotFixID
$KBArray += $HotFix

WMI Collector

#===============================#
function CheckWMI{
$Regex = 'KB\d+'
$WMIRecord = Get-WmiObject win32_reliabilityrecords -ComputerName $Computer
foreach($WMIArticle in $WMIRecord){

    #------If WMI article contains a KB - Select only the KB name
    if($WMIArticle.message -match "$Regex"){
        $KBMatch = select-string -InputObject ($WMIArticle.message) -Pattern "$Regex" | select Matches
        $WMIUser = $WMIArticle.User.Split("=")[0]
        $KBMatch = $KBMatch.Matches
        $KBMatch = $KBMatch | Select Value
        $WMIKB = $KBMatch.Value
        
        #------Checks for installed WMI KB not already found via Get-Hotfix
        if($WMIKB -like $kbarticle){$kbarticle}}}}

Registry Collector

#===============================#

Try{
$RegQuery = Invoke-Command -ComputerName $Computer -ScriptBlock {
$OSArc = (Get-WmiObject Win32_OperatingSystem).OSArchitecture
if($OSArc -eq "64-bit"){$KeyPath = "hklm:\SOFTWARE\Wow6432Node\Microsoft\Updates"}
else{$KeyPath = "hklm:\SOFTWARE\Microsoft\Updates"}
#------Regular expression to capture KB articles
$KB7regex = 'KB(\d{7}$)'
$ResultArray = @()
$NDPregArray = @()
Get-ChildItem -path $KeyPath -Recurse | Select -Property Name | foreach{$NDPregArray += [string]$_.Name}
#------Collects KB articles from subkey names
foreach($Item in $NDPregArray){
if($Item -match "$KB7regex"){
$ResultArray += $Matches[0]
$Matches.Clear()}}
#------Returns results
Return $ResultArray }
#------Stores returned KB articles if $KBArray does not already contain them
foreach($Item in $RegQuery){
if(!($KBArray.Contains($Item))){$KBArray += $Item }}}

Catch{ "Unable to query $Computer registry for installed NDP patches. Installation will continue."}

function CheckRegistry{
$RegQuery = Invoke-Command -ComputerName $Computer -ScriptBlock {
$OSArc = (Get-WmiObject Win32_OperatingSystem).OSArchitecture
if($OSArc -eq "64-bit"){$KeyPath = "hklm:\SOFTWARE\Wow6432Node\Microsoft\Updates"}
else{$KeyPath = "hklm:\SOFTWARE\Microsoft\Updates"}
#------Regular expression to capture KB articles
$KB7regex = 'KB(\d{7}$)'
$ResultArray = @()
$NDPregArray = @()
Get-ChildItem -path $KeyPath -Recurse | Select -Property Name | foreach{$NDPregArray += [string]$_.Name}
#------Collects KB articles from subkey names
foreach($Item in $NDPregArray){
if($Item -match "$KB7regex"){
$ResultArray += $Matches[0]
$Matches.Clear()}}
#------Returns results
Return $ResultArray }
#------Stores returned KB articles if $KBArray does not already contain them
if($RegQuery -contains $kbarticle){$kbarticle}}
#endregion - KBCollector

#region - Gather SysInfo

Local Respository

#===============================#

#------Checks if Local Repository directory exists
$LocalRepositoryPath = "\$Computer\c$\Local_Repository"
$LPcheck = Test-Path $LocalRepositoryPath
$localrepo = "C:\Local_Repository"

if($LPcheck -eq $False){ New-Item -Path $LocalRepositoryPath -ItemType Directory }
Get-Item -Path "$LocalRepositoryPath*" | where {$_.Name -notmatch ".csv"} -ErrorAction SilentlyContinue | Remove-Item

Try{
$sysArch = (Get-WmiObject -Class Win32_OperatingSystem -ComputerName $Computer).OSArchitecture
$sysversion = (Get-WmiObject -Class Win32_OperatingSystem -ComputerName $Computer).Version
$OSversion = (Get-WmiObject -Class Win32_OperatingSystem -ComputerName $Computer).Caption
}
Catch{ Continue }

#------Sets OS architecture variable for future search parameters
If($sysArch -eq "64-bit"){ $sysArch = "x64" }
If($sysarch -eq "32-bit"){ $sysArch = "x86" }

#------Determines computer's viable Cab and Office patches based of installed OS version (6.1 = Windows 7 ; 8.1 = Windows 8)
If($sysversion -like "6.1"){ $CabVer = "6.1" }
If($sysversion -like "6.3"){ $CabVer = "8.1" }
If($sysversion -like "10"){ $CabVer = "10.0" }
#endregion - Gather SysInfo

#Open remote session
$session = New-PSSession -ComputerName $computer -Name $computer

#region##################### MSU #########################

#------Collets all patch viable .msu files that match computer's architecture
$MSUPatchFiles = Get-Childitem -Path $using:ServerRepo -Recurse | Where{($.Name -like "$sysArch") -and ($.Name -like "$CabVer") -and ($_.Name -match ".msu")}

Foreach ($MSU in $MSUPatchFiles){

[string]$kbArticle = $MSU.FullName -split '-' | Select-String -Pattern "KB"

If ($KBArray -contains $kbArticle){
    $PrevInstallPatches += $kbArticle
    $PrevInstallDate = (Get-Hotfix -ComputerName $Computer | where {$_.hotfixid -eq $kbArticle} | select -ExpandProperty installedon).toshortdatestring()
    
    write-Host "$computer - $kbArticle previously installed on $PrevInstallDate" -ForegroundColor Green
        if($kbArticlearray -notcontains $kbarticle){
                $kbArticlearray += $kbarticle
                $remotecomputer = [ordered]@{ "KB Article"           = $kbarticle
                                              "Install Date"         = $PrevInstallDate }
                $remoteobj = New-Object psobject -Property $remotecomputer
                if (([bool](($remotearray | select -ExpandProperty "KB Article") -match $remoteobj.'KB Article')) -eq $false){
                $remotearray += $remoteobj
                $remotearray | Export-Csv -Path "$LocalRepositoryPath\$ReportDate - LocalPatchReport.csv" -NoTypeInformation -Append}}}

Else{
Check-Mutex
Copy-Item -LiteralPath $MSU.FullName -Destination "\\$computer\c$\Local_Repository"

#expand msu file to .cab
$SB = { Start-Process -filepath 'powershell.exe' -ArgumentList "expand -f:* c:\Local_Repository\*.msu c:\Local_Repository" -PassThru | Wait-Process -Timeout 2000 }
invoke-command -Session $session -ScriptBlock $SB 

#install patch
$SB={ $patch = Get-Item -Path "C:\Local_Repository\*" | where {($_.name -like "*kb*") -and ($_.Name -like "*.cab")}
$patch = $patch.fullname
Start-Process -FilePath 'dism.exe' -ArgumentList "/online /add-package /PackagePath:$patch /quiet /norestart" -PassThru | Wait-Process -Timeout 2000 }
invoke-command -Session $session -ScriptBlock $SB 


#delete non-csv folder items
Get-Item "$LocalRepositoryPath\*" | where {$_.name -notmatch ".csv"} | Remove-Item
Close-Mutex

        #------Send installed KB# to log file and counts number of installed patches
        if ((Get-HotFix -ComputerName $computer | select -expandProperty hotfixid) -contains $kbArticle){$InstalledPatches += $kbArticle
             write-Host "$computer - $kbArticle successfully installed!" -ForegroundColor Green}
             elseif(CheckWMI -like $kbarticle){$InstalledPatches += $kbArticle
                    write-Host "$computer - $kbArticle successfully installed!" -ForegroundColor Green}
             elseif(CheckRegistry -like $kbarticle){$InstalledPatches += $kbArticle
                    write-Host "$computer - $kbArticle successfully installed!" -ForegroundColor Green}
        else{Write-Host "$computer - $kbArticle skipped, possibly superseeded" -ForegroundColor Yellow; $SkippedArray+=$kbArticle}
        $ReportDate = Get-Date -Format ("yyyy-MM-dd")
        try{$InstallDate = (Get-Hotfix -ComputerName $Computer | where {$_.hotfixid -eq $kbArticle} | select -ExpandProperty installedon).toshortdatestring()}
        catch{$InstallDate = $null}
        if($kbArticlearray -notcontains $kbarticle){
                $kbArticlearray += $kbarticle
                $remotecomputer = [ordered]@{ "KB Article"           = $kbarticle
                                              "Install Date"         = $InstallDate }
                $remoteobj = New-Object psobject -Property $remotecomputer
                $remotearray += $remoteobj
                $remotearray | Export-Csv -Path "$LocalRepositoryPath\$ReportDate - LocalPatchReport.csv" -NoTypeInformation -Append}}}
                #endregion-MSU

#region###################### CAB ########################

#------Collets all patch viable .cab files that match computer's architecture
$CABPatchFiles = Get-Childitem -Path "$using:ServerRepo" -Recurse | Where{($.Name -like "$sysArch") -and ($.Name -like "$CabVer") -and ($_.Name -match ".cab")}

Foreach ($CAB in $CABPatchFiles){
[string]$kbArticle = $CAB.FullName -split '-' | Select-String -Pattern "KB"

If ($KBArray -contains $kbArticle){
    $PrevInstallPatches += $kbArticle
    $PrevInstallDate = (Get-Hotfix -ComputerName $Computer | where {$_.hotfixid -eq $kbArticle} | select -ExpandProperty installedon).toshortdatestring()
    
    write-Host "$computer - $kbArticle previously installed on $PrevInstallDate" -ForegroundColor Green
        if($kbArticlearray -notcontains $kbarticle){
                $kbArticlearray += $kbarticle
                $remotecomputer = [ordered]@{ "KB Article"           = $kbarticle
                                              "Install Date"         = $PrevInstallDate }
                $remoteobj = New-Object psobject -Property $remotecomputer
                if (([bool](($remotearray | select -ExpandProperty "KB Article") -match $remoteobj.'KB Article')) -eq $false){
                $remotearray += $remoteobj
                $remotearray | Export-Csv -Path "$LocalRepositoryPath\$ReportDate - LocalPatchReport.csv" -NoTypeInformation -Append}}}

Else{
Check-Mutex
Copy-Item -LiteralPath $CAB.FullName -Destination "\\$computer\c$\Local_Repository"

#install patch
$SB={ $patch = Get-Item -Path "C:\Local_Repository\*" | where {($_.name -like "*kb*") -and ($_.Name -like "*.cab")}
$patch = $patch.fullname
Start-Process -FilePath 'dism.exe' -ArgumentList "/online /add-package /PackagePath:$patch /quiet /norestart" -PassThru | Wait-Process -Timeout 2000 }
invoke-command -Session $session -ScriptBlock $SB 


#delete non-csv folder items
Get-Item "$LocalRepositoryPath\*" | where {$_.name -notmatch ".csv"} | Remove-Item
Close-Mutex

        #------Send installed KB# to log file and counts number of installed patches
        if ((Get-HotFix -ComputerName $computer | select -expandProperty hotfixid) -contains $kbArticle){$InstalledPatches += $kbArticle
             write-Host "$computer - $kbArticle successfully installed!" -ForegroundColor Green}
             elseif(CheckWMI -like $kbarticle){$InstalledPatches += $kbArticle
                    write-Host "$computer - $kbArticle successfully installed!" -ForegroundColor Green}
             elseif(CheckRegistry -like $kbarticle){$InstalledPatches += $kbArticle
                    write-Host "$computer - $kbArticle successfully installed!" -ForegroundColor Green}
        else{Write-Host "$computer - $kbArticle skipped, possibly superseeded" -ForegroundColor Yellow; $SkippedArray+=$kbArticle}
        $ReportDate = Get-Date -Format ("yyyy-MM-dd")
        try{$InstallDate = (Get-Hotfix -ComputerName $Computer | where {$_.hotfixid -eq $kbArticle} | select -ExpandProperty installedon).toshortdatestring()}
        catch{$InstallDate = $null}
        if($kbArticlearray -notcontains $kbarticle){
                $kbArticlearray += $kbarticle
                $remotecomputer = [ordered]@{ "KB Article"           = $kbarticle
                                              "Install Date"         = $PrevInstallDate }
                $remoteobj = New-Object psobject -Property $remotecomputer
                $remotearray += $remoteobj
                $remotearray | Export-Csv -Path "$LocalRepositoryPath\$ReportDate - LocalPatchReport.csv" -NoTypeInformation -Append}}}
                #endregion-CAB

#region##################### .NET ########################

#------Collets all patch viable .NET Framework files that match computer's architecture
$NDPPatchFiles = Get-Childitem -Path "$using:ServerRepo" -Recurse | Where{($.Name -like "$sysArch") -and ($.Name -like "$CabVer") -and ($_.Name -like "NDP")}

Foreach ($NDP in $NDPPatchFiles){
[string]$kbArticle = $NDP.FullName -split '-' | Select-String -Pattern "KB"
[string]$Name = $NDP.Name

If ($KBArray -contains $kbArticle){
    $PrevInstallPatches += $kbArticle
    $PrevInstallDate = (Get-Hotfix -ComputerName $Computer | where {$_.hotfixid -eq $kbArticle} | select -ExpandProperty installedon).toshortdatestring()
    
    write-Host "$computer - $kbArticle previously installed on $PrevInstallDate" -ForegroundColor Green
        if($kbArticlearray -notcontains $kbarticle){
                $kbArticlearray += $kbarticle
                $remotecomputer = [ordered]@{ "KB Article"           = $kbarticle
                                              "Install Date"         = $PrevInstallDate }
                $remoteobj = New-Object psobject -Property $remotecomputer
                if (([bool](($remotearray | select -ExpandProperty "KB Article") -match $remoteobj.'KB Article')) -eq $false){
                $remotearray += $remoteobj
                $remotearray | Export-Csv -Path "$LocalRepositoryPath\$ReportDate - LocalPatchReport.csv" -NoTypeInformation -Append}}}

Else{
Check-Mutex
Copy-Item -LiteralPath $NDP.FullName -Destination "\\$computer\c$\Local_Repository"

#install patch
    #------Installation Command
    $EXEline = "$localrepo\$name /quiet /norestart"

    #------Creates cmd for currently loaded patch to install on target computer
    mAdSwitch -Switch "Install" -InstallLine $EXEline -InstallPath $LocalRepositoryPath
    
    #------Launches created cmd
    $install = (([WMICLASS]"\\$Computer\ROOT\CIMV2:win32_process").Create('c:\Local_Repository\mAd.cmd')).processid
    $wait1 = get-process -cn $Computer -pid $install -ea SilentlyContinue                       
    
    #------Script waits for the launched cmd process to complete
    While($wait1 -ne $null){
        start-sleep -seconds 5
        $wait1 = get-process -cn $Computer -pid $install -ea SilentlyContinue
        }

    #------Deletes the cmd file
    mAdSwitch -Switch "Cleanup" -File $name  -InstallPath $LocalRepositoryPath


#delete non-csv folder items
Get-Item "$LocalRepositoryPath\*" | where {$_.name -notmatch ".csv"} | Remove-Item
Close-Mutex

        #------Send installed KB# to log file and counts number of installed patches
        if ((Get-HotFix -ComputerName $computer | select -expandProperty hotfixid) -contains $kbArticle){$InstalledPatches += $kbArticle
             write-Host "$computer - $kbArticle successfully installed!" -ForegroundColor Green}
             elseif(CheckWMI -like $kbarticle){$InstalledPatches += $kbArticle
                    write-Host "$computer - $kbArticle successfully installed!" -ForegroundColor Green}
             elseif(CheckRegistry -like $kbarticle){$InstalledPatches += $kbArticle
                    write-Host "$computer - $kbArticle successfully installed!" -ForegroundColor Green}
        else{Write-Host "$computer - $kbArticle skipped, possibly superseeded" -ForegroundColor Yellow; $SkippedArray+=$kbArticle}
        $ReportDate = Get-Date -Format ("yyyy-MM-dd")
        try{$InstallDate = (Get-Hotfix -ComputerName $Computer | where {$_.hotfixid -eq $kbArticle} | select -ExpandProperty installedon).toshortdatestring()}
        catch{$InstallDate = $null}
        if($kbArticlearray -notcontains $kbarticle){
                $kbArticlearray += $kbarticle
                $remotecomputer = [ordered]@{ "KB Article"           = $kbarticle
                                              "Install Date"         = $PrevInstallDate }
                $remoteobj = New-Object psobject -Property $remotecomputer
                $remotearray += $remoteobj
                $remotearray | Export-Csv -Path "$LocalRepositoryPath\$ReportDate - LocalPatchReport.csv" -NoTypeInformation -Append}}}
                #endregion-.NET

#region#################### Office #######################

#------Registry location of Office version
$OfficeRegPath = "hklm:\SOFTWARE\Microsoft\Office"
$OfficeRegArray = @()

#------Collects all installed office version via registry key subnames
Get-ChildItem -path $OfficeRegPath | Select -Property PSChildName | Select-String -Pattern "\d+" | foreach{$OfficeRegArray += [string]$_.matches}

#------Determines computer's viable office patches via registry keys
if($OfficeRegArray -contains "14"){ $OfficeVer = '2010' }
if($OfficeRegArray -contains "15"){ $OfficeVer = '2013' }

#------Collets all patch viable office files that match computer's architecture
$OfficePatchFiles = Get-Childitem -Path "$using:ServerRepo" -Recurse | Where{($.Name -like "$sysArch") -and ($.Name -match $Officever)}

Foreach ($Office in $OfficePatchFiles){
[string]$kbArticle = $Office.FullName -split '-' | Select-String -Pattern "KB"
[string]$Name = $Office.Name

If ($KBArray -contains $kbArticle){
    $PrevInstallPatches += $kbArticle
    $PrevInstallDate = (Get-Hotfix -ComputerName $Computer | where {$_.hotfixid -eq $kbArticle} | select -ExpandProperty installedon).toshortdatestring()
    
    write-Host "$computer - $kbArticle previously installed on $PrevInstallDate" -ForegroundColor Green
        if($kbArticlearray -notcontains $kbarticle){
                $kbArticlearray += $kbarticle
                $remotecomputer = [ordered]@{ "KB Article"           = $kbarticle
                                              "Install Date"         = $PrevInstallDate }
                $remoteobj = New-Object psobject -Property $remotecomputer
                if (([bool](($remotearray | select -ExpandProperty "KB Article") -match $remoteobj.'KB Article')) -eq $false){
                $remotearray += $remoteobj
                $remotearray | Export-Csv -Path "$LocalRepositoryPath\$ReportDate - LocalPatchReport.csv" -NoTypeInformation -Append}}}

Else{
Check-Mutex
Copy-Item -LiteralPath $Office.FullName -Destination "\\$computer\c$\Local_Repository"

#install patch
    #------Installation Command
    $EXEline = "$localrepo\$name /quiet /norestart"

    #------Creates cmd for currently loaded patch to install on target computer
    mAdSwitch -Switch "Install" -InstallLine $EXEline -InstallPath $LocalRepositoryPath
    
    #------Launches created cmd
    $install = (([WMICLASS]"\\$Computer\ROOT\CIMV2:win32_process").Create('c:\Local_Repository\mAd.cmd')).processid
    $wait1 = get-process -cn $Computer -pid $install -ea SilentlyContinue                       
    
    #------Script waits for the launched cmd process to complete
    While($wait1 -ne $null){
        start-sleep -seconds 5
        $wait1 = get-process -cn $Computer -pid $install -ea SilentlyContinue
        }

    #------Deletes the cmd file
    mAdSwitch -Switch "Cleanup" -File $name  -InstallPath $LocalRepositoryPath


#delete non-csv folder items
Get-Item "$LocalRepositoryPath\*" | where {$_.name -notmatch ".csv"} | Remove-Item
Close-Mutex

        #------Send installed KB# to log file and counts number of installed patches
        if ((Get-HotFix -ComputerName $computer | select -expandProperty hotfixid) -contains $kbArticle){$InstalledPatches += $kbArticle
             write-Host "$computer - $kbArticle successfully installed!" -ForegroundColor Green}
             elseif(CheckWMI -like $kbarticle){$InstalledPatches += $kbArticle
                    write-Host "$computer - $kbArticle successfully installed!" -ForegroundColor Green}
             elseif(CheckRegistry -like $kbarticle){$InstalledPatches += $kbArticle
                    write-Host "$computer - $kbArticle successfully installed!" -ForegroundColor Green}
        else{Write-Host "$computer - $kbArticle skipped, possibly superseeded" -ForegroundColor Yellow; $SkippedArray+=$kbArticle}
        $ReportDate = Get-Date -Format ("yyyy-MM-dd")
        try{$InstallDate = (Get-Hotfix -ComputerName $Computer | where {$_.hotfixid -eq $kbArticle} | select -ExpandProperty installedon).toshortdatestring()}
        catch{$InstallDate = $null}
        if($kbArticlearray -notcontains $kbarticle){
                $kbArticlearray += $kbarticle
                $remotecomputer = [ordered]@{ "KB Article"           = $kbarticle
                                              "Install Date"         = $PrevInstallDate }
                $remoteobj = New-Object psobject -Property $remotecomputer
                $remotearray += $remoteobj
                $remotearray | Export-Csv -Path "$LocalRepositoryPath\$ReportDate - LocalPatchReport.csv" -NoTypeInformation -Append}}}
                #endregion-Office

Remove-PSSession -computerName $computer

Ending Tasks

#######################################

if($InstalledPatches -eq "offline"){$InstalledPatches = "offline"}
else{$InstalledPatches = ($InstalledPatches | sort -Unique).count}

if($PrevInstallPatches -eq "offline"){$PrevInstallPatches = "offline"}
else{$PrevInstallPatches = ($PrevInstallPatches | sort -Unique).count}

$stoptimer = Get-Date #------Collects end time
$ReportDate = Get-Date -Format ("yyyy-MM-dd")
$resultsarray = @()

$eachcomputer = [ordered]@{ "Computer" = $computer
"SysArch" = $sysArch
"OS Version" = $OSversion
"OS Build" = $sysversion
"Start Time" = $starttimer
"Stop Time" = $stoptimer
"Elapsed Time in Hours" = [math]::round(($stoptimer - $starttimer).TotalHours , 2)
"New Patches Installed" = $InstalledPatches
"Previously Installed" = $PrevInstallPatches
"Skipped/Doesn't Apply" = ($SkippedArray | sort -Unique).count }

$newobj = New-Object psobject -Property $eachcomputer
$resultsarray += $newobj
$resultsarray | Export-Csv -Path "$LocalRepositoryPath$ReportDate - PatchReport.csv" -NoTypeInformation -Force

Write-Verbose "Attempting to grab mutex" -Verbose
$mtx = New-Object System.Threading.Mutex($false, "Global\TestMutex")
If ($mtx.WaitOne(1000))
{
Write-Verbose "Recieved mutex!" -Verbose
$log = "$using:psscriptroot\Logs$ReportDate -PatchReport.csv"
Write-Verbose "Writing data to $log" -Verbose
$resultsarray | Export-Csv -Path $log -NoTypeInformation -Append
Write-Verbose "Releasing mutex" -Verbose
[void]$mtx.ReleaseMutex() }
Else { Write-Warning "Timed out acquiring mutex!" }

}}

Function Menu{
Function Show-Menu
{
param (
[string]$Title = 'Choose an Option Below:'
)
cls
Write-Host ""
Write-Host " SCOO PATCHER -beta-"
Write-Host ""
Write-Host "================ $Title ================="
Write-Host ""
Write-Host "1: Status - All Jobs"
Write-Host "2: Status - Running Jobs"
Write-Host "3: Status - Completed Jobs"
Write-Host "4: Count - Running jobs"
Write-Host "5: Count - Completed jobs"
Write-Host "Q: Press 'Q' to quit."
}

Do
{
Show-Menu
$input = Read-Host "Please make a selection"
switch ($input)
{
'1' {
cls
""
Write-Host 'Status - All Jobs' -ForegroundColor Darkcyan
""
Write-Host "Id Name State HasMoreData HasErrors Command
-- ---- ----- ----------- --------- -------"
Get-RSJob
write-host ""
} '2' {
cls
""
Write-Host 'Status - Running Jobs' -ForegroundColor Darkcyan
""
Write-Host "Id Name State HasMoreData HasErrors Command
-- ---- ----- ----------- --------- -------"
Get-RSJob -state running
write-host ""
} '3' {
cls
""
Write-Host 'Status - Completed Jobs' -ForegroundColor Darkcyan
""
Write-Host "Id Name State HasMoreData HasErrors Command
-- ---- ----- ----------- --------- -------"
Get-RSJob -state Completed
write-host ""

        } '4' {
             cls
             $continue = $true
             while($continue){
                 if ([console]::KeyAvailable){
                     echo "Press Q to Exit to Main Menu";
                     $x = [System.Console]::ReadKey() 
                     switch ( $x.key)
                     { q { $continue = $false } } } 
                 else{cls
                     ""
                     Write-Host 'Count - Running Jobs' -ForegroundColor Darkcyan
                     Write-Host 'Refreshing every 10 seconds....Press Q to Exit to Main Menu' -ForegroundColor Darkcyan
                     ""
                     $a = (Get-RSJob -State Running).count
                     $b = (Get-RSJob).count 
                     write-host "$a/$b Jobs Running"
                     ""
                     Start-Sleep -seconds 10}}
       } '5' {
             cls
             $continue = $true
             while($continue){
                 if ([console]::KeyAvailable){
                     echo "Press Q to Exit to Main Menu";
                     $x = [System.Console]::ReadKey() 
                     switch ( $x.key)
                     { q { $continue = $false } } } 
                 else{cls
                     ""
                     Write-Host 'Count - Completed Jobs' -ForegroundColor Darkcyan
                     Write-Host 'Refreshing every 10 seconds....Press Q to Exit to Main Menu' -ForegroundColor Darkcyan
                     ""
                     $a = (Get-RSJob -State Completed).count 
                     $b = (Get-RSJob).count 
                     write-host "$a/$b Jobs Completed"
                     ""
                     Start-Sleep -seconds 10}}
        } 'q' {return}
  }
  pause

}
Until ($input -eq 'q')
}
Menu


Do you try to use latest build?

proxb commented

Probably similar issue as #48