Public/Install-ADTMSUpdates.ps1

#-----------------------------------------------------------------------------
#
# MARK: Install-ADTMSUpdates
#
#-----------------------------------------------------------------------------

function Install-ADTMSUpdates
{
    <#
    .SYNOPSIS
        Install all Microsoft Updates in a given directory.
 
    .DESCRIPTION
        Install all Microsoft Updates of type ".exe", ".msu", or ".msp" in a given directory (recursively search directory). The function will check if the update is already installed and skip it if it is. It handles older redistributables and different types of updates appropriately.
 
    .PARAMETER Directory
        Directory containing the updates.
 
    .INPUTS
        None
 
        You cannot pipe objects to this function.
 
    .OUTPUTS
        None
 
        This function does not return any objects.
 
    .EXAMPLE
        Install-ADTMSUpdates -Directory "$dirFiles\MSUpdates"
 
        Installs all Microsoft Updates found in the specified directory.
 
    .NOTES
        An active ADT session is NOT required to use this function.
 
        Tags: psadt
        Website: https://psappdeploytoolkit.com
        Copyright: (C) 2024 PSAppDeployToolkit Team (Sean Lillis, Dan Cunningham, Muhammad Mashwani, Mitch Richters, Dan Gough).
        License: https://opensource.org/license/lgpl-3-0
 
    .LINK
        https://psappdeploytoolkit.com
    #>


    [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseSingularNouns', '', Justification = "This function is appropriately named and we don't need PSScriptAnalyzer telling us otherwise.")]
    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [System.String]$Directory
    )

    begin
    {
        Initialize-ADTFunction -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState
        $kbPattern = '(?i)kb\d{6,8}'
    }

    process
    {
        # Get all hotfixes and install if required.
        Write-ADTLogEntry -Message "Recursively installing all Microsoft Updates in directory [$Directory]."
        foreach ($file in (Get-ChildItem -LiteralPath $Directory -Recurse -Include ('*.exe', '*.msu', '*.msp')))
        {
            try
            {
                try
                {
                    if ($file.Name -match 'redist')
                    {
                        # Handle older redistributables (ie, VC++ 2005)
                        [System.Version]$redistVersion = $file.VersionInfo.ProductVersion
                        [System.String]$redistDescription = $file.VersionInfo.FileDescription
                        Write-ADTLogEntry -Message "Installing [$redistDescription $redistVersion]..."
                        if ($redistDescription -match 'Win32 Cabinet Self-Extractor')
                        {
                            Start-ADTProcess -FilePath $file.FullName -ArgumentList '/q' -WindowStyle 'Hidden' -IgnoreExitCodes '*'
                        }
                        else
                        {
                            Start-ADTProcess -FilePath $file.FullName -ArgumentList '/quiet /norestart' -WindowStyle 'Hidden' -IgnoreExitCodes '*'
                        }
                    }
                    elseif ($kbNumber = [System.Text.RegularExpressions.Regex]::Match($file.Name, $kbPattern).ToString())
                    {
                        # Check to see whether the KB is already installed
                        if (Test-ADTMSUpdates -KbNumber $kbNumber)
                        {
                            Write-ADTLogEntry -Message "KB Number [$kbNumber] is already installed. Continue..."
                            continue
                        }
                        Write-ADTLogEntry -Message "KB Number [$KBNumber] was not detected and will be installed."
                        switch ($file.Extension)
                        {
                            '.exe'
                            {
                                # Installation type for executables (i.e., Microsoft Office Updates).
                                Start-ADTProcess -FilePath $file.FullName -ArgumentList '/quiet /norestart' -WindowStyle 'Hidden' -IgnoreExitCodes '*'
                                break
                            }
                            '.msu'
                            {
                                # Installation type for Windows updates using Windows Update Standalone Installer.
                                Start-ADTProcess -FilePath "$([System.Environment]::SystemDirectory)\wusa.exe" -ArgumentList "`"$($file.FullName)`" /quiet /norestart" -WindowStyle 'Hidden' -IgnoreExitCodes '*'
                                break
                            }
                            '.msp'
                            {
                                # Installation type for Windows Installer Patch
                                Start-ADTMsiProcess -Action 'Patch' -Path $file.FullName -IgnoreExitCodes '*'
                                break
                            }
                        }
                    }
                }
                catch
                {
                    Write-Error -ErrorRecord $_
                }
            }
            catch
            {
                Invoke-ADTFunctionErrorHandler -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState -ErrorRecord $_
            }
        }
    }

    end
    {
        Complete-ADTFunction -Cmdlet $PSCmdlet
    }
}