Public/Show-SCOMPropertyBag.ps1
|
Function Show-SCOMPropertyBag { <# .Synopsis This function is designed to launch a SCOM Property Bag PowerShell script or SCOM PowerShell discovery. It will output a formatted table to the screen. It can also output a hashtable or write the DataItem to an xml file.. .DESCRIPTION When authoring and troubleshooting a SCOM Property Bag or discovery PowerShell script it is helpful to be able to analyze the resulting DataItem. This script will make this process easier by outputing the DataItem(s) to the screen and optionally an XML file. The default output file will have the same path/name as the input file (+.xml). Script must properly use 'MOM.ScriptAPI' and output the proerty bag. Why is this useful? It's easy to output the DataItem to the screen but then it's difficult to read/analyze. If you open the .xml file with a fancy text editor like Notepad++ you can leverage the XMLTools plugin to format the data nicely. This is especially helpful for large property bags. Example : $api = New-Object -ComObject 'MOM.ScriptAPI' $bag = $api.CreatePropertyBag() $bag.AddValue('Message',"Some message") $api.Return($bag) #This method will output the DataItem text for analysis and is only used for script testing/analysis. .EXAMPLE # # This will execute the example script and output the property bag data of each bag ($Bag assumed to be in the script) to <$env:windir\Temp\><script file name>_<uniquetimestamp>.xml. Individual bags will be returned in a formatted table. PS C:\> Show-SCOMPropertyBag 'C:\Program Files\WindowsPowerShell\Modules\SCOMHelper\SCOMPowerShellPropertyBagTypes.ps1' .EXAMPLE # # This will execute MyScript.ps1 and output the property bag data of each bag ($Bag assumed to be in the script) to <$env:windir\Temp\><script file name>_<uniquetimestamp>.xml and then open the .xml file with the default application. PS C:\> Show-SCOMPropertyBag -FilePath C:\test\MyScript.ps1 -ShowXML .EXAMPLE # # This will execute C:\test\MyScript.ps1 and output the property bag data of each bag ($Bag assumed to be in the script) to <$env:windir\Temp\><script file name>_<uniquetimestamp>.xml. # All property bag data will be displayed to the screen in one formatted table. Use -Verbose switch to display output file location. PS C:\> Show-SCOMPropertyBag -FilePath C:\test\MyScript.ps1 -Format Combined -Verbose .EXAMPLE # # This will store the discovery dataitem to a hash table variable. PS C:\> $hash = Show-SCOMPropertyBag -FilePath 'C:\Test\MyDiscoveryScript.ps1' -Format HashTable PS C:\> $hash.1 | Out-GridView .NOTES Name: Show-SCOMPropertyBag Author: Tyson Paul History: 2022.06.14 - Added ability to digest PB collections as well as discovery dataitems. 2019.04.19 - Initial release. #> [CmdletBinding(DefaultParameterSetName='Parameter Set 1', SupportsShouldProcess=$true, PositionalBinding=$false)] Param ( # The full path to the input script (PowerShell .ps1) file. [Parameter(Mandatory=$false, ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true, ValueFromRemainingArguments=$false, Position=0, ParameterSetName='Parameter Set 1')] [ValidateNotNull()] [ValidateNotNullOrEmpty()] [string]$FilePath, # Powershell script file object. Typically this type of object would be obtained with Get-ChildItem. [Parameter(Mandatory=$false, ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true, ValueFromRemainingArguments=$false, Position=0, ParameterSetName='Parameter Set 2')] [ValidateNotNull()] [ValidateNotNullOrEmpty()] [System.IO.FileInfo]$File, [Parameter(Mandatory=$false, ValueFromPipeline=$false, ValueFromPipelineByPropertyName=$false, ValueFromRemainingArguments=$true, Position=1)] [ValidateSet("Combined", "Separate","HashTable")] # Output formated objects (property bags) instead of default hash table. Not applicable to discovery bags. # Separate: Individual bags # Combined: One large table containing all data from all bags # HashTable: Hash table containing all bag data. Useful for spying on data values that don't otherwise display in the table format due to large size. [string]$Format='Separate', # This will open the .XML output file type with default application. [switch]$ShowXML ) #region BEGIN Begin{ #----------------- FUNCTIONS ----------------------- Function FormatEach { Param( $hash ) Return $hash.Value } #--------------- END FUNCTIONS --------------------- $hashVariantTypes = [ordered]@{ 0 = "Empty" 1 = "Null (unconfirmed)" 2 = "Short (unconfirmed)" 3 = "Integer" 4 = "Float" 5 = "Double" 6 = "Currency (unconfirmed)" 7 = "Date" 8 = "String" 9 = "Object (unconfirmed)" 10 = "Error (unconfirmed)" 11 = "Boolean" 12 = "Variant (unconfirmed)" 13 = "DataObject (unconfirmed)" 14 = "Decimal" 15 = "Byte (unconfirmed)" 16 = "Char (unconfirmed)" 17 = "Byte" 18 = "Char" 20 = "Long" } # Setup temp dir for output file. Using a redirector to an output file is the only way I could find to obtain the property bag data. $TempDir = (Join-Path (Join-Path $env:Windir "Temp") (Join-Path 'SCOMHelper' 'Show-SCOMPropertyBag') ) New-Item -Type Directory -Path $TempDir -ErrorAction SilentlyContinue | Out-Null If (-not(Test-Path -Path $TempDir -PathType Container)) { Write-Error "Unable to create/access directory: [$($TempDir)]. " Return $false } $hash = @{} $int=1 } #endregion BEGIN Process { $batchstamp = Get-Date -Format o | ForEach-Object {$_ -replace ":", "."} If ([bool]$File){ If (Test-Path -PathType Leaf -Path $File.FullName -ErrorAction SilentlyContinue){ $OutFile = (Join-Path $TempDir "$($File.Name)_$($batchstamp)_.xml" ) cmd /c PowerShell.exe -file $File.FullName > $OutFile } } Else { If (-NOT [bool]$FilePath) { # Locate SCOMPowerShellPropertyBagTypes.ps1 — lives at module root. # After Phase 3 Public/Private reorg this script lives in .\Public\, so # check the parent of $PSScriptRoot first; fall back to flat layout for # legacy deployments where this script was at module root. $moduleRoot = Split-Path $PSScriptRoot -Parent $FilePath = Join-Path $moduleRoot 'SCOMPowerShellPropertyBagTypes.ps1' if (-not (Test-Path -LiteralPath $FilePath)) { $FilePath = Join-Path $PSScriptRoot 'SCOMPowerShellPropertyBagTypes.ps1' } } If (Test-Path -PathType Leaf -Path $FilePath -ErrorAction SilentlyContinue){ $OutFile = (Join-Path $TempDir "$(Split-Path $FilePath -Leaf)_$($batchstamp)_.xml") cmd /c PowerShell.exe -file $FilePath > $OutFile } Else { Throw "Problem with FilePath: [ $($FilePath) ]." } } # Remove generic typename that often appears as a result of standard '$bag' statement $Content = (Get-Content $OutFile -Raw ) If ($Content.Length) { $Content = $Content.Replace('System.__ComObject','').Replace('ÿ','') -Replace '\<Collection\>|\<\/Collection\>' $Content | Set-Content $OutFile $token = (Get-Date -f fffffff) # Add a delimiter after each DataItem, then split on it. $array = (($Content -replace '\<\/DataItem\>',"</DataItem>$($token)" ) -split $token) ForEach ($DI in $array[0..($array.Count -2)]){ # If Discovery bag If (([xml]$DI).DataItem.DiscoveryType -match '0') { #ForEach () { } ForEach ($Item in ([xml]$DI).DataItem) { [System.Collections.ArrayList]$arrayItems = @() 'type','time','sourceHealthServiceId','DiscoveryType','DiscoverySourceType','DiscoverySourceObjectId','DiscoverySourceManagedEntity' | ForEach-Object { $obj = New-Object PSCUSTOMOBJECT $obj | Add-Member -MemberType NoteProperty -Name "ClassInstance" -Value 'N/A' $obj | Add-Member -MemberType NoteProperty -Name 'Name' -Value $_ $obj | Add-Member -MemberType NoteProperty -Name 'Value' -Value ($Item.$_) $NULL = $arrayItems.Add($obj) } ForEach ($ClassInstance in ($Item.ClassInstances).ClassInstance){ ForEach ($Settings in $ClassInstance.Settings){ ForEach ($Setting in $Settings.Setting){ $obj = New-Object PSCUSTOMOBJECT $obj | Add-Member -MemberType NoteProperty -Name "ClassInstance" -Value ([string]$ClassInstance.TypeId) $obj | Add-Member -MemberType NoteProperty -Name "Name" -Value ([string]$Setting.Name) $obj | Add-Member -MemberType NoteProperty -Name "Value" -Value ([string]$Setting.Value) $NULL = $arrayItems.Add($obj) } } } }#end ForEach Item } #end Discovery bag type # Assume property bag Else { #ForEach ($DI in $array[0..($array.Count -2)]){ [System.Collections.ArrayList]$arrayItems = @() 'type','time','sourceHealthServiceId' | ForEach-Object { $obj = New-Object PSCUSTOMOBJECT $obj | Add-Member -MemberType NoteProperty -Name 'Name' -Value $_ $obj | Add-Member -MemberType NoteProperty -Name 'VariantType' -Value '' $obj | Add-Member -MemberType NoteProperty -Name 'Value' -Value (([xml]$DI).DataItem.$_) $NULL = $arrayItems.Add($obj) } ForEach ($Property in ([xml]$DI).DataItem.Property){ $obj = New-Object PSCUSTOMOBJECT $obj | Add-Member -MemberType NoteProperty -Name "Name" -Value ([string]$Property.Name) $obj | Add-Member -MemberType NoteProperty -Name "VariantType" -Value ("{0,2},{1}" -F $([int]$Property.VariantType), $($hashVariantTypes.[int]$Property.VariantType)) $obj | Add-Member -MemberType NoteProperty -Name "Value" -Value ([string]$Property.'#text') $NULL = $arrayItems.Add($obj) } } $hash.Add($int,$arrayItems) $int++ } If ($ShowXML) { # Open with default application & $OutFile } Write-Verbose "Output file: $OutFile" } #endif Content Else { Write-Output "The script returned no data." } } End { If ($Format -eq 'Separate'){ ForEach ($element in @($hash.GetEnumerator() )) { FormatEach -hash $element | Out-Host } } ElseIf ($Format -eq 'Combined'){ ForEach ($element in @($hash.GetEnumerator() )) { FormatEach -hash $element } } Else{ Return $hash } } } |