param( [Parameter(Mandatory)] [AllowNull()] $oldVMs, [Parameter(Mandatory)] [AllowNull()] $newVMs, [ValidateSet("VMonly", "All")] [string]$Category = "" ) if ($Category -eq "") { $Category = "All" if (($oldVms -ne $null) -and ($oldVms.Count -gt 0)) { $objTypes = $oldVms[0].PSObject.TypeNames if ($objTypes -contains "Microsoft.HyperV.PowerShell.VirtualMachine") { $Category = "VMonly" } } if (($newVms -ne $null) -and ($newVms.Count -gt 0)) { $objTypes = $newVms[0].PSObject.TypeNames if ($objTypes -contains "Microsoft.HyperV.PowerShell.VirtualMachine") { $Category = "VMonly" } } } $result = @() function Compare-SimpleProperties { param( $OldObject, $NewObject, [string]$Category, $VM, [string[]]$ExcludeProperty = @() ) if ($null -eq $OldObject -and $null -eq $NewObject) { return } # オブジェクト自体の追加・削除 if ($null -eq $OldObject) { return [pscustomobject]@{ ChangeType = 'Added' VMName = $VM.Name VMId = $VM.VMId Category = $Category Property = $null OldValue = $null NewValue = $null } } if ($null -eq $NewObject) { return [pscustomobject]@{ ChangeType = 'Removed' VMName = $VM.Name VMId = $VM.VMId Category = $Category Property = $null OldValue = $null NewValue = $null } } foreach ($prop in $NewObject.PSObject.Properties.Name) { if ($prop -in $ExcludeProperty) { continue } $oldValue = $OldObject.$prop $newValue = $NewObject.$prop # 新旧とも単純値だけを対象にする $oldSimple = $null -eq $oldValue -or $oldValue.GetType().IsPrimitive -or $oldValue -is [string] -or $oldValue -is [decimal] -or $oldValue -is [datetime] -or $oldValue -is [guid] -or $oldValue.GetType().IsEnum $newSimple = $null -eq $newValue -or $newValue.GetType().IsPrimitive -or $newValue -is [string] -or $newValue -is [decimal] -or $newValue -is [datetime] -or $newValue -is [guid] -or $newValue.GetType().IsEnum if (-not $oldSimple -or -not $newSimple) { continue } # Deserialized Enumなどの型差を避け、表示上の値で比較 $oldText = if ($null -eq $oldValue) { $null } else { $oldValue.ToString() } $newText = if ($null -eq $newValue) { $null } else { $newValue.ToString() } if ($oldText -ne $newText) { [pscustomobject]@{ ChangeType = 'Changed' VMName = $VM.Name VMId = $VM.VMId Category = $Category Property = $prop OldValue = $oldText NewValue = $newText } } } } # 既存VM / 追加VM foreach ($newVM in $newVMs) { $oldVM = $oldVMs | Where-Object VMId -eq $newVM.VMId # 追加されたVM if (-not $oldVM) { $result += [pscustomobject]@{ ChangeType = 'Added' VMName = $newVM.Name VMId = $newVM.VMId Category = 'VM' Property = $null OldValue = $null NewValue = $null } continue } # ----------------------------- # Get-VM 本体 # ----------------------------- $result += Compare-SimpleProperties ` -OldObject $oldVM ` -NewObject $newVM ` -Category 'VM' ` -VM $newVM ` -ExcludeProperty @( 'CPUUsage' 'MemoryAssigned' 'MemoryDemand' 'MemoryStatus' 'NumaAligned' 'NumaNodesCount' 'NumaSocketCount' 'Heartbeat' 'IntegrationServicesState' 'Uptime' 'OperationalStatus' 'PrimaryOperationalStatus' 'SecondaryOperationalStatus' 'StatusDescriptions' 'PrimaryStatusDescription' 'SecondaryStatusDescription' 'Status' 'ReplicationHealth' 'ReplicationState' 'State' 'SizeOfSystemFiles' 'GuestControlledCacheTypes' 'LowMemoryMappedIoSpace' 'HighMemoryMappedIoSpace' 'HighMemoryMappedIoBaseAddress' 'VMIntegrationService' 'HA' 'VCPU' 'VRAM' 'VMFirmware' 'VMSecurity' 'VNIC' 'VHD' 'VMSnapshot' ) # ----------------------------- # Get-VM のコレクションプロパティの比較 # ----------------------------- # Groups $oldValue = @($oldVM.Groups) | ForEach-Object { $_.Name } | Sort-Object $newValue = @($newVM.Groups) | ForEach-Object { $_.Name } | Sort-Object if (($oldValue -join ',') -ne ($newValue -join ',')) { $result += [pscustomobject]@{ ChangeType = 'Changed' VMName = $newVM.Name VMId = $newVM.VMId Category = 'VM' Property = 'Groups' OldValue = $oldValue -join ',' NewValue = $newValue -join ',' } } # ComPort1 Pathで比較   $oldValue = $oldVM.ComPort1.Path $newValue = $newVM.ComPort1.Path if ($oldValue -ne $newValue) { $result += [pscustomobject]@{ ChangeType = 'Changed' VMName = $newVM.Name VMId = $newVM.VMId Category = 'VM' Property = 'ComPort1' OldValue = $oldValue NewValue = $newValue } } # ComPort2 Pathで比較   $oldValue = $oldVM.ComPort2.Path $newValue = $newVM.ComPort2.Path if ($oldValue -ne $newValue) { $result += [pscustomobject]@{ ChangeType = 'Changed' VMName = $newVM.Name VMId = $newVM.VMId Category = 'VM' Property = 'ComPort2' OldValue = $oldValue NewValue = $newValue } } # FibreChannelHostBusAdapters Countで比較 $oldCount = @($oldVM.FibreChannelHostBusAdapters).Count $newCount = @($newVM.FibreChannelHostBusAdapters).Count if ($oldCount -ne $newCount) { $result += [pscustomobject]@{ ChangeType = 'Changed' VMName = $newVM.Name VMId = $newVM.VMId Category = 'VM' Property = 'FibreChannelHostBusAdapters.Count' OldValue = $oldCount NewValue = $newCount } } # DVDDrives Countで比較 $oldCount = @($oldVM.DVDDrives).Count $newCount = @($newVM.DVDDrives).Count if ($oldCount -ne $newCount) { $result += [pscustomobject]@{ ChangeType = 'Changed' VMName = $newVM.Name VMId = $newVM.VMId Category = 'VM' Property = 'DVDDrives.Count' OldValue = $oldCount NewValue = $newCount } } # HardDrives Countで比較 $oldCount = @($oldVM.HardDrives).Count $newCount = @($newVM.HardDrives).Count if ($oldCount -ne $newCount) { $result += [pscustomobject]@{ ChangeType = 'Changed' VMName = $newVM.Name VMId = $newVM.VMId Category = 'VM' Property = 'HardDrives.Count' OldValue = $oldCount NewValue = $newCount } } # NetworkAdapters Countで比較 $oldCount = @($oldVM.NetworkAdapters).Count $newCount = @($newVM.NetworkAdapters).Count if ($oldCount -ne $newCount) { $result += [pscustomobject]@{ ChangeType = 'Changed' VMName = $newVM.Name VMId = $newVM.VMId Category = 'VM' Property = 'NetworkAdapters.Count' OldValue = $oldCount NewValue = $newCount } } # VMIntegrationService $oldServices = @($oldVM.VMIntegrationService) $newServices = @($newVM.VMIntegrationService) foreach ($newService in $newServices) { $oldService = $oldServices | Where-Object Name -eq $newService.Name if (-not $oldService) { $result += [pscustomobject]@{ ChangeType = 'Added' VMName = $newVM.Name VMId = $newVM.VMId Category = 'IntegrationService' Property = $newService.Name OldValue = $null NewValue = if ($newService.Enabled) { 'Enabled' } else { 'Disabled' } } continue } if ($oldService.Enabled -ne $newService.Enabled) { $result += [pscustomobject]@{ ChangeType = 'Changed' VMName = $newVM.Name VMId = $newVM.VMId Category = 'IntegrationService' Property = $newService.Name OldValue = if ($oldService.Enabled) { 'Enabled' } else { 'Disabled' } NewValue = if ($newService.Enabled) { 'Enabled' } else { 'Disabled' } } } } foreach ($oldService in $oldServices) { if ($oldService.Name -notin $newServices.Name) { $result += [pscustomobject]@{ ChangeType = 'Removed' VMName = $newVM.Name VMId = $newVM.VMId Category = 'IntegrationService' Property = $oldService.Name OldValue = if ($oldService.Enabled) { 'Enabled' } else { 'Disabled' } NewValue = $null } } } # ----------------------------- # Get-VM 以外の関連プロパティの比較 # ----------------------------- if ($Category -eq 'All') { # ----------------------------- # HA # ----------------------------- # VirtualMachines (Get-VM) の .IsClustered と同じなので比較しない # ----------------------------- # VCPU (Get-VMProcessor) # ----------------------------- $result += Compare-SimpleProperties ` -OldObject $oldVM.VCPU ` -NewObject $newVM.VCPU ` -Category 'VCPU' ` -VM $newVM ` -ExcludeProperty @( 'Dummy' ) # ----------------------------- # VRAM (Get-VMMemory) # ----------------------------- $result += Compare-SimpleProperties ` -OldObject $oldVM.VRAM ` -NewObject $newVM.VRAM ` -Category 'VRAM' ` -VM $newVM ` -ExcludeProperty @( 'MemoryEncryptionEnabled' ) # ----------------------------- # VMFirmware (Get-VMFirmware) # ----------------------------- $result += Compare-SimpleProperties ` -OldObject $oldVM.VMFirmware ` -NewObject $newVM.VMFirmware ` -Category 'VMFirmware' ` -VM $newVM ` -ExcludeProperty @( 'Dummy' ) # BootOrder $oldBootOrder = @($oldVM.VMFirmware.BootOrder) | ForEach-Object { $_.FirmwarePath } $newBootOrder = @($newVM.VMFirmware.BootOrder) | ForEach-Object { $_.FirmwarePath} if (($oldBootOrder -join ',') -ne ($newBootOrder -join ',')) { $oldBootOrder = @($oldVM.VMFirmware.BootOrder) | ForEach-Object { if ($null -eq $_.BootType) { 'Unknown' } else { $_.BootType.ToString() } } $newBootOrder = @($newVM.VMFirmware.BootOrder) | ForEach-Object { if ($null -eq $_.BootType) { 'Unknown' } else { $_.BootType.ToString() } } $result += [pscustomobject]@{ ChangeType = 'Changed' VMName = $newVM.Name VMId = $newVM.VMId Category = 'VMFirmware' Property = 'BootOrder' OldValue = $oldBootOrder -join ',' NewValue = $newBootOrder -join ',' } } # ----------------------------- # VMSecurity (Get-VMSecurity) # ----------------------------- $result += Compare-SimpleProperties ` -OldObject $oldVM.VMSecurity ` -NewObject $newVM.VMSecurity ` -Category 'VMSecurity' ` -VM $newVM ` -ExcludeProperty @( 'Dummy' ) # ----------------------------- # VNIC (Get-VMNetworkAdapter) # ----------------------------- $oldMac = @($oldVM.VNIC.MacAddress) | Sort-Object $newMac = @($newVM.VNIC.MacAddress) | Sort-Object if (($oldMac -join ',') -ne ($newMac -join ',')) { $result += [pscustomobject]@{ ChangeType = 'Changed' VMName = $newVM.Name VMId = $newVM.VMId Category = 'VNIC' Property = 'MACAddress' OldValue = $oldMac -join ',' NewValue = $newMac -join ',' } } # ----------------------------- # VHD (Get-VMHardDiskDrive) # ----------------------------- $oldvals = @($oldVM.VHD) $newvals = @($newVM.VHD) $oldvals = @($oldvals | Sort-Object | ForEach-Object { @( $_.ControllerType $_.ControllerNumber $_.ControllerLocation $_.DiskNumber $_.Path ) -join '_' }) $newvals = @($newvals | Sort-Object | ForEach-Object { @( $_.ControllerType $_.ControllerNumber $_.ControllerLocation $_.DiskNumber $_.Path ) -join '_' }) for ($i = 0; $i -lt [Math]::Max($oldvals.Count, $newvals.Count); $i++) { $diff = if ($i -ge $oldvals.Count) { "Added" } elseif ($i -ge $newvals.Count) { "Removed" } elseif ($oldvals[$i] -eq $newvals[$i]) { "NoChanged" } else { "Changed" } if($diff -ne "NoChanged") { $result += [pscustomobject]@{ ChangeType = $diff VMName = $newVM.Name VMId = $newVM.VMId Category = "VHD" Property = "Disk$i" OldValue = $oldvals[$i] NewValue = $newvals[$i] } } } # ----------------------------- # VMSnapshot (Get-VMSnapshot) # ----------------------------- $oldCount = @($oldVM.VMSnapshot).Count $newCount = @($newVM.VMSnapshot).Count if ($oldCount -ne $newCount) { $result += [pscustomobject]@{ ChangeType = 'Changed' VMName = $newVM.Name VMId = $newVM.VMId Category = 'VMSnapshot' Property = 'Count' OldValue = $oldCount NewValue = $newCount } } } # ----------------------------- # Get-VM 以外の関連プロパティの比較(ここまで) # ----------------------------- } # 削除されたVM foreach ($oldVM in $oldVMs) { if ($oldVM.VMId -notin $newVMs.VMId) { $result += [pscustomobject]@{ ChangeType = 'Removed' VMName = $oldVM.Name VMId = $oldVM.VMId Category = 'VM' Property = $null OldValue = $null NewValue = $null } } } $result