SHIMSOFT-Fundamentals.psm1
function Get-PropertyNames { <# .SYNOPSIS オブジェクト配列からプロパティ名を取得します。 .DESCRIPTION オブジェクト配列からプロパティ名を取得します。 構成要素の異なるオブジェクトの配列から、すべてのプロパティ名を取得します。 .EXAMPLE Get-PropertyName -Array $Array .EXAMPLE Get-PropertyName -Array $Array .EXAMPLE $Array | Get-PropertyName .EXAMPLE $Array | ft -Property (Get-PropertyName -Array $Array) .PARAMETER Array オブジェクト配列を指定してください。 .LINK .NOTES .INPUTS パイプラインからの入力可能です。 .OUTPUTS 文字列 #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseSingularNouns", "", Justification="Always multiple results.")] param( [Parameter(Mandatory=$false,Position=1,ValueFromPipeLine=$True)] [Array]$Array ) begin { $PropertyNames = @() } process { foreach ($A in $Array) { $PropertyNames += ($A | Get-Member -MemberType Properties -ErrorAction SilentlyContinue).Name } } end { ($PropertyNames | Sort-Object -Unique) } } function Get-MSProductLifeCycle { <# .SYNOPSIS マイクロソフト社製品の製品名からライフサイクル情報を確認します。 .DESCRIPTION マイクロソフト社製品の製品名からライフサイクル情報を確認します。 https://app-omaha-prod.azurewebsites.net/api/PublishedListings/Export(endOfSupportYear=0,endOfSupportMonths=0,family=%27%27,group=%27%27) インターネットに接続できない環境の場合は利用できません。 処理のため、モジュール実態フォルダ内に以下の3個のデータファイルが保存されます。 ・Microsoft 社 Web サイトからダウンロードした Excel ファイル ・Excel ファイルを CSV 変換した CSV ファイル ・CSV ファイルを処理に合わせて成型した XML ファイル .EXAMPLE Windows Server 2016 について確認したい場合。 Get-MSProductLifeCycle -Product "Windows Server 2016" .EXAMPLE Microsoft Product Life Cycle リストを強制的にリフレッシュしたい場合。 Get-MSProductLifeCycle -Refresh .EXAMPLE Microsoft Product Life Cycle リストをリフレッシュを抑制したい場合。 Get-MSProductLifeCycle -Product "Windows Server 2016" -NoRefresh ただし、処理に必要なデータファイルがそろっていない場合はリフレッシュ動作は強制されます。 .EXAMPLE 2022年中にメインストリームサポートが終了する製品を確認したい。 Get-MSProductLifeCycle -Product * | where {$_.MainstreamDate -ge "2022/01/01" -and $_.MainstreamDate -le "2022/12/31"} .EXAMPLE 2022年中に延長サポートが終了する製品を確認したい。 Get-MSProductLifeCycle -Product * | where {$_.ExtendedEndDateDate -ge "2022/01/01" -and $_.ExtendedEndDate -le "2022/12/31"} .EXAMPLE 2022年中にモダンサイクルポリシー製品の提供が終了する製品を確認したい。 Get-MSProductLifeCycle -Product * | where {$_.RetirementDate -ge "2022/01/01" -and $_.RetirementDate -le "2022/12/31"} .PARAMETER Product マイクロソフト製品名を指定します。半角スペースを含む場合は "Windows Server" のように指定します。 ワイルドカード指定できます。 "Exchange*2019" や "Windows 10*21H2" .PARAMETER Refresh Life Cycle 情報を再ダウンロードし、キャッシュをリフレッシュします。 Life Cycle 情報が古い場合(タイムスタンプが今月ではない) 場合はこのオプション指定有無にかかわらず最新データへのリフレッシします。 データ再生成に時間がかかります。(ver 1.7 以前と比べ処理時間は高速化されました) .PARAMETER NoRefresh Life Cycle 情報が古い場合(タイムスタンプが今月ではない) 場合も最新データへのリフレッシュをスキップします。 インターネット通信できない環境や、データリフレッシュにかかる時間を節約したい場合に利用します。 ただし、処理に必要なデータファイルがそろっていない場合はリフレッシュ動作は強制されます。 .LINK .NOTES https://app-omaha-prod.azurewebsites.net/api/PublishedListings/Export(endOfSupportYear=0,endOfSupportMonths=0,family=%27%27,group=%27%27) の Web サイト利用は無許可利用です。 .INPUTS なし。パイプラインからの入力は受け付けません。 .OUTPUTS ダウンロードした CSV データから生成されたオブジェクトです。 #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidUsingWriteHost", "", Justification="Progress information")] [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSReviewUnusedParameter", "", Justification="Parameter used.")] Param( [Parameter(Mandatory=$false,Position=1)] [String]$Product="no-product", [Parameter(Mandatory=$false,Position=2)] [Switch]$Refresh, [Parameter(Mandatory=$false,Position=3)] [Switch]$NoRefresh ) $PLCExcel = "\ProductLifeCycle_SourceData.xlsx" $PLCCSV = "\ProductLifeCycle.csv" $PLCCache = "\ProductLifeCycle.xml" if ($True -eq $Refresh) { Write-Host ("go to Refresh Cache") $CacheActive = $False } else { $CacheActive = $true } if ($False -eq (Test-Path -Path ("{0}{1}" -f $PSScriptRoot,$PLCExcel)) -or $False -eq (Test-Path -Path ("{0}{1}" -f $PSScriptRoot,$PLCCache)) -or $False -eq (Test-Path -Path ("{0}{1}" -f $PSScriptRoot,$PLCCSV))) { $CacheActive = $False } else { $File = Get-ChildItem -Path ("{0}{1}" -f $PSScriptRoot,$PLCExcel) if ((Get-Date -Date $file.LastWriteTime -Format "yyyyMM") -ne (Get-Date -Format "yyyyMM")) { if ($False -eq $NoRefresh) { $CacheActive = $False } else { Write-Host ("Cache file is old but No Refresh.") } } } if ($False -eq $CacheActive) { # Refresh Cache Write-Host ("Downloading Microsoft Prodcut Life Cycle List.") Write-Verbose ("Downloading... {0}" -f (get-date -format "hh:mm:ss")) $WebCli = New-Object System.Net.WebClient $url ="https://app-omaha-prod.azurewebsites.net/api/PublishedListings/Export(endOfSupportYear=0,endOfSupportMonths=0,family=%27%27,group=%27%27)" $WebCli.DownloadFile($url, ("{0}{1}" -f $PSScriptRoot,$PLCExcel)) Write-Verbose ("Download Complete. {0}" -f (get-date -format "hh:mm:ss")) Write-Host ("Generate Cache File...") Write-Verbose ("Open Excel file. {0}" -f (get-date -format "hh:mm:ss")) $File = Get-ChildItem -Path ("{0}{1}" -f $PSScriptRoot,$PLCExcel) $Excel =New-Object -ComObject Excel.Application $Excel.DisplayAlerts=$false $book = $Excel.Workbooks.Open($File.FullName) # $Sheet = $Excel.Worksheets.Item(1) $work = $book.SaveAs(("{0}{1}" -f $PSScriptRoot,$PLCCSV),6) [void]$book.Close($false) [void][System.Runtime.InteropServices.Marshal]::ReleaseComObject($book) Write-Verbose (" Closed book. {0}" -f (get-date -format "hh:mm:ss")) [void]$Excel.Quit() [void][System.Runtime.InteropServices.Marshal]::ReleaseComObject($Excel) Write-Verbose (" Closed Excel {0}" -f (get-date -format "hh:mm:ss")) if ($False -eq $Work) { Write-Error ("Can not SaveAs to CSV") break } Write-Verbose ("SaveAs CSV file. {0}" -f (get-date -format "hh:mm:ss")) $PLCwork = Get-Content -Path ("{0}{1}" -f $PSScriptRoot,$PLCCSV) -Encoding Default $PLCtitle = $PLCwork | Where-Object {$_ -like "Product,*"} $PLC = $PLCwork[($PLCwork.IndexOf($PLCtitle))..($PLCwork.Count-1)] | ConvertFrom-Csv Write-Verbose ("Column Title fetching... {0}" -f (get-date -format "hh:mm:ss")) $PropList = Get-PropertyNames -Array $PLC Write-Verbose ("Column Title fetch completed. {0}" -f (get-date -format "hh:mm:ss")) $ProductLifeCycle = @() Write-Verbose ("Records fetching... {0}" -f (get-date -format "hh:mm:ss")) foreach ($P in $PLC) { Write-Verbose (" Row Start. {0}" -f (get-date -format "hh:mm:ss")) $Mem = New-Object PSObject foreach ($Prop in $PropList) { if ($Prop -like "*Date" -and ("" -ne $P.($Prop))) { $Mem | Add-Member -MemberType NoteProperty -Name $Prop -Value (Get-Date -Date $P.($Prop) -Format "yyyy/MM/dd") } else { $Mem | Add-Member -MemberType NoteProperty -Name $Prop -Value $P.($Prop) } } $Mem | Add-Member -MemberType NoteProperty -Name ProductString -Value (@($Mem.Product,$Mem.Edition,$Mem.Release) -join " ") $ProductLifeCycle += $Mem Write-Verbose (" Row End. {0}" -f (get-date -format "hh:mm:ss")) } Write-Verbose ("Records fetch Completed and Close Excel. {0}" -f (get-date -format "hh:mm:ss")) $ProductLifeCycle | Export-Clixml -Path ("{0}{1}" -f $PSScriptRoot,$PLCCache) Write-Verbose ("Exported Cache file. {0}" -f (get-date -format "hh:mm:ss")) Write-Host ("Cache file created.") } else { Write-Verbose ("Using current cache file.") } $ProductLifeCycle = Import-Clixml -Path ("{0}{1}" -f $PSScriptRoot,$PLCCache) $Results = $ProductLifeCycle | Where-Object {$_.ProductString -like ("{0}*" -f $Product)} $defaultProperties = @("ProductString","MainstreamDate","ExtendedEndDate","RetirementDate") $defaultDisplayPropertySet = New-Object System.Management.Automation.PSPropertySet("DefaultDisplayPropertySet",[string[]]$defaultProperties) $PSStandardMembers = [System.Management.Automation.PSMemberInfo[]]@($defaultDisplayPropertySet) $Results | Add-Member -MemberType MemberSet -Name PSStandardMembers -Value $PSStandardMembers $Results } function ConvertTo-Narrow { <# .SYNOPSIS 全角文字列を半角文字列に変換します。 .DESCRIPTION 全角文字列を半角文字列に変換します。 すべての全角文字が半角に出来るわけではありません。 平仮名、片仮名、数字、アルファベットが変換できます。 全角平仮名は半角片仮名になります。 .EXAMPLE ConvertTo-Narrow -String "あいうえお" .EXAMPLE ConvertTo-Narrow -String "12345" .PARAMETER String 全角文字列を指定します。 .LINK .NOTES .INPUTS なし。パイプラインからの入力は受け付けません。 .OUTPUTS 半角に変換した文字列を返します。 #> Param( [Parameter(Mandatory=$true,Position=1)] [String]$String ) $a = "あいうえおかきくけこさしすせそたちつてとなにぬねのはひふへほまみむめもやゆよらりるれろわをんぁぃぅぇぉゃゅょっ0123456789" $a += "アイウエオカキクケコサシスセソタチツテトナニヌネノハヒフヘホマミムメモヤユヨラリルレロワヲンァィゥェォャュョッ" $a += "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" $a += "!#$%&()=~|-ー¥@{}「」[]【】〔〕`”`’`‘/*-+、,。.<>゛ ゜" $b = "アイウエオカキクケコサシスセソタチツテトナニヌネノハヒフヘホマミムメモヤユヨラリルレロワヲンァィゥェォャュョッ0123456789" $b += "アイウエオカキクケコサシスセソタチツテトナニヌネノハヒフヘホマミムメモヤユヨラリルレロワヲンァィゥェォャュョッ" $b += "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" $b += "!#`$%&()=~|-ー\@`{}[][][][]`"`'``/*-+,,..<>゙ ゚" $c = "ヴがぎぐげござじずぜぞだぢづでどばびぶべぼぱぴぷぺぽ" $c += "ヴガギグゲゴザジズゼゾダヂヅデドバビブベボパピプペポ" $d = "ヴガギグゲゴザジズゼゾダヂヅデドバビブベボパピプペポ" $d += "ヴガギグゲゴザジズゼゾダヂヅデドバビブベボパピプペポ" $Work = $String -Split "" $Work = $Work[1..($Work.Count-2)] $Result = "" foreach ($s in $Work) { if ($a.IndexOf($s) -ne -1) { # 普通の文字 $Result += $b[($a.IndexOf($s))] } else { if ($c.IndexOf($s) -ne -1) { # 濁音・半濁音 $Result += $d.Substring(($c.IndexOf($s)*2),2) } else { # 変換できない $Result += $s } } } $Result.Replace(" "," ") } function ConvertTo-Wide { <# .SYNOPSIS 半角文字列を全角文字列に変換します。 .DESCRIPTION 半角文字列を全角文字列に変換します。 すべての半角文字が全角に出来るわけではありません。 片仮名、数字、アルファベットが変換できます。 半角片仮名は全角片仮名になります。 .EXAMPLE ConverTo-Wide -String "アイウエオ" .EXAMPLE ConvertTo-Wide -String "12345" .PARAMETER String 全角文字列を指定します。 .LINK .NOTES .INPUTS なし。パイプラインからの入力は受け付けません。 .OUTPUTS 半角に変換した文字列を返します。 #> Param( [Parameter(Mandatory=$true,Position=1)] [String]$String ) $a = "アイウエオカキクケコサシスセソタチツテトナニヌネノハヒフヘホマミムメモヤユヨラリルレロワヲンァィゥェォャュョッ0123456789" $a += "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" $a += "!#$%&()=~|-ー¥@{}「」[]【】〔〕`”`’`‘/*-+、,。.<>゛ ゜" $b = "アイウエオカキクケコサシスセソタチツテトナニヌネノハヒフヘホマミムメモヤユヨラリルレロワヲンァィゥェォャュョッ0123456789" $b += "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" $b += "!#`$%&()=~|-ー\@`{}[][][][]`"`'``/*-+,,..<>゙ ゚" $c = "ヴガギグゲゴザジズゼゾダヂヅデドバビブベボパピプペポ" $d = "ヴガギグゲゴザジズゼゾダヂヅデドバビブベボパピプペポ" $Work = $String -Split "" $Work = $Work[1..($Work.Count-2)] $Result = "" $PreFetch = " " foreach ($s in $Work) { if ("゙ ゚".IndexOf($s) -ne -1 -and $d.IndexOf($PreFetch+$s) -ne -1) { $s = $PreFetch + $s $Result = $Result.Substring(0,$Result.Length-1) } if ($b.IndexOf($s) -ne -1) { # 普通の文字 $Result += $a[($b.IndexOf($s))] } else { if ($d.IndexOf($s) -ne -1) { # 濁音・半濁音 $Result += $c.Substring(($d.IndexOf($s)/2),1) } else { # 変換できない $Result += $s } } $PreFetch = $s } $Result.Replace(" "," ") } function Test-IsNumeric { <# .SYNOPSIS 指定した文字列が数値文字列か検証します。 数値文字列なら $True を、そうでないなら $False を返します。 数値文字列とは [int] などにキャスト変換できる文字列を指します。 .DESCRIPTION 指定した文字列が数値文字列か検証します。 数値文字列なら $True を、そうでないなら $False を返します。 .EXAMPLE Test-IsNumeric -String "100" 結果は $True .EXAMPLE Test-IsNumeric -String "123.4" 結果は $True .EXAMPLE Test-IsNumeric -String "1,234" 結果は $True .EXAMPLE Test-IsNumeric -String "123" 結果は $False 全角文字列は数値文字列とはみなされません。 .EXAMPLE Test-IsNumeric -String "123abc" 結果は $False .EXAMPLE Test-IsNumeric -String "百十五" 結果は $False .PARAMETER String 文字列を指定します。 .LINK .NOTES s .INPUTS なし。パイプラインからの入力は受け付けません。 .OUTPUTS 半角に変換した文字列を返します。 #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseDeclaredVarsMoreThanAssignments", "", Justification="The variable Work is suprress output int value.")] Param( [Parameter(Mandatory=$true,Position=1)] [String]$String ) Try { $Work = [int]$String; $True } catch { $False } } function Format-WindowsFeature { <# .SYNOPSIS Get-WindowsFeature 実行したときのような結果を表示します。 .DESCRIPTION Get-WindowsFeature コマンドを実行した結果を保存しておきたいときに、 Get-WindowsFeature | Export-Clixml -Path WindowsFeature.xml として情報を保存したとします。 保存したファイルなどを別の環境で確認したい場合に、以下のようにしたとします。 $WF = Import-Clixml -Path WindowsFeature.xml $FW すると Get-WindowsFeature を実行したときのような結果が出ずにがっかりするんです。 そこで、 Format-WindowsFeature でもう一度あの表示を得ることができます。 .EXAMPLE Format-WindowsFeature -WindowsFeature (Import-Clixml -Path WindowsFeature.xml) .PARAMETER WindowsFeatures Get-WindowsFeatures の結果を指定します。 .LINK .NOTES s .INPUTS なし。パイプラインからの入力は受け付けません。 .OUTPUTS 半角に変換した文字列を返します。 #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseDeclaredVarsMoreThanAssignments", "", Justification="The variable Work is suprress output int value.")] Param( [Parameter(Mandatory=$true,Position=1)] [Array]$WindowsFeature ) $WF_Object = @( "AdditionalInfo", "BestPracticesModelId", "DependsOn", "Depth", "Description", "DisplayName", "EventQuery", "FeatureType", "Installed", "InstallState", "Name", "Notification", "Parent", "Path", "PostConfigurationNeeded", "ServerComponentDescriptor", "SubFeatures", "SystemService") if ($WindowsFeature.GetType().BaseType.Name -ne "Array") { Write-Error "指定されたデータは Get-WindowsFeature の結果セットではありません。" break } $WF_Check = Get-PropertyNames -Array $WindowsFeature if ($WF_Check.Count -ne $WF_Object.Count) { Write-Error "指定されたデータは Get-WindowsFeature の結果セットではありません。" } $isFail = $false foreach ($CK in $WF_Check) { if ($WF_Object.IndexOf($CK) -eq -1) { Write-Error ("指定されたデータに不明なプロパティ値 {0} があります。" -f $CK) $isFail = $true } } if ($isFail) { break } foreach ($WF in $WindowsFeature) { Write-Output ("{0}[{1}] {2}" -f (" "*($WF.Depth-1)),(("X"* $WF.Installed) + (" " * !$WF.Installed)),$WF.DisplayName) } } Function Compare-ObjectProperties { <# .SYNOPSIS オブジェクトの一致性を確認します。 .DESCRIPTION オブジェクトの一致性を確認します。 戻り値として不一致のプロパティ一覧を返します。 戻り値が Null の場合、比較対象のオブジェクトは一致している可能性が濃厚です。 一致する項目は返されません。 戻り値に含まれる項目は以下の通りです。 PropertyName : 比較対象オブジェクトに含まれるプロパティの名前です。 RefValue : 参照側の値 RefExist : 参照側にプロパティが存在していたかどうか RefIsNull : 参照側が Null だったかどうか DiffValue : 比較側の値 DiffExist : 比較側にプロパティが存在していたかどうか DiffIsNull : 比較側が Null だったかどうか DiffType : 差異種別 オプションフィールド SubDiff : Deep 比較動作時に比較結果情報が含まれる。 DiffArray : プロパティタイプが ArrayList の時に比較参考値が含まれる。 DiffType の種類 <> : 両方に値が含まれ、相違している <=> : 両方に値が含まれ、相違している。そのプロパティ値以下の属性は一致していた。 <= : 参照側にのみ値が含まれている => : 比較側にのみ値が含まれている =//= : プロパティが ArrayList であり、アイテム数が同値であり、判定できない。 .EXAMPLE Compare-ObjectProperties -ReferenceObject $RefObj -DifferenceObject $DiffObj .EXAMPLE $Diff = Compare-ObjectProperties -ReferenceObject $RefObj -DifferenceObject $DiffObj if ($null -eq $Diff) { "一致" } else { "相違" } .EXAMPLE 比較除外したいプロパティ名がる場合、IgnoteProperties オプションを指定します。 Compare-ObjectProperties -ReferenceObject $RefObj -DifferenceObject $DiffObj -IgnoreProperties LastAccessTime,LastAccessTimeUtc あるオブジェクトで最終アクセス時間が異なることが予想されるが無視したい場合、IgnoreProperties オプションに指定すると比較除外されます。 無視したいプロパティ名が多数ある場合、配列変数に入れて渡すことが可能です。 $IgnoreProperties = @("LastAccessTime","LastAccessTimeUtc") Compare-ObjectProperties -ReferenceObject $RefObj -DifferenceObject $DiffObj -IgnoreProperties $IgnoreProperties .EXAMPLE 通常の比較で確認したい差異情報に辿り着かない場合、Deep オプションや Depth オプションを試みることができます。 オブジェクトのプロパティが階層的に情報を持つ場合、規定値で Depth 3 階層まで掘り下げて確認されます。 Depth 指定値を大きくすることでより深い階層の値を確認できる可能性があります。 処理時間がより多くかかります。 Compare-ObjectProperties -ReferenceObject $RefObj -DifferenceObject $DiffObj -Depth 4 通常比較で一致とされている部分でも Depp モードで比較することにより差異を見つけられる可能性があります。 処理時間がより多くかかります。 Compare-ObjectProperties -ReferenceObject $RefObj -DifferenceObject $DiffObj -Deep:$true .EXAMPLE プロパティのタイプが ArrayList の場合で、要素数が一致している場合同値と判断され (DiffType = =//=) た場合、 本当に一致していて確認不要の場合が多い見込みなので結果リストには含まれない様省略されます。 しかしながら、状況によりこのステータスの情報を確認したい場合があります。 その場合、IgnoreArrayNoDiffs オプションを $false に指定します。 Compare-ObjectProperties -ReferenceObject $RefObj -DifferenceObject $DiffObj -IgnoreArrayNoDiffs:$false .EXAMPLE Exchange Online 上で User1 と User2 メールボックスの設定値の差異を確認したい場合 あらかじめ Exchange Online に接続してあることを前提とした例を示します。 $RefMailbox = Get-Mailbox -Identity User1 $DiffMailbox = Get-Mailbox -Identity User2 Compare-ObjectProperties -ReferenceObject $RefMailbox -DifferenceObject $DiffMailbox .PARAMETER ReferenceObject 参照側オブジェクトを指定します。 .PARAMETER DifferenceObject 比較側オブジェクトを指定します。 .PARAMETER Deep Deep 比較モードを利用する場合、$True を指定します。規定値 $False です。 .PARAMETER IgnoreProperties 差異を気にしない無視してよいプロパティ名リストを指定します。 .PARAMETER Depth 階層的なプロパティ値を指定階層分掘り下げて比較します。規定値は 3 です。 .PARAMETER Prefix 内部的に利用します。階層的なプロパティを掘り下げ比較する際の親プロパティ名を保持します。 .PARAMETER IgnoreArrayNoDiffs プロパティのタイプが ArrayList のとき、参照側・比較側で要素数が一致する場合、おそらく同値 (DiffType = =//=) の場合に 結果リストに含めないように省略します。 これらを省略せず結果リストに含めたい場合 $False を指定します。 規定値 $True です。 .LINK .NOTES .INPUTS パイプラインからの入力出来ません。 .OUTPUTS 文字列 #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseSingularNouns", "", Justification="Always multiple results.")] [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidDefaultValueSwitchParameter", "", Justification="When specify False if nessesary.")] [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseProcessBlockForPipelineCommand", "", Justification="Not supported Pipeline Input")] param( [Parameter(Mandatory=$true,Position=1,ValueFromPipeLine=$False,ValueFromPipelineByPropertyName=$False)] $ReferenceObject, [Parameter(Mandatory=$true,Position=2,ValueFromPipeLine=$False,ValueFromPipelineByPropertyName=$False)] $DifferenceObject, [Parameter(Mandatory=$false,Position=3,ValueFromPipeLine=$False,ValueFromPipelineByPropertyName=$False)] [Boolean]$Deep=$false, [Parameter(Mandatory=$false,Position=4,ValueFromPipeLine=$False,ValueFromPipelineByPropertyName=$False)] [Array]$IgnoreProperties, [Parameter(Mandatory=$false,Position=5,ValueFromPipeLine=$False,ValueFromPipelineByPropertyName=$False)] [Int]$Depth=3, [Parameter(Mandatory=$false,Position=6,ValueFromPipeLine=$False,ValueFromPipelineByPropertyName=$False)] [String]$Prefix, [Parameter(Mandatory=$false,Position=7,ValueFromPipeLine=$False,ValueFromPipelineByPropertyName=$False)] [Switch]$IgnoreArrayNoDiffs=$True ) if (1 -eq $IgnoreProperties.Count) { $IgnoreProperties = @($IgnoreProperties[0].Split(","))} $RefPropName = Get-PropertyNames -Array $ReferenceObject $DiffPropName = Get-PropertyNames -Array $DifferenceObject $ComboPropName = ($RefPropName + $DiffPropName) | Sort-Object -Unique $DIFFS = @() foreach ($Prop in $ComboPropName) { Write-Verbose ("PropName : {0}.{1} // Depth : {2}" -f $Prefix,$Prop, $Depth) Write-Verbose (" {0} ;; {1}" -f $ReferenceObject.($Prop), $DifferenceObject.($Prop)) if ($Prop -notin $IgnoreProperties) { $isRefNull = $false $isDiffNull = $false if ($null -eq $ReferenceObject.($Prop)) { if ($null -eq $DifferenceObject.($Prop)) { Write-Verbose (" Equal both NULL") continue; } else { Write-Verbose (" Diff Ref NULL") $isRefNull = $true $DiffType ="=>" $Mem = New-Object PSObject if ("" -eq $Prefix) { $Mem | Add-Member -MemberType NoteProperty -Name PropertyName -Value $Prop } else { $Mem | Add-Member -MemberType NoteProperty -Name PropertyName -Value ("{0}.{1}" -f $Prefix,$Prop) } $Mem | Add-Member -MemberType NoteProperty -Name RefValue -Value $ReferenceObject.($Prop) $Mem | Add-Member -MemberType NoteProperty -Name RefExist -Value $true if (-1 -eq $RefPropName.IndexOf($Prop)) { Write-Verbose (" Not have Reference") $Mem.RefExist = $false $DiffType ="=>" } $Mem | Add-Member -MemberType NoteProperty -Name RefIsNull -Value $false if ($null -eq $RefPropName.IndexOf($Prop)) { $Mem.RefIsNull = $true } $Mem | Add-Member -MemberType NoteProperty -Name DiffValue -Value $DifferenceObject.($Prop) $Mem | Add-Member -MemberType NoteProperty -Name DiffExist -Value $true if (-1 -eq $DiffPropName.IndexOf($Prop)) { Write-Verbose (" Not have Difference") $Mem.DiffExist = $false $DiffType ="<=" } $Mem | Add-Member -MemberType NoteProperty -Name DiffIsNull -Value $false if ($null -eq $DiffPropName.IndexOf($Prop)) { $Mem.DiffIsNull = $true } $Mem | Add-Member -MemberType NoteProperty -Name DiffType -Value $DiffType $DIFFS += $Mem $DiffFlag = $false } } if ($null -ne $ReferenceObject.($Prop) -and $null -eq $DifferenceObject.($Prop)) { Write-Verbose (" Diff Diff NULL") $isDiffNull = $true $DiffType ="<=" $Mem = New-Object PSObject if ("" -eq $Prefix) { $Mem | Add-Member -MemberType NoteProperty -Name PropertyName -Value $Prop } else { $Mem | Add-Member -MemberType NoteProperty -Name PropertyName -Value ("{0}.{1}" -f $Prefix,$Prop) } $Mem | Add-Member -MemberType NoteProperty -Name RefValue -Value $ReferenceObject.($Prop) $Mem | Add-Member -MemberType NoteProperty -Name RefExist -Value $true if (-1 -eq $RefPropName.IndexOf($Prop)) { Write-Verbose (" Not have Reference") $Mem.RefExist = $false $DiffType ="=>" } $Mem | Add-Member -MemberType NoteProperty -Name RefIsNull -Value $false if ($null -eq $RefPropName.IndexOf($Prop)) { $Mem.RefIsNull = $true } $Mem | Add-Member -MemberType NoteProperty -Name DiffValue -Value $DifferenceObject.($Prop) $Mem | Add-Member -MemberType NoteProperty -Name DiffExist -Value $true if (-1 -eq $DiffPropName.IndexOf($Prop)) { Write-Verbose (" Not have Difference") $Mem.DiffExist = $false $DiffType ="<=" } $Mem | Add-Member -MemberType NoteProperty -Name DiffIsNull -Value $false if ($null -eq $DiffPropName.IndexOf($Prop)) { $Mem.DiffIsNull = $true } $Mem | Add-Member -MemberType NoteProperty -Name DiffType -Value $DiffType $DIFFS += $Mem $DiffFlag = $false } $isString = $false if ($false -eq $isRefNull -and $false -eq $isDiffNull) { if (($ReferenceObject.($Prop) | Get-Member -MemberType Method -ErrorAction SilentlyContinue | Where-Object {$_.Name -eq "toString"}).Count -eq 1 -and (($ReferenceObject.($Prop).GetType()).Name -ne "ArrayList")) { $isString = $true $DiffFlag = ($ReferenceObject.($Prop).toString() -eq $DifferenceObject.($Prop).toString()) } else { $DiffFlag = ($ReferenceObject.($Prop) -eq $DifferenceObject.($Prop)) } } if ($false -eq $DiffFlag) { Write-Verbose (" Diif String({0})" -f $isString) $DiffType ="<>" $Mem = New-Object PSObject if ("" -eq $Prefix) { $Mem | Add-Member -MemberType NoteProperty -Name PropertyName -Value $Prop } else { $Mem | Add-Member -MemberType NoteProperty -Name PropertyName -Value ("{0}.{1}" -f $Prefix,$Prop) } switch (($ReferenceObject.($Prop).GetType()).Name) { "ArrayList" { if (($ReferenceObject.($Prop).Count -eq 0) -and ($DifferenceObject.($Prop).Count -eq 0)) { $DIffType="==" } else { $DiffArray = Compare-Object -ReferenceObject $ReferenceObject.($Prop) -DifferenceObject $DifferenceObject.($Prop) $Mem | Add-Member -MemberType NoteProperty -Name DiffArray -Value $DiffArray if ($ReferenceObject.($Prop).Count -lt $DifferenceObject.($Prop).Count) { $DiffType="=>" } else { if ($ReferenceObject.($Prop).Count -gt $DifferenceObject.($Prop).Count) { $DiffType="<=" } else { $DiffType="=//=" if ($null -ne $DiffArray) { $DiffType="<>" } else { if ($True -eq $IgnoreArrayNoDiffs) { $DiffType="==" } } } } } } default { if ((($false -eq $isString) -or ($true -eq $Deep)) -and (($ReferenceObject.($Prop)|Get-Member -MemberType Properties -ErrorAction SilentlyContinue).Count -ge 1 -and (-1 -ne $RefPropName.IndexOf($Prop)) -and (-1 -ne $DiffPropName.IndexOf($Prop)))) { if ((0 -lt $Depth) -and -($false -eq $isRefNull) -and -($false -eq $isDiffNull)) { Write-Verbose (" go Deep") if ($null -eq $Prefix) { $SubDiff = Compare-ObjectProperties -ReferenceObject $ReferenceObject.($Prop) -DifferenceObject $DifferenceObject.($Prop) -Deep:$Deep -IgnoreProperties $IgnoreProperties -Depth ($Depth-1) -Prefix $Prop -IgnoreArrayNoDiffs:$IgnoreArrayNoDiffs } else { $SubDiff = Compare-ObjectProperties -ReferenceObject $ReferenceObject.($Prop) -DifferenceObject $DifferenceObject.($Prop) -Deep:$Deep -IgnoreProperties $IgnoreProperties -Depth ($Depth-1) -Prefix ("{0}.{1}" -f $Prefix,$Prop) -IgnoreArrayNoDiffs:$IgnoreArrayNoDiffs } } else { if ($false -eq $isRefNull -and $false -eq $isDiffNull) { if ($ReferenceObject.($Prop).toString() -ne $DifferenceObject.($Prop).toString()) { $SubMem = New-Object PSObject if ($null -eq $Prefix) { $SubMem | Add-Member -MemberType NoteProperty -Name PropertyName -Value $Prop } else { Write-Debug ("Check PropertyName") $SubMem | Add-Member -MemberType NoteProperty -Name PropertyName -Value ("{0}.{1}" -f $Prefix,$Prop) } $SubMem | Add-Member -MemberType NoteProperty -Name RefValue -Value $ReferenceObject.($Prop) $SubMem | Add-Member -MemberType NoteProperty -Name RefExist -Value $true $SubMem | Add-Member -MemberType NoteProperty -Name DiffValue -Value $DifferenceObject.($Prop) $SubMem | Add-Member -MemberType NoteProperty -Name DiffExist -Value $true $SubMem | Add-Member -MemberType NoteProperty -Name DiffType -Value "<>" $SubDiff = @($SubMem) } } } if (0 -eq $SubDiff.Count) { Write-Verbose (" Deep Equal") $DiffType = "<=>" if ($true -eq $isString -and $true -eq $DiffFlag) { $DiffType = "<>" Write-Verbose (" but Diff Reason toString") } } else { $Mem | Add-Member -MemberType NoteProperty -Name SubDiff -Value $SubDiff } } } } $Mem | Add-Member -MemberType NoteProperty -Name RefValue -Value $ReferenceObject.($Prop) $Mem | Add-Member -MemberType NoteProperty -Name RefExist -Value $true if (-1 -eq $RefPropName.IndexOf($Prop)) { Write-Verbose (" Not have Reference") $Mem.RefExist = $false $DiffType ="=>" } $Mem | Add-Member -MemberType NoteProperty -Name RefIsNull -Value $false if ($null -eq $RefPropName.IndexOf($Prop)) { $Mem.RefIsNull = $true } $Mem | Add-Member -MemberType NoteProperty -Name DiffValue -Value $DifferenceObject.($Prop) $Mem | Add-Member -MemberType NoteProperty -Name DiffExist -Value $true if (-1 -eq $DiffPropName.IndexOf($Prop)) { Write-Verbose (" Not have Difference") $Mem.DiffExist = $false $DiffType ="<=" } $Mem | Add-Member -MemberType NoteProperty -Name DiffIsNull -Value $false if ($null -eq $DiffPropName.IndexOf($Prop)) { $Mem.DiffIsNull = $true } $Mem | Add-Member -MemberType NoteProperty -Name DiffType -Value $DiffType if ("==" -ne $DiffType) { $DIFFS += $Mem } } else { Write-Verbose (" Equal") if ($True -eq $Deep -and ($ReferenceObject.($Prop)|Get-Member -MemberType Properties).Count -ge 1) { if (0 -lt $Depth) { Write-Verbose (" go Deep") if ("" -eq $Prefix) { $SubDiff = Compare-ObjectProperties -ReferenceObject $ReferenceObject.($Prop) -DifferenceObject $DifferenceObject.($Prop) -Deep $Deep -IgnoreProperties $IgnoreProperties -Depth ($Depth-1) -Prefix $Prop -IgnoreArrayNoDiffs:$IgnoreArrayNoDiffs } else { $SubDiff = Compare-ObjectProperties -ReferenceObject $ReferenceObject.($Prop) -DifferenceObject $DifferenceObject.($Prop) -Deep $Deep -IgnoreProperties $IgnoreProperties -Depth ($Depth-1) -Prefix ("{0}.{1}" -f $Prefix,$Prop) -IgnoreArrayNoDiffs:$IgnoreArrayNoDiffs } if (0 -ne $SubDiff.Count) { Write-Verbose (" Deep Diff") $DiffType ="<>" $Mem = New-Object PSObject $Mem | Add-Member -MemberType NoteProperty -Name PropertyName -Value $Prop $Mem | Add-Member -MemberType NoteProperty -Name SubDiff -Value $SubDiff $Mem | Add-Member -MemberType NoteProperty -Name RefValue -Value $ReferenceObject.($Prop) $Mem | Add-Member -MemberType NoteProperty -Name RefExist -Value $true $Mem | Add-Member -MemberType NoteProperty -Name DiffValue -Value $DifferenceObject.($Prop) $Mem | Add-Member -MemberType NoteProperty -Name DiffExist -Value $true $Mem | Add-Member -MemberType NoteProperty -Name DiffType -Value $DiffType $DIFFS += $Mem } } } } } else { Write-Verbose (" Ignored") } } $defaultProperties = @("PropertyName","DiffType","RefValue","DiffValue") $defaultDisplayPropertySet = New-Object System.Management.Automation.PSPropertySet("DefaultDisplayPropertySet",[string[]]$defaultProperties) $PSStandardMembers = [System.Management.Automation.PSMemberInfo[]]@($defaultDisplayPropertySet) $DIFFS | Add-Member -MemberType MemberSet -Name PSStandardMembers -Value $PSStandardMembers $DIFFS } Export-ModuleMember -Function Get-PropertyNames, Get-MSProductLifeCycle, ConvertTo-Narrow, ConvertTo-Wide, Test-IsNumeric, Format-WindowsFeature, Compare-ObjectProperties |