Speeding up SharePoint Subscription Edition CU installation

 Speeding up SharePoint Subscription Edition CU installation

 

1)  It seems like Miguel Isidoro has a pretty good blog post about speeding up SharePoint CU installation.

However, it hasn't been updated for SharePoint Subscription Edition, yet.

 How to install SharePoint Cumulative Updates In Less Than An Hour in a SharePoint Farm Step by Step - Blog IT (create.pt)

 

2)  The SharePoint CU Installer script hasn't been updated for SharePoint Subscription edition, which no longer requires two separate patches for the language pack (wssloc patch) and SharePoint itself (sts patch).  With SP SE patches, you only need one patch file with the uber prefix.

For example: uber-subscription-kb5002581-fullfile-x64-glb.exe

Latest patches can be found at Microsoft Learn:

SharePoint updates - Office release notes | Microsoft Learn

 

To download the SharePoint Patch PowerShell script, click here. This script, written by Trevor Seward, is an improved version of a PowerShell script originally created by Russ Maxwell for SharePoint 2013.

 

The SharePointPatch comes with a number of useful functions.  One of them is the Install-SPPatch.  Here is the original Install-SPPatch that checks for wssloc and sts patches.

SharePointPatchScript 1.0


<#
    .SYNOPSIS
        Install-SPPatch
    .DESCRIPTION
        Install-SPPatch reduces the amount of time it takes to install SharePoint patches. This cmdlet supports SharePoint 2013 and above. Additional information
        can be found at https://github.com/Nauplius.
    .PARAMETER Path
        The folder where the patch file(s) reside.
    .PARAMETER Pause
        Pauses the Search Service Application(s) prior to stopping the SharePoint Search Services.
    .PARAMETER Stop
        Stop the SharePoint Search Services without pausing the Search Service Application(s).
    .PARAMETER SilentInstall
        Silently installs the patches without user input. Not specifying this parameter will cause each patch to prompt to install.
    .PARAMETER KeepSearchPaused
        Keeps the Search Service Application(s) in a paused state after the installation of the patch has completed. Useful for when applying the patch to multiple
        servers in the farm. Default to false.
    .PARAMETER OnlySTS
        Only apply the STS (non-language dependent) patch. This switch may be used when only an STS patch is available.
    .EXAMPLE
        Install-SPPatch -Path C:\Updates -Pause -SilentInstall

        Install the available patches in C:\Updates, pauses the Search Service Application(s) on the farm, and performs a silent installation.
    .EXAMPLE
        Install-SPPatch -Path C:\Updates -Pause -KeepSearchPaused:$true -SilentInstall

        Install the available patches in C:\Updates, pauses the Search Service Application(s) on the farm,
        does not resume the Search Service Application(s) after the installation is complete, and performs a silent installation.
    .NOTES
        Author: Trevor Seward
        Date: 01/16/2020
    .LINK
        https://thesharepointfarm.com
    .LINK
        https://github.com/Nauplius
    .LINK
        https://sharepointupdates.com
#>


Function Install-SPPatch {
    param
    (
        [string]
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        $Path,
        [switch]
        [Parameter(Mandatory = $true, ParameterSetName = "PauseSearch")]
        $Pause,
        [switch]
        [Parameter(Mandatory = $true, ParameterSetName = "StopSearch")]
        $Stop,
        [switch]
        [Parameter(Mandatory = $false, ParameterSetName = "PauseSearch")]
        $KeepSearchPaused = $false,
        [switch]
        [Parameter(Mandatory = $false)]
        $SilentInstall,
        [switch]
        [Parameter(Mandatory = $false)]
        $OnlySTS
    )

    $version = (Get-SPFarm).BuildVersion
    $majorVersion = $version.Major
    $startTime = Get-Date
    $exitRebootCodes = @(3010, 17022)
    $searchSvcRunning = $false
    
    Write-Host -ForegroundColor Green "Current build: $version"

    ###########################
    ##Ensure Patch is Present##
    ###########################

    if ($majorVersion -eq '16') {
        $sts = Get-ChildItem -LiteralPath $Path  -Filter *.exe | ? { $_.Name -match 'sts([A-Za-z0-9\-]+).exe' }
        $wssloc = Get-ChildItem -LiteralPath $Path  -Filter *.exe | ? { $_.Name -match 'wssloc([A-Za-z0-9\-]+).exe' }
        
        if ($OnlySTS) {
            if ($sts -eq $null) {
                Write-Host 'Missing the sts patch. Please make sure the sts patch present in the specified directory.' -ForegroundColor Red
                return            
            }
        }
        else {
            if ($sts -eq $null -and $wssloc -eq $null) {
                Write-Host 'Missing the sts and wssloc patch. Please make sure both patches are present in the specified directory.' -ForegroundColor Red
                return
            }

            if ($sts -eq $null -or $wssloc -eq $null) {
                Write-Host '[Warning] Either the sts and wssloc patch is not available. Please make sure both patches are present in the same directory or safely ignore if only single patch is available.' -ForegroundColor Yellow
                return
            }
        }

        if ($OnlySTS) {
            $patchfiles = $sts
            Write-Host -for Yellow "Installing $sts"
        }
        else {
            $patchfiles = $sts, $wssloc
            Write-Host -for Yellow "Installing $sts and $wssloc"
        }
    }
    elseif ($majorVersion -eq '15') {
        $patchfiles = Get-ChildItem -LiteralPath $Path  -Filter *.exe | ? { $_.Name -match '([A-Za-z0-9\-]+)2013-kb([A-Za-z0-9\-]+)glb.exe' }
        
        if ($patchfiles -eq $null) { 
            Write-Host 'Unable to retrieve the file(s). Exiting Script' -ForegroundColor Red 
            return 
        }

        Write-Host -ForegroundColor Yellow "Installing $patchfiles"
    }
    elseif ($majorVersion -lt '15') {
        throw 'This cmdlet supports SharePoint 2013 and above.'
    }

    ########################
    ##Stop Search Services##
    ########################
    ##Checking Search services##

    $oSearchSvc = Get-Service "OSearch$majorVersion" 
    $sPSearchHCSvc = Get-Service "SPSearchHostController"

    if (($oSearchSvc.status -eq 'Running'-or ($sPSearchHCSvc.status -eq 'Running')) { 
        $searchSvcRunning = $true
        if ($Pause) { 
            $ssas = Get-SPEnterpriseSearchServiceApplication

            foreach ($ssa in $ssas) {
                Write-Host -ForegroundColor Yellow "Pausing the Search Service Application: $($ssa.DisplayName)"
                Write-Host  -ForegroundColor Yellow  ' This could take a few minutes...'
                Suspend-SPEnterpriseSearchServiceApplication -Identity $ssa | Out-Null
            }
        }
        elseif ($Stop) { 
            Write-Host -ForegroundColor Cyan ' Continuing without pausing the Search Service Application'
        }
    }

    #We don't need to stop SharePoint Services for 2016 and above
    if ($majorVersion -lt '16') {
        Write-Host -ForegroundColor Yellow 'Stopping Search Services if they are running'

        if ($oSearchSvc.status -eq 'Running') { 
            Set-Service -Name "OSearch$majorVersion" -StartupType Disabled 
            Stop-Service "OSearch$majorVersion" -WA 0
        }

        if ($sPSearchHCSvc.status -eq 'Running') { 
            Set-Service 'SPSearchHostController' -StartupType Disabled 
            Stop-Service 'SPSearchHostController' -WA 0
        }

        Write-Host -ForegroundColor Green 'Search Services are Stopped'
        Write-Host

        #######################
        ##Stop Other Services##
        #######################
        Set-Service -Name 'IISADMIN' -StartupType Disabled 
        Set-Service -Name 'SPTimerV4' -StartupType Disabled

        Write-Host -ForegroundColor Green 'Gracefully stopping IIS...'
        Write-Host 
        iisreset -stop -noforce 
        Write-Host -ForegroundColor Yellow 'Stopping SPTimerV4'
        Write-Host

        $sPTimer = Get-Service 'SPTimerV4' 
        if ($sPTimer.Status -eq 'Running') {
            Stop-Service 'SPTimerV4'
        }

        Write-Host -ForegroundColor Green 'Services are Stopped'
        Write-Host 
        Write-Host
    }

    ##################
    ##Start patching##
    ##################
    Write-Host -ForegroundColor Yellow 'Working on it... Please keep this PowerShell window open...'
    Write-Host

    $patchStartTime = Get-Date

    foreach ($patchfile in $patchfiles) {
        $filename = $patchfile.Fullname
        #unblock the file, to get rid of the prompts
        Unblock-File -Path $filename -Confirm:$false

        if ($SilentInstall) {
            $process = Start-Process $filename -ArgumentList '/passive /quiet' -PassThru -Wait
        }
        else {
            $process = Start-Process $filename -ArgumentList '/norestart' -PassThru -Wait
        }

        if ($exitRebootCodes.Contains($process.ExitCode)) {
            $reboot = $true
        }

        Write-Host -ForegroundColor Yellow "Patch $patchfile installed with Exit Code $($process.ExitCode)"
    }

    $patchEndTime = Get-Date

    Write-Host 
    Write-Host -ForegroundColor Yellow ('Patch installation completed in {0:g}' -f ($patchEndTime - $patchStartTime))
    Write-Host

    if ($majorVersion -lt '16') {
        ##################
        ##Start Services##
        ##################
        Write-Host -ForegroundColor Yellow 'Starting Services'
        Set-Service -Name 'SPTimerV4' -StartupType Automatic 
        Set-Service -Name 'IISADMIN' -StartupType Automatic

        Start-Service 'SPTimerV4'
        Start-Service 'IISAdmin'

        ###Ensuring Search Services were stopped by script before Starting"
        if ($searchSvcRunning = $true) { 
            Set-Service -Name "OSearch$majorVersion" -StartupType Manual 
            Start-Service "OSearch$majorVersion" -WA 0
            Set-Service 'SPSearchHostController' -StartupType Automatic 
            Start-Service 'SPSearchHostController' -WA 0
        }
    }

    ###Resuming Search Service Application if paused###
    if ($Pause -and $KeepSearchPaused -eq $false) { 
        $ssas = Get-SPEnterpriseSearchServiceApplication

        foreach ($ssa in $ssas) {
            Write-Host -ForegroundColor Yellow "Resuming the Search Service Application: $($ssa.DisplayName)"
            Write-Host -ForegroundColor Yellow ' This could take a few minutes...'
            Resume-SPEnterpriseSearchServiceApplication -Identity $ssa | Out-Null
        }
    }
    elseif ($pause -and $KeepSearchPaused -eq $true) {
        Write-Host -ForegroundColor Yellow 'Not resuming the Search Service Application(s)'
    }

    ###Resuming IIS###
    iisreset -start

    $endTime = Get-Date
    Write-Host -ForegroundColor Green 'Services are Started'
    Write-Host 
    Write-Host 
    Write-Host -ForegroundColor Yellow ('Script completed in {0:g}' -f ($endTime - $startTime))
    Write-Host -ForegroundColor Yellow 'Started:'  $startTime 
    Write-Host -ForegroundColor Yellow 'Finished:'  $endTime 

    if ($reboot) {
        Write-Host -ForegroundColor Yellow 'A reboot is required'
    }
}






I modified my copy version of this script so that the script no longer requires both the sts and wssloc patches.


<#

    .SYNOPSIS

        Install-SPPatch

    .DESCRIPTION

        Install-SPPatch reduces the amount of time it takes to install SharePoint patches. This cmdlet supports SharePoint 2013 and above. Additional information

        can be found at https://github.com/Nauplius.

    .PARAMETER Path

        The folder where the patch file(s) reside.

    .PARAMETER Pause

        Pauses the Search Service Application(s) prior to stopping the SharePoint Search Services.

    .PARAMETER Stop

        Stop the SharePoint Search Services without pausing the Search Service Application(s).

    .PARAMETER SilentInstall

        Silently installs the patches without user input. Not specifying this parameter will cause each patch to prompt to install.

    .PARAMETER KeepSearchPaused

        Keeps the Search Service Application(s) in a paused state after the installation of the patch has completed. Useful for when applying the patch to multiple

        servers in the farm. Default to false.

    .PARAMETER OnlySTS

        Only apply the STS (non-language dependent) patch. This switch may be used when only an STS patch is available.

    .EXAMPLE

        Install-SPPatch -Path C:\Updates -Pause -SilentInstall



        Install the available patches in C:\Updates, pauses the Search Service Application(s) on the farm, and performs a silent installation.

    .EXAMPLE

        Install-SPPatch -Path C:\Updates -Pause -KeepSearchPaused:$true -SilentInstall



        Install the available patches in C:\Updates, pauses the Search Service Application(s) on the farm, 

        does not resume the Search Service Application(s) after the installation is complete, and performs a silent installation.

    .NOTES

        Author: Trevor Seward

        Date: 01/16/2020

    .LINK

        https://thesharepointfarm.com

    .LINK

        https://github.com/Nauplius

    .LINK

        https://sharepointupdates.com

#>



Function Install-SPPatch {

    param

    (

        [string]

        [Parameter(Mandatory = $true)]

        [ValidateNotNullOrEmpty()]

        $Path,

        [switch]

        [Parameter(Mandatory = $true, ParameterSetName = "PauseSearch")]

        $Pause,

        [switch]

        [Parameter(Mandatory = $true, ParameterSetName = "StopSearch")]

        $Stop,

        [switch]

        [Parameter(Mandatory = $false, ParameterSetName = "PauseSearch")]

        $KeepSearchPaused = $false,

        [switch]

        [Parameter(Mandatory = $false)]

        $SilentInstall,

        [switch]

        [Parameter(Mandatory = $false)]

        $OnlySTS

    )



    $version = (Get-SPFarm).BuildVersion

    $majorVersion = $version.Major

    $build = $version.Build;

    $startTime = Get-Date

    $exitRebootCodes = @(3010, 17022)

    $searchSvcRunning = $false

    

    Write-Host -ForegroundColor Green "Current build: $version"



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

    ##Ensure Patch is Present## 

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



    if ($majorVersion -eq '16' -and $build -ge 15601)

    {

        $sts = Get-ChildItem -LiteralPath $Path  -Filter *.exe | ? { $_.Name -match 'sts([A-Za-z0-9\-]+).exe' }

        $wssloc = Get-ChildItem -LiteralPath $Path  -Filter *.exe | ? { $_.Name -match 'wssloc([A-Za-z0-9\-]+).exe' }

        $uberloc = Get-ChildItem -LiteralPath $Path  -Filter *.exe | ? { $_.Name -match 'uber([A-Za-z0-9\-]+).exe' }

        



        if ($uberloc -ne $null)

        {



            $patchfiles = $uberloc;

            Write-Host -for Yellow "Installing $uberloc";

        }

        else

        {



            if ($OnlySTS) {

                if ($sts -eq $null) {

                    Write-Host 'Missing the sts patch. Please make sure the sts patch present in the specified directory.' -ForegroundColor Red

                    return            

                }

            }

            else {

                if ($sts -eq $null -and $wssloc -eq $null) {

                    Write-Host 'Missing the sts and wssloc patch. Please make sure both patches are present in the specified directory.' -ForegroundColor Red

                    return

                }



                if ($sts -eq $null -or $wssloc -eq $null) {

                    Write-Host '[Warning] Either the sts and wssloc patch is not available. Please make sure both patches are present in the same directory or safely ignore if only single patch is available.' -ForegroundColor Yellow

                    return

                }

            }



            if ($OnlySTS) {

                $patchfiles = $sts

                Write-Host -for Yellow "Installing $sts"

            }

            else {

                $patchfiles = $sts, $wssloc

                Write-Host -for Yellow "Installing $sts and $wssloc"

            }



        }



    }



    elseif ($majorVersion -eq '16') {

        $sts = Get-ChildItem -LiteralPath $Path  -Filter *.exe | ? { $_.Name -match 'sts([A-Za-z0-9\-]+).exe' }

        $wssloc = Get-ChildItem -LiteralPath $Path  -Filter *.exe | ? { $_.Name -match 'wssloc([A-Za-z0-9\-]+).exe' }

        $uberloc = Get-ChildItem -LiteralPath $Path  -Filter *.exe | ? { $_.Name -match 'uber([A-Za-z0-9\-]+).exe' }

        



        if ($OnlySTS) {

            if ($sts -eq $null) {

                Write-Host 'Missing the sts patch. Please make sure the sts patch present in the specified directory.' -ForegroundColor Red

                return            

            }

        }

        else {

            if ($sts -eq $null -and $wssloc -eq $null) {

                Write-Host 'Missing the sts and wssloc patch. Please make sure both patches are present in the specified directory.' -ForegroundColor Red

                return

            }



            if ($sts -eq $null -or $wssloc -eq $null) {

                Write-Host '[Warning] Either the sts and wssloc patch is not available. Please make sure both patches are present in the same directory or safely ignore if only single patch is available.' -ForegroundColor Yellow

                return

            }

        }



        if ($OnlySTS) {

            $patchfiles = $sts

            Write-Host -for Yellow "Installing $sts"

        }

        else {

            $patchfiles = $sts, $wssloc

            Write-Host -for Yellow "Installing $sts and $wssloc"

        }

    }

    elseif ($majorVersion -eq '15') {

        $patchfiles = Get-ChildItem -LiteralPath $Path  -Filter *.exe | ? { $_.Name -match '([A-Za-z0-9\-]+)2013-kb([A-Za-z0-9\-]+)glb.exe' }

        

        if ($patchfiles -eq $null) { 

            Write-Host 'Unable to retrieve the file(s).  Exiting Script' -ForegroundColor Red 

            return 

        }



        Write-Host -ForegroundColor Yellow "Installing $patchfiles"

    }

    elseif ($majorVersion -lt '15') {

        throw 'This cmdlet supports SharePoint 2013 and above.'

    }



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

    ##Stop Search Services## 

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

    ##Checking Search services## 



    $oSearchSvc = Get-Service "OSearch$majorVersion" 

    $sPSearchHCSvc = Get-Service "SPSearchHostController"



    if (($oSearchSvc.status -eq 'Running') -or ($sPSearchHCSvc.status -eq 'Running')) { 

        $searchSvcRunning = $true

        if ($Pause) { 

            $ssas = Get-SPEnterpriseSearchServiceApplication



            foreach ($ssa in $ssas) {

                Write-Host -ForegroundColor Yellow "Pausing the Search Service Application: $($ssa.DisplayName)"

                Write-Host  -ForegroundColor Yellow  '    This could take a few minutes...'

                Suspend-SPEnterpriseSearchServiceApplication -Identity $ssa | Out-Null

            }

        }

        elseif ($Stop) { 

            Write-Host -ForegroundColor Cyan '    Continuing without pausing the Search Service Application'

        }

    }



    #We don't need to stop SharePoint Services for 2016 and above

    if ($majorVersion -lt '16') {

        Write-Host -ForegroundColor Yellow 'Stopping Search Services if they are running'



        if ($oSearchSvc.status -eq 'Running') { 

            Set-Service -Name "OSearch$majorVersion" -StartupType Disabled 

            Stop-Service "OSearch$majorVersion" -WA 0

        }



        if ($sPSearchHCSvc.status -eq 'Running') { 

            Set-Service 'SPSearchHostController' -StartupType Disabled 

            Stop-Service 'SPSearchHostController' -WA 0

        }



        Write-Host -ForegroundColor Green 'Search Services are Stopped'

        Write-Host



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

        ##Stop Other Services## 

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

        Set-Service -Name 'IISADMIN' -StartupType Disabled 

        Set-Service -Name 'SPTimerV4' -StartupType Disabled



        Write-Host -ForegroundColor Green 'Gracefully stopping IIS...'

        Write-Host 

        iisreset -stop -noforce 

        Write-Host -ForegroundColor Yellow 'Stopping SPTimerV4'

        Write-Host



        $sPTimer = Get-Service 'SPTimerV4' 

        if ($sPTimer.Status -eq 'Running') {

            Stop-Service 'SPTimerV4'

        }



        Write-Host -ForegroundColor Green 'Services are Stopped'

        Write-Host 

        Write-Host

    }



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

    ##Start patching## 

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

    Write-Host -ForegroundColor Yellow 'Working on it... Please keep this PowerShell window open...'

    Write-Host



    $patchStartTime = Get-Date



    foreach ($patchfile in $patchfiles) {

        $filename = $patchfile.Fullname

        #unblock the file, to get rid of the prompts

        Unblock-File -Path $filename -Confirm:$false



        if ($SilentInstall) {

            $process = Start-Process $filename -ArgumentList '/passive /quiet' -PassThru -Wait

        }

        else {

            $process = Start-Process $filename -ArgumentList '/norestart' -PassThru -Wait

        }



        if ($exitRebootCodes.Contains($process.ExitCode)) {

            $reboot = $true

        }



        Write-Host -ForegroundColor Yellow "Patch $patchfile installed with Exit Code $($process.ExitCode)"

    }



    $patchEndTime = Get-Date



    Write-Host 

    Write-Host -ForegroundColor Yellow ('Patch installation completed in {0:g}' -f ($patchEndTime - $patchStartTime))

    Write-Host



    if ($majorVersion -lt '16') {

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

        ##Start Services## 

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

        Write-Host -ForegroundColor Yellow 'Starting Services'

        Set-Service -Name 'SPTimerV4' -StartupType Automatic 

        Set-Service -Name 'IISADMIN' -StartupType Automatic



        Start-Service 'SPTimerV4'

        Start-Service 'IISAdmin'



        ###Ensuring Search Services were stopped by script before Starting" 

        if ($searchSvcRunning = $true) { 

            Set-Service -Name "OSearch$majorVersion" -StartupType Manual 

            Start-Service "OSearch$majorVersion" -WA 0

            Set-Service 'SPSearchHostController' -StartupType Automatic 

            Start-Service 'SPSearchHostController' -WA 0

        }

    }



    ###Resuming Search Service Application if paused### 

    if ($Pause -and $KeepSearchPaused -eq $false) { 

        $ssas = Get-SPEnterpriseSearchServiceApplication



        foreach ($ssa in $ssas) {

            Write-Host -ForegroundColor Yellow "Resuming the Search Service Application: $($ssa.DisplayName)"

            Write-Host -ForegroundColor Yellow '    This could take a few minutes...'

            Resume-SPEnterpriseSearchServiceApplication -Identity $ssa | Out-Null

        }

    }

    elseif ($pause -and $KeepSearchPaused -eq $true) {

        Write-Host -ForegroundColor Yellow 'Not resuming the Search Service Application(s)'

    }



    ###Resuming IIS###

    iisreset -start



    $endTime = Get-Date

    Write-Host -ForegroundColor Green 'Services are Started'

    Write-Host 

    Write-Host 

    Write-Host -ForegroundColor Yellow ('Script completed in {0:g}' -f ($endTime - $startTime))

    Write-Host -ForegroundColor Yellow 'Started:'  $startTime 

    Write-Host -ForegroundColor Yellow 'Finished:'  $endTime 



    if ($reboot) {

        Write-Host -ForegroundColor Yellow 'A reboot is required'

    }

}


 

 

I have discovered that even with this fix, however, patching was still slow.

 

One issue, it turns out was that the original script assumes that IISAdmin installed.   In the days of Windows Server 2012, you would include the IIS 6.0 Metabase Compatibility tools which included IISAdmin.  You don't need that anymore for SP SE.

 

Also, I noticed that the w3svc service was often running still and have been manually stopping it when CU installs have been taking too long.

 

I managed to discover where the CU installation logs what it is doing:

 

While the CU is installing, it is logged into: $env:temp\sts-x-none_MSPLOG.LOG

 

You can run to watch what the install is doing live:

 

Get-Content $env:temp\sts-x-none_MSPLOG.LOG -tail 500 -wait

 

One thing, I also noticed is the log would eventually go into a loop trying to clean up files and display a list of things preventing it from continuing:

 

 

Restart Manager Info: 3 entries

 

04/10/2024 19:19:48.026 [6900]:                           App[0]: (10748) Windows PowerShell (), type = 5

 

04/10/2024 19:19:48.026 [6900]:                           App[1]: (6088) Windows PowerShell ISE (), type = 0

 

04/10/2024 19:19:48.026 [6900]:                           App[2]: (10936) SharePoint Caching Service (SPCache), type = 3

 

04/10/2024 19:19:48.026 [6900]:            Security info:

 

04/10/2024 19:19:48.026 [6900]:                           Owner: S-1-5-18

 

04/10/2024 19:19:48.026 [6900]:                           Group: S-1-5-18

 

04/10/2024 19:19:48.026 [6900]:                           DACL information: 5 entries:

 

04/10/2024 19:19:48.026 [6900]:                           ACE[0]: Type = 0x00, Flags = 010, Mask = 001f01ff, SID = S-1-5-18

 

04/10/2024 19:19:48.026 [6900]:                           ACE[1]: Type = 0x00, Flags = 010, Mask = 001f01ff, SID = S-1-5-32-544

 

04/10/2024 19:19:48.026 [6900]:                           ACE[2]: Type = 0x00, Flags = 010, Mask = 001200a9, SID = S-1-5-32-545

 

04/10/2024 19:19:48.026 [6900]:                           ACE[3]: Type = 0x00, Flags = 010, Mask = 001200a9, SID = S-1-15-2-1

 

04/10/2024 19:19:48.026 [6900]:                           ACE[4]: Type = 0x00, Flags = 010, Mask = 001200a9, SID = S-1-15-2-2

 

04/10/2024 19:19:48.026 [6900]: Assembly Install: Failing with hr=80070005 at RemoveDirectoryAndChildren, line 393

 

04/10/2024 19:19:48.026 [6900]: Detailed info about C:\Windows\assembly\temp\JVPGGYWOGR\Microsoft.Office.InfoPath.dll

 

04/10/2024 19:19:48.042 [6900]:            File attributes: 00000080

 

In this case, PowerShell and the SharePoint Cache service were preventing it from continuing.

 

Another example here where IIS Worker Process may have been preventing the clean up process from completing:

12/30/2022 00:29:50.842 [14812]: Detailed info about C:\Windows\assembly\temp\KAITD5GHQO\Microsoft.SharePoint.IdentityModel.dll

12/30/2022 00:29:50.842 [14812]:        File attributes: 00000080

12/30/2022 00:29:50.909 [14812]:        Restart Manager Info: 4 entries

12/30/2022 00:29:50.909 [14812]:                App[0]: (15072) IIS Worker Process (), type = 5

12/30/2022 00:29:50.909 [14812]:                App[1]: (548) Windows PowerShell (), type = 5

12/30/2022 00:29:50.909 [14812]:                App[2]: (13056) IIS Worker Process (), type = 5

12/30/2022 00:29:50.909 [14812]:                App[3]: (1684) IIS Worker Process (), type = 5

12/30/2022 00:29:50.909 [14812]:        Security info:

12/30/2022 00:29:50.909 [14812]:                Owner: S-1-5-18

12/30/2022 00:29:50.909 [14812]:                Group: S-1-5-18

12/30/2022 00:29:50.909 [14812]:                DACL information: 5 entries:

12/30/2022 00:29:50.909 [14812]:                ACE[0]: Type = 0x00, Flags = 010, Mask = 001f01ff, SID = S-1-5-18

12/30/2022 00:29:50.909 [14812]:                ACE[1]: Type = 0x00, Flags = 010, Mask = 001f01ff, SID = S-1-5-32-544

12/30/2022 00:29:50.909 [14812]:                ACE[2]: Type = 0x00, Flags = 010, Mask = 001200a9, SID = S-1-5-32-545

12/30/2022 00:29:50.909 [14812]:                ACE[3]: Type = 0x00, Flags = 010, Mask = 001200a9, SID = S-1-15-2-1

12/30/2022 00:29:50.909 [14812]:                ACE[4]: Type = 0x00, Flags = 010, Mask = 001200a9, SID = S-1-15-2-2

12/30/2022 00:29:50.909 [14812]: Assembly Install: Failing with hr=80070005 at RemoveDirectoryAndChildren, line 393

 

 

If my understanding of these log entries is correct, there is evidence that PowerShell, IIS, and some SharePoint Services may be slowing down the CU install when it gets into this cleanup loop state.

 

So, it may be wise to not have some of those services or apps running during the CU install to speed things up.

 

Therefore, rather than launching the update from PowerShell and then leaving PowerShell open while waiting for the install to complete as is done by the script, disable the SPTimerV4 job, stop IIS, and stop the SPCache service if it is running on the computer.  And consider closing PowerShell windows that have the Microsoft.SharePoint.PowerShell PSSnapin added to it.

 

So, rather than a single module, maybe you could just use a few ps1 scripts like so:

 

Pause-SPServicesForCU.ps1

 

 

Write-Host ("Adding Microsofot.SharePointPowerShell Snapin...") -ForegroundColor Green;

Add-PSSnapin Microsoft.SharePoint.PowerShell -ErrorAction Continue

 

 

#Display Current Services Running

Write-Host("Current SharePoint Service State: ");

$services = get-Service | where {$_.DisplayName.StartsWith("SharePoint")};

$services;

 

 

 

#Pause Search

$ssas = Get-SPEnterpriseSearchServiceApplication

 

            foreach ($ssa in $ssas) {

 

                if ($ssa.IsPaused() -eq 0)

                {

                Write-Host -ForegroundColor Yellow "Pausing the Search Service Application: $($ssa.DisplayName)"

                Write-Host  -ForegroundColor Yellow  '    This could take a few minutes...'

                Suspend-SPEnterpriseSearchServiceApplication -Identity $ssa | Out-Null

                }

                else

                {

                    Write-Host("Search is paused, alrady.") -ForegroundColor Green;

                }

            }

 

 

#Stop IISAdmin

$iisadminsvc = Get-Service -Name IISADMIN;

if ($iisadminsvc -ne $null)

{

 

    Set-Service -Name IISADMIN -StartupType Disabled;

}

else

{

    Write-Host("No IISAdmin Service on this system.  This is not an issue for IIS10...") -ForegroundColor Yellow;

}

 

        Write-Host -ForegroundColor Green 'Gracefully stopping IIS...'

        Write-Host

iisreset /stop /noforce

 

$w3svc = Get-Service -Name W3SVC

if ($w3svc -ne $null)

{

    Stop-Service -Name W3SVC;

 

}

 

$w3svc

 

 

 

Write-Host("Disabling the SPTimerV4 service....") -ForegroundColor Green;

#Stop SPTimerV4

$sptimerv4 = Get-Service -Name SPTimerV4;

if ($sptimerv4 -ne $null)

{

    Set-Service -Name SPTimerV4 -StartupType Disabled;

    Stop-Service -Name SPTimerV4 -WA 0;

 

}

$sptimerv4 = Get-Service -Name SPTimerV4;

$sptimerv4

 

#Stop Other SharePoint Services

Write-Host ("Stopping other SharePoint Services...") -ForegroundColor Green;

$services = get-Service | where {$_.DisplayName.StartsWith("SharePoint")};

$services

 

foreach ($service in $services)

{

    #Stop the SP Cache service

    if ($service.Status -eq "Running" -and $service.Name -eq "SPCache")

    {

        Write-Host("Stopping " + $service.Name + "....") -ForegroundColor Green;

        Stop-Service -Name $service.Name;

    }

 

}

 

$services = get-Service | where {$_.DisplayName.StartsWith("SharePoint")};

$services;

 

 

 

Write-Host("Ready to launch the SP CU.  You can close this Powershell afer launching, but remember to run Start-SPServicesAfterCU.") -ForegroundColor Green;

 

Write-Host("To view CU install details:");

Write-Host("Get-Content $env:temp\sts-x-none_MSPLOG.LOG -tail 500 -wait") -ForegroundColor Green;

 

 

After pausing services, you would manually launch the CU.

 

Start-SPServicesAfterCU.ps1

Write-Host ("Adding Microsofot.SharePointPowerShell Snapin...") -ForegroundColor Green;

Add-PSSnapin Microsoft.SharePoint.PowerShell -ErrorAction Continue

 

 

 

 

#Stop IISAdmin

$iisadminsvc = Get-Service -Name IISADMIN;

if ($iisadminsvc -ne $null)

{

 

    Set-Service -Name IISADMIN -StartupType Automatic;

}

else

{

    Write-Host("No IISAdmin Service on this system.  This is not an issue for IIS10...") -ForegroundColor Yellow;

}

 

        Write-Host -ForegroundColor Green 'Starting IIS...'

        Write-Host

iisreset /start

 

$w3svc = Get-Service -Name W3SVC

if ($w3svc -ne $null -and $w3svc.Status -eq "Stopped")

{

   

    Start-Service -Name W3SVC;

    $w3svc = Get-Service -Name W3SVC;

 

}

 

 

$w3svc

 

 

 

Write-Host("Enabling SPTimerV4...") -ForegroundColor Green;

#Enable SPTimerV4

$sptimerv4 = Get-Service -Name SPTimerV4;

if ($sptimerv4 -ne $null)

{

    Set-Service -Name SPTimerV4 -StartupType Automatic;

    Start-Service -Name SPTimerV4;

 

}

$sptimerv4 = Get-Service -Name SPTimerV4;

$sptimerv4

 

Write-Host("Now that the SPTimerV4 service is running, timer jobs should start up dependent SPServices based on the role of the server.  Verify just in case.");

 

 

$services = get-Service | where {$_.DisplayName.StartsWith("SharePoint")};

$services;

 

 

Write-Host("Next Things to do are Invoke-SPConfigWizard and Resume-SPSearch") -ForegroundColor Green;

 

 

After installing the CU on all SP Servers:

 

 

Invoke-SPConfigWizard.ps1

 

 PSConfig.exe -cmd upgrade -inplace b2b -wait -cmd applicationcontent -install -cmd installfeatures -cmd secureresources -cmd services -install

 

 

 

Resume search after patching is complete:

 

 

Resume-SPSearch

#Resume  Search

$ssas = Get-SPEnterpriseSearchServiceApplication

 

            foreach ($ssa in $ssas) {

 

                if ($ssa.IsPaused() -ne 0)

                {

                Write-Host -ForegroundColor Yellow "Resuming the Search Service Application: $($ssa.DisplayName)"

                Write-Host  -ForegroundColor Yellow  '    This could take a few minutes...'

                Resume-SPEnterpriseSearchServiceApplication -Identity $ssa

                }

                else

                {

                    Write-Host("Search is resumed, already.") -ForegroundColor Green;

                }

            }

 

 

 

Comments

Popular posts from this blog

How To use ASPNET_SetReg to store encrypted data in the registry and then decrypt the data for use in your app

Nostalgia for SNL's Il Returno De Hercules

Unlock a local account on a remote system using WMIC.exe