Scripts/Install-OSDUpdatePackageOffice.ps1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
#Requires -Version 5

<#
.SYNOPSIS
    Standalone script for applying Legacy Office Updates
 
.DESCRIPTION
    Standalone script for applying Legacy Office Updates
 
.NOTES
    Author: David Segura
    Website: osdeploy.com
    Twitter: @SeguraOSD
    Version: 19.6.25.0
#>

function Convert-GuidToCompressedGuid {
    <#
    .SYNOPSIS
        This converts a GUID to a compressed GUID also known as a product code.    
    .DESCRIPTION
        This function will typically be used to figure out the product code
        that matches up with the product code stored in the 'SOFTWARE\Classes\Installer\Products'
        registry path to a MSI installer GUID.
    .EXAMPLE
        Convert-GuidToCompressedGuid -Guid '{7C6F0282-3DCD-4A80-95AC-BB298E821C44}'
     
        This example would output the compressed GUID '2820F6C7DCD308A459CABB92E828C144'
    .PARAMETER Guid
        The GUID you'd like to convert.
    .LINK
        https://www.adamtheautomator.com/compressed-guid-with-powershell/
    #>

    [CmdletBinding()]
    [OutputType()]
    param (
        [Parameter(ValueFromPipeline, ValueFromPipelineByPropertyName, Mandatory)]
        [string]$Guid
    )
    begin {
        $Guid = $Guid.Replace('-', '').Replace('{', '').Replace('}', '')
    }
    process {
        try {
            $Groups = @(
                $Guid.Substring(0, 8).ToCharArray(),
                $Guid.Substring(8, 4).ToCharArray(),
                $Guid.Substring(12, 4).ToCharArray(),
                $Guid.Substring(16, 16).ToCharArray()
            )
            $Groups[0..2] | foreach {
                [array]::Reverse($_)
            }
            $CompressedGuid = ($Groups[0..2] | foreach { $_ -join '' }) -join ''
            
            $chararr = $Groups[3]
            for ($i = 0; $i -lt $chararr.count; $i++) {
                if (($i % 2) -eq 0) {
                    $CompressedGuid += ($chararr[$i+1] + $chararr[$i]) -join ''
                }
            }
            $CompressedGuid
        } catch {
            Write-Error $_.Exception.Message    
        }
    }
}

function Convert-CompressedGuidToGuid {
<#
    .SYNOPSIS
        This converts a compressed GUID also known as a product code into a GUID.    
    .DESCRIPTION
        This function will typically be used to figure out the MSI installer GUID
        that matches up with the product code stored in the 'SOFTWARE\Classes\Installer\Products'
        registry path.
    .EXAMPLE
        Convert-CompressedGuidToGuid -CompressedGuid '2820F6C7DCD308A459CABB92E828C144'
     
        This example would output the GUID '{7C6F0282-3DCD-4A80-95AC-BB298E821C44}'
    .PARAMETER CompressedGuid
        The compressed GUID you'd like to convert.
    .LINK
        https://www.adamtheautomator.com/convert-compressed-guid-to-guid/
    #>

    [CmdletBinding()]
    [OutputType([System.String])]
    param (
        [Parameter(ValueFromPipeline, ValueFromPipelineByPropertyName, Mandatory)]
        [ValidatePattern('^[0-9a-fA-F]{32}$')]
        [string]$CompressedGuid
    )
    process {
        $Indexes = [ordered]@{
            0 = 8;
            8 = 4;
            12 = 4;
            16 = 2;
            18 = 2;
            20 = 2;
            22 = 2;
            24 = 2;
            26 = 2;
            28 = 2;
            30 = 2
        }
        #$Guid = '{'
        $Guid = ''
        foreach ($index in $Indexes.GetEnumerator()) {
            $part = $CompressedGuid.Substring($index.Key, $index.Value).ToCharArray()
            [array]::Reverse($part)
            $Guid += $part -join ''
        }
        $Guid = $Guid.Insert(9,'-').Insert(14, '-').Insert(19, '-').Insert(24, '-')
        #$Guid + '}'
        $Guid + ''
    }
}
#======================================================================================
# Begin
#======================================================================================
Write-Host "OSDUpdate Microsoft Office" -ForegroundColor Green
#======================================================================================
# Current Path
#======================================================================================
$Invocation = (Get-Variable MyInvocation -Scope Script).Value
$ScriptPath = Split-Path -Parent $Invocation.MyCommand.Path
#======================================================================================
# Installed Patches
#======================================================================================
$PatchesInstalledRegistry = @()
$PatchesInstalledRegistry = 'HKLM:\SOFTWARE\Classes\Installer\Patches'
$PatchesInstalledProductCode = @()
$PatchesInstalledProductCode = Get-ChildItem -Path $PatchesInstalledRegistry -EA SilentlyContinue | Select-Object -Property @{Name="ProductCode"; Expression = {$_.PSChildName}} -Unique
$PatchesInstalledGuids = @()
foreach ($InstalledPatch in $PatchesInstalledProductCode) {
    $InstalledPatchGuid = Convert-CompressedGuidToGuid -CompressedGuid "$($InstalledPatch.ProductCode)"
    $PatchesInstalledGuids += $InstalledPatchGuid
}
#======================================================================================
# Available Patches (MSP's)
#======================================================================================
$PatchesAvailable = @()
$PatchesAvailable = Get-ChildItem "$ScriptPath" -Recurse -File -Include *.msp | Select-Object -Property LastWriteTime,Name,Length,FullName,Directory,BaseName,Extension
$PatchesAvailable = $PatchesAvailable | Sort-Object -Property @{Expression = {$_.LastWriteTime}; Ascending = $true}, Length -Descending
#======================================================================================
# Get Patch XML Information
#======================================================================================
foreach ($Patch in $PatchesAvailable) {
    $PatchXml = "$($Patch.Directory)\$($Patch.BaseName).xml"

    $Patch | Add-Member -MemberType NoteProperty -Name PatchGuid -value ''
    $Patch | Add-Member -MemberType NoteProperty -Name ProductCode -value ''
    $Patch | Add-Member -MemberType NoteProperty -Name TargetProductCode -value ''

    if (Test-Path $PatchXml) {
        $xml = [xml](Get-Content $PatchXml)
        $Patch.PatchGuid = $($xml.MsiPatch | Select PatchGuid).PatchGuid
        $Patch.ProductCode = Convert-GuidToCompressedGuid -Guid $($Patch.PatchGuid)
        $Patch.TargetProductCode = $($xml.MsiPatch.TargetProductCode)
    }
}
#======================================================================================
# Set InstallationStatus
#======================================================================================
foreach ($Patch in $PatchesAvailable) {
    $Patch | Add-Member -MemberType NoteProperty -Name InstallStatus -value ''
    foreach ($PatchInstalled in $PatchesInstalledProductCode) {
        if ($Patch.ProductCode -eq $PatchInstalled.ProductCode) {
            $Patch.InstallStatus = 'Installed'
        }
    }
}
#======================================================================================
# Install Updates
#======================================================================================
foreach ($Patch in $PatchesAvailable) {
    $PatchName = $($Patch.Directory) | Split-Path -Leaf

    if ($Patch.InstallStatus -eq 'Installed') {
        Write-Host "Installed: $PatchName $($Patch.Name)" -ForegroundColor DarkGray
    } else {
        Write-Host "$PatchName $($Patch.Name)" -ForegroundColor Cyan
        msiexec /p "$($Patch.FullName)" /qn REBOOT=ReallySuppress MSIRESTARTMANAGERCONTROL=Disable | Out-Null
    }
}