Private/Kinds/OfficeAddins.ps1
|
# The OfficeAddins Kind: what else is loaded inside Word, Excel or Outlook. # # An add-in is code from a third party running inside the application, and it is the # faulting module in most Office crashes a Technician will ever see. The count is the # Finding; the list beneath it is what names the one to disable. # LoadBehavior, as Office records it. 3 is "loaded, and load at startup", which is the only # value that means the add-in is actually in the process. 2 and 0 are installed and idle; # 9 and 8 are load-on-demand. Numeric because it is a registry value, not a display string. $script:OfficeAddinLoadBehaviorActive = 3 # The add-ins Office and Teams install themselves, by ProgId: OneNote, the Outlook Social # Connector, Skype for Business, Exchange Unified Messaging, the Teams meeting add-in, # VBA, the Access data collection, the SharePoint colleague import and Business # Connectivity Services. They are on nearly every machine and nobody put them there, so # they do not count towards "many add-ins". The default of OfficeOwnAddinPattern. $script:OfficeOwnAddinPattern = '^(OneNote\.OutlookAddin|OscAddin\.Connect|UCAddin\.LyncAddin\.\d+|UmOutlookAddin\.FormRegionAddin|TeamsAddin\.FastConnect|Microsoft\.VbaAddinForOutlook\.\d+|AccessAddin\.DC|ColleagueImport\.ColleagueImportAddin|BCSAddin\.Connect)$' function Get-OfficeAddinsData { [CmdletBinding()] [OutputType([psobject])] param([hashtable]$Parameters = @{}) $application = Get-Parameter $Parameters 'OfficeApp' '' if (-not $application) { return [pscustomobject]@{ PSTypeName = 'Gutcheck.Data.OfficeAddins' OfficeApp = '' Addins = @() DisabledItems = $null } } # Click-to-Run keeps a virtualised copy of the machine hive, so an add-in registered # by an installer shows up there and nowhere else. $roots = @( "HKCU:\Software\Microsoft\Office\$application\Addins" "HKLM:\Software\Microsoft\Office\$application\Addins" "HKLM:\Software\WOW6432Node\Microsoft\Office\$application\Addins" "HKLM:\SOFTWARE\Microsoft\Office\ClickToRun\REGISTRY\MACHINE\Software\Microsoft\Office\$application\Addins" "HKLM:\SOFTWARE\Microsoft\Office\ClickToRun\REGISTRY\MACHINE\Software\WOW6432Node\Microsoft\Office\$application\Addins" ) $addins = @(foreach ($root in $roots) { if (-not (Test-Path $root)) { continue } Get-ChildItem $root -ErrorAction SilentlyContinue | ForEach-Object { $properties = Get-ItemProperty $_.PSPath -ErrorAction SilentlyContinue [pscustomobject]@{ ProgId = $_.PSChildName Name = $properties.FriendlyName LoadBehavior = $properties.LoadBehavior Key = $root } } }) # Office disables what crashed it and never mentions it again, so a Customer whose # add-in stopped working has a machine that will not say why. $disabled = $null $resiliency = Get-Item "HKCU:\Software\Microsoft\Office\16.0\$application\Resiliency\DisabledItems" ` -ErrorAction SilentlyContinue if ($resiliency) { $disabled = $resiliency.ValueCount } [pscustomobject]@{ PSTypeName = 'Gutcheck.Data.OfficeAddins' OfficeApp = $application Addins = $addins DisabledItems = $disabled } } function ConvertTo-OfficeAddinsFinding { [CmdletBinding()] [OutputType([psobject])] param( [AllowNull()]$Data, [hashtable]$Parameters = @{} ) $warnAbove = Get-Parameter $Parameters 'LoadedAddinWarnAbove' 6 $failAbove = Get-Parameter $Parameters 'LoadedAddinFailAbove' ([double]::MaxValue) $application = Get-DataProperty $Data 'OfficeApp' if (-not $application) { return New-Finding -Category Apps -Check (Get-Text 'Check.OfficeAddins.OfficeAddIns') -Severity INFO ` -Value (Get-Text 'Value.OfficeAddins.NoOfficeApp') ` -Hint (Get-Text 'Hint.OfficeAddins.ThisCheckDefinitionNamesNo') } $loaded = @((Get-DataCollection $Data 'Addins') | Where-Object { (ConvertTo-Number $_.LoadBehavior) -eq $script:OfficeAddinLoadBehaviorActive } | Sort-Object ProgId -Unique) # Judged on what somebody installed. Office's own are named after them: they load # too, and an add-in that is switched off to test is one of them as often as not. $ownPattern = "$(Get-Parameter $Parameters 'OfficeOwnAddinPattern' $script:OfficeOwnAddinPattern)" $own = @($loaded | Where-Object { $ownPattern -and "$($_.ProgId)" -match $ownPattern }) $others = @($loaded | Where-Object { $own -notcontains $_ }) $value = ('{0}: {1}' -f $others.Count, (($others | ForEach-Object { $_.ProgId }) -join ', ')).TrimEnd(': ') if ($own.Count) { $value = '{0} | {1}' -f $value, ((Get-Text 'Value.OfficeAddins.OfficeOwn') -f $own.Count, (($own | ForEach-Object { $_.ProgId }) -join ', ')) } New-Finding -Category Apps -Check ((Get-Text 'Check.OfficeAddins.ComAddinsSetToLoad') -f $application) ` -Severity (Get-Severity $others.Count $warnAbove $failAbove) ` -Value $value ` -Hint ((Get-Text 'Hint.OfficeAddins.AddinsSlowDown') -f $application) $disabled = ConvertTo-Number (Get-DataProperty $Data 'DisabledItems') if ($null -ne $disabled -and $disabled -gt 0) { New-Finding -Category Apps -Check ((Get-Text 'Check.OfficeAddins.DisabledItems') -f $application) ` -Severity WARN -Value $disabled ` -Hint ((Get-Text 'Hint.OfficeAddins.HasDisabledItems') -f $application) } } function ConvertTo-OfficeAddinsSection { [CmdletBinding()] [OutputType([psobject])] param([AllowNull()]$Data) $application = Get-DataProperty $Data 'OfficeApp' if (-not $application) { return } # LoadBehavior is a number in the registry and is shown with what it means before it: # a code is never shown bare. New-Section -Title ((Get-Text 'Title.OfficeAddins.ComAddins') -f $application) -Row @( (Get-DataCollection $Data 'Addins') | ForEach-Object { $row = [ordered]@{} $row[(Get-Text 'Column.OfficeAddins.ProgId')] = $_.ProgId $row[(Get-Text 'Column.OfficeAddins.Name')] = $_.Name $row[(Get-Text 'Column.OfficeAddins.LoadBehavior')] = Get-OfficeAddinLoadBehaviorText -LoadBehavior $_.LoadBehavior $row[(Get-Text 'Column.OfficeAddins.Key')] = $_.Key [pscustomobject]$row } ) } function Get-OfficeAddinLoadBehaviorText { <# .SYNOPSIS "laedt beim Start (3)": what a LoadBehavior says, with the number behind it. The number alone for one Office does not document. Pure. .DESCRIPTION The values Microsoft documents for the LoadBehavior of an Office add-in: whether it is loaded now (the low bit) and when Office loads it - never by itself, at startup, on demand, or once at the next start. #> [CmdletBinding()] [OutputType([string])] param([AllowNull()]$LoadBehavior) $number = ConvertTo-Number $LoadBehavior if ($null -eq $number) { return "$LoadBehavior" } $meaning = switch ([double]$number) { 0 { Get-Text 'Value.OfficeAddins.LoadBehavior.0' } 1 { Get-Text 'Value.OfficeAddins.LoadBehavior.1' } 2 { Get-Text 'Value.OfficeAddins.LoadBehavior.2' } 3 { Get-Text 'Value.OfficeAddins.LoadBehavior.3' } 8 { Get-Text 'Value.OfficeAddins.LoadBehavior.8' } 9 { Get-Text 'Value.OfficeAddins.LoadBehavior.9' } 16 { Get-Text 'Value.OfficeAddins.LoadBehavior.16' } default { '' } } if (-not $meaning) { return "$number" } (Get-Text 'Value.OfficeAddins.LoadBehavior') -f $meaning, $number } |