ASDK Bare Metal test Netsed edit
<################################################### | |
# # | |
# Copyright (c) Microsoft. All rights reserved. # | |
# # | |
##################################################> | |
# These tests are expected to run only as part of initial deployment of a stamp. | |
Import-LocalizedData CommonLocalizedStrings -BaseDirectory $PSScriptRoot\..\..\Common -FileName Roles.Strings.psd1 -ErrorAction SilentlyContinue | |
Import-LocalizedData BareMetalLocalizedStrings -BaseDirectory $PSScriptRoot\.. -FileName PhysicalMachines.Strings.psd1 -ErrorAction SilentlyContinue | |
Import-Module -Name "$PSScriptRoot\..\..\Common\RoleHelpers.psm1" | |
Import-Module -Name "$PSScriptRoot\..\..\..\ECEngine\Telemetry.dll" | |
# Disk information of the physical host. | |
# Some fields are used telemetry/logging only: | |
# FriendlyName/SerialNumber/DeviceId/Manufacturer/Model/MediaType/FirmwareVersion | |
class Disk : System.IComparable | |
{ | |
[string] $FriendlyName | |
[string] $SerialNumber | |
[string] $BusType | |
[string] $DeviceId | |
[string] $Manufacturer | |
[string] $Model | |
[string] $MediaType | |
[string] $HealthStatus | |
[long] $Size | |
[string] $FirmwareVersion | |
[string] $UniqueId | |
[int] $IndexWithinSimilarDisks | |
[int] CompareTo([object] $obj) | |
{ | |
[Disk] $other = $obj -as [Disk] | |
if (-not $other) | |
{ | |
throw new System.ArgumentException("The object to compare with is not of type Disk.") | |
} | |
[int] $busTypeComparisonResult = $this.BusType.CompareTo($other.BusType) | |
if ($busTypeComparisonResult -ne 0) | |
{ | |
return $busTypeComparisonResult | |
} | |
[int] $indexComparisonResult = $this.IndexWithinSimilarDisks.CompareTo($other.IndexWithinSimilarDisks) | |
if ($indexComparisonResult -ne 0) | |
{ | |
return $indexComparisonResult | |
} | |
[int] $sizeComparisonResult = $this.Size.CompareTo($other.Size) | |
return $sizeComparisonResult | |
} | |
[bool] Equals([object] $other) | |
{ | |
return $this.CompareTo($other) -eq 0 | |
} | |
[int] GetHashCode() | |
{ | |
return [Tuple]::Create($this.BusType, $this.Size, $this.IndexWithinSimilarDisks).GetHashCode() | |
} | |
} | |
class NetworkAdapter : System.IComparable | |
{ | |
[long] $Speed | |
[string] $IsRdmaEnabled | |
[string] $Name | |
[string] $InterfaceIndex | |
[string] $InterfaceDescription | |
[string] $DriverInformation | |
[string] $DriverName | |
[int] $MtuSize | |
[int] CompareTo([object] $obj) | |
{ | |
[NetworkAdapter] $other = $obj -as [NetworkAdapter] | |
if (-not $other) | |
{ | |
throw new System.ArgumentException("The object to compare with is not of type NetworkAdapter.") | |
} | |
[int] $speedComparisonResult = $this.Speed.CompareTo($other.Speed) | |
if ($speedComparisonResult -ne 0) | |
{ | |
return $speedComparisonResult | |
} | |
[int] $isRdmaEnabledComparisonResult = $this.IsRdmaEnabled.CompareTo($other.IsRdmaEnabled) | |
return $isRdmaEnabledComparisonResult | |
} | |
[bool] Equals([object] $other) | |
{ | |
return $this.CompareTo($other) -eq 0 | |
} | |
[int] GetHashCode() | |
{ | |
return [Tuple]::Create($this.Speed, $this.IsRdmaEnabled).GetHashCode() | |
} | |
} | |
class ScsiAdapter | |
{ | |
[string] $DeviceName | |
[string] $Description | |
[string] $DriverVersion | |
[string] $DriverDate | |
[string] $Location | |
[string] $HardWareID | |
} | |
class Processor | |
{ | |
[string] $NumberOfEnabledCores | |
[bool] $HasVMMonitorModeExtensions | |
[bool] $IsVirtualizationEnabled | |
[bool] $SupportsSecondLevelAddressTranslation | |
[string] $Name | |
[string] $Caption | |
[string] $Model | |
[string] $Socket | |
[string] $DriverVersion | |
[string] $DriverDate | |
[string] $TotalCores | |
[string] $TotalThreads | |
[string] $MicrocodeVersion | |
} | |
# BIOS information of the physical host. Used for telemetry only | |
class Bios | |
{ | |
[string] $Name | |
[string] $Manufacturer | |
[string] $SerialNumber | |
[string] $Version | |
[string] $SMBIOSBIOSVersion | |
[string] $SMBIOSMajorVersion | |
[string] $SMBIOSMinorVersion | |
[string] $UUID | |
} | |
class Memory | |
{ | |
[string] $Name | |
[string] $Manufacturer | |
[uint64] $Capacity | |
[string] $SerialNumber | |
[string] $MemoryType | |
[string] $PartNumber | |
[string] $DeviceLocator | |
[uint32] $ConfiguredClockSpeed | |
[string] $Model | |
[string] $PoweredOn | |
[string] $Status | |
} | |
class PcieDevices | |
{ | |
[string] $Name | |
[string] $Id | |
[string] $ManufacturerName | |
[string] $DriverVersion | |
[string] $PNPDeviceID | |
[string] $Status | |
} | |
class PhysicalMachine | |
{ | |
[string] $ComputerName | |
[string] $IPAddress | |
[bool] $IsVirtualMachine | |
[bool] $IsHyperVServiceRunning | |
[bool] $IsDataExecutionPreventionEnabled | |
[string] $OSVersion | |
[int] $OSSku | |
[bool] $IsPartOfDomain | |
[long] $TotalPhysicalMemory | |
[Processor[]] $Processors | |
[Disk] $SystemDisk | |
[Disk[]] $NonSystemDisks | |
[NetworkAdapter[]] $NetworkAdapters | |
[Disk[]] $PhysicalDisks | |
[Bios] $Bios | |
[ScsiAdapter[]] $ScsiAdapters | |
[Memory[]] $Memories | |
[PcieDevices[]] $Gpu | |
} | |
# Validation is done on three stages. | |
# First, data about the configuration of the machines is retrieved from all of the machines. | |
# Second, the hardware is emitted for telemetry purpose. (not related to the validation itself). | |
# After that, the data is checked to find potential problems. | |
Describe "Validate-Role" ` | |
{ | |
# Before we start, validate that the expected parameters have been passed. | |
It ($CommonLocalizedStrings.TestParameterShouldBeProvided -f "MachineNameToIPAddress") ` | |
{ | |
$Parameters.MachineNameToIPAddress | Should Not BeNullOrEmpty | |
} | |
It ($CommonLocalizedStrings.TestParameterShouldBeProvided -f "Credential") ` | |
{ | |
($Parameters.Credential -eq $null) | Should Be $false | |
} | |
It ($CommonLocalizedStrings.TestParameterShouldBeProvided -f "Topology") ` | |
{ | |
$Parameters.Topology | Should Not BeNullOrEmpty | |
} | |
It ($CommonLocalizedStrings.TestParameterShouldBeProvided -f "PhysicalMachinesRole") ` | |
{ | |
$Parameters.PhysicalMachinesRole | Should Not BeNullOrEmpty | |
} | |
It ($CommonLocalizedStrings.TestParameterShouldBeProvided -f "DeploymentGuid") ` | |
{ | |
$Parameters.DeploymentGuid | Should Not BeNullOrEmpty | |
} | |
It ($CommonLocalizedStrings.TestParameterShouldBeProvided -f "ClusterRole") ` | |
{ | |
$Parameters.OEMPublisher | Should Not BeNullOrEmpty | |
} | |
It ($CommonLocalizedStrings.TestParameterShouldBeProvided -f "OEMPublisher") ` | |
{ | |
$Parameters.OEMPublisher | Should Not BeNullOrEmpty | |
} | |
$isOneNode = ($Parameters.Topology -eq 'OneNode') | |
$isVirtualizedDeployment = ($Parameters.OEMModel -eq 'Hyper-V') | |
$physicalMachinesRole = $Parameters.PhysicalMachinesRole | |
$clusterRole = $Parameters.ClusterRole | |
# Validate that the expected settings that are needed for validation have been specified in CustomerConfig.xml. | |
It ($BareMetalLocalizedStrings.TestCustomerConfigParameters) ` | |
{ | |
$script:minimumOperatingSystemVersion = New-Object Version($physicalMachinesRole.PrivateInfo.ValidationRequirements.MinimumOperatingSystemVersion) | |
$script:minimumNumberOfCoresPerMachine = [int]::Parse($physicalMachinesRole.PrivateInfo.ValidationRequirements.MinimumNumberOfCoresPerMachine) | |
$script:minimumPhysicalMemoryPerMachineGB = [long]::Parse($physicalMachinesRole.PrivateInfo.ValidationRequirements.MinimumPhysicalMemoryPerMachineGB) | |
$script:minimumNumberOfAdaptersPerMachine = [int]::Parse($physicalMachinesRole.PrivateInfo.ValidationRequirements.MinimumNumberOfAdaptersPerMachine) | |
$script:minimumAdapterSpeedBitsPerSecond = [long]::Parse($physicalMachinesRole.PrivateInfo.ValidationRequirements.MinimumAdapterSpeedBitsPerSecond) | |
$script:minimumSizeOfSystemDiskGB = [long]::Parse($physicalMachinesRole.PrivateInfo.ValidationRequirements.MinimumSizeOfSystemDiskGB) | |
$script:minimumSizeOfDataDisksGB = [long]::Parse($physicalMachinesRole.PrivateInfo.ValidationRequirements.MinimumSizeOfDataDisksGB) | |
$script:minimumNumberOfDataDisksPerMachine = [int]::Parse($physicalMachinesRole.PrivateInfo.ValidationRequirements.MinimumNumberOfDataDisksPerMachine) | |
$script:maximumNumberOfMissingDisksPerMachine = [int]::Parse($physicalMachinesRole.PrivateInfo.ValidationRequirements.MaximumNumberOfMissingDisksPerMachine) | |
$script:sizeComparisonTolerancePercentage = [int]::Parse($physicalMachinesRole.PrivateInfo.ValidationRequirements.SizeComparisonTolerancePercentage) | |
$script:toleranceMultiplier = 1 + ($sizeComparisonTolerancePercentage / 100.0) | |
} | |
# Validate the number of machines and that, in the one-node case, the test is running on the physical machine. | |
if ($isOneNode) | |
{ | |
It ($BareMetalLocalizedStrings.TestExactlyOneMachine) ` | |
{ | |
$Parameters.MachineNameToIPAddress.Count | Should Be 1 | |
} | |
foreach ($physicalMachineName in $Parameters.MachineNameToIPAddress.Keys) | |
{ | |
It ($BareMetalLocalizedStrings.TestRunningOnPhysicalMachine -f $physicalMachineName) ` | |
{ | |
$physicalMachineName | Should Be $env:COMPUTERNAME | |
} | |
} | |
# For OneNode, there is no need to use the IPv4 address because there will not be any IPv6 address problems. Use | |
# "localhost" for the host address instead of the IPv4 address so that CredSSP is not needed. | |
$Parameters.MachineNameToIPAddress[$env:COMPUTERNAME] = "localhost" | |
} | |
################################################################################################################################################### | |
# Stage 1: Query for machine information | |
# Querying for machine information will be kept separate from the checks that use this information, because in the future this machine information | |
# may be retrieved as part of a discovery stage. | |
################################################################################################################################################### | |
$physicalMachines = [PhysicalMachine[]]@() | |
[System.Collections.Hashtable[]] $physicalMachinesInventoryInfo = @() | |
foreach ($machineName in $Parameters.MachineNameToIPAddress.Keys) | |
{ | |
$ipAddress = $Parameters.MachineNameToIPAddress[$machineName]; | |
$physicalMachine = New-Object PhysicalMachine -Property @{ ComputerName=$machineName; IPAddress=$ipAddress } | |
Trace-Execution "Querying machine information for machine $machineName with address $ipAddress" | |
It ($CommonLocalizedStrings.TestConnection -f $machineName) ` | |
{ | |
if($machineName -eq $env:COMPUTERNAME) | |
{ | |
$script:cimSession = New-CimSession -Credential $Parameters.Credential | |
} | |
else | |
{ | |
$script:cimSession = New-CimSession -ComputerName $ipAddress -Credential $Parameters.Credential | |
} | |
$currentMachineIdString = Invoke-CimMethod -CimSession $cimSession -NameSpace "root/cimv2" -Class "StdRegProv" -MethodName "GetStringValue" -Arguments @{ | |
'hDefKey' = [uint32]'0x80000002' | |
'sSubKeyName' = "Software\Microsoft\SQMClient" | |
'sValueName' = "MachineId" | |
} | |
[System.Guid] $tmpGuid = [System.Guid]::Empty | |
if ([System.Guid]::TryParse($currentMachineIdString.sValue, [ref]$tmpGuid)) | |
{ | |
$script:machineId = $tmpGuid.ToString() | |
} | |
else | |
{ | |
$script:machineId = "" | |
} | |
} | |
# | |
# Start: Computer and OS information | |
# | |
It ($BareMetalLocalizedStrings.TestRetrieveComputerSystemInformation -f $machineName) ` | |
{ | |
Trace-Execution "Querying Win32_ComputerSystem." | |
$computerSystemWmiResults = Get-CimInstance -CimSession $cimSession -Query "select TotalPhysicalMemory, PartOfDomain, Manufacturer from Win32_ComputerSystem" | |
$physicalMachine.TotalPhysicalMemory = $computerSystemWmiResults.TotalPhysicalMemory | |
$physicalMachine.IsPartOfDomain = $computerSystemWmiResults.PartOfDomain | |
$physicalMachine.IsVirtualMachine = $computerSystemWmiResults.Manufacturer.ToLower() -match 'microsoft corporation' -or ` | |
$computerSystemWmiResults.Manufacturer.ToLower() -match 'vmware' | |
Trace-Execution "Manufacturer reported by Win32_ComputerSystem is $($computerSystemWmiResults.Manufacturer)" | |
Trace-Execution "Total physical memory is $($computerSystemWmiResults.TotalPhysicalMemory)" | |
Trace-Execution "PartOfDomain is $($computerSystemWmiResults.PartOfDomain)" | |
} | |
It ($BareMetalLocalizedStrings.TestRetrieveOperatingSystemInformation -f $machineName) ` | |
{ | |
Trace-Execution "Querying Win32_OperatingSystem." | |
$operatingSystemWmiResults = Get-CimInstance -CimSession $cimSession -Query "select Version, OperatingSystemSKU, DataExecutionPrevention_Available from Win32_OperatingSystem" | |
$physicalMachine.OSVersion = $operatingSystemWmiResults.Version | |
$physicalMachine.OSSku = $operatingSystemWmiResults.OperatingSystemSKU | |
$physicalMachine.IsDataExecutionPreventionEnabled = $operatingSystemWmiResults.DataExecutionPrevention_Available | |
Trace-Execution "OS version is $($operatingSystemWmiResults.Version)" | |
Trace-Execution "OS SKU is $($operatingSystemWmiResults.OperatingSystemSKU)" | |
Trace-Execution "DataExecutionPreventionAvailable is $($operatingSystemWmiResults.DataExecutionPrevention_Available)" | |
} | |
It ($BareMetalLocalizedStrings.TestRetrieveHyperVServiceInformation -f $machineName) ` | |
{ | |
Trace-Execution "Querying Win32_Service." | |
$hyperVServiceWmiResults = Get-CimInstance -CimSession $cimSession -Query "select State from Win32_Service where name='vmms'" | |
$physicalMachine.IsHyperVServiceRunning = $false | |
if ($hyperVServiceWmiResults) | |
{ | |
Trace-Execution "Hyper-V service was found." | |
Trace-Execution "State of Hyper-V service is $($hyperVServiceWmiResults.State)" | |
$physicalMachine.IsHyperVServiceRunning = ($hyperVServiceWmiResults.State -eq 'Running') | |
} | |
} | |
# | |
# End: Computer and OS information | |
# | |
# | |
# Start: Processor information | |
# | |
It ($BareMetalLocalizedStrings.TestRetrieveProcessorInformation -f $machineName) ` | |
{ | |
function GetCpuMicrocodeVersionString | |
{ | |
[CmdletBinding()] | |
Param ( | |
[Parameter(Mandatory = $true)] | |
[Byte[]] $littleEndianBytes | |
) | |
$retStr = "" | |
if ($null -eq $littleEndianBytes -or $littleEndianBytes.Length -eq 0 -or $littleEndianBytes.Length % 4 -ne 0) | |
{ | |
return $retStr | |
} | |
# We are expecting an array that represents one INT32 integer. | |
# For some OEM, the array might contains multiple INT32 numbers. In that case the leading number should | |
# be 0: the 1st 4 bytes represents 1st integer, the 2nd 4 bytes represents 2nd integer, etc. | |
# So cannot just revert the byte array to get the correct HEX string: should do some workaround here. | |
[String] $littleEndianString = [System.BitConverter]::ToString($littleEndianBytes).Replace("-", "") | |
[int] $littleEndianNumber = 0 | |
try | |
{ | |
$littleEndianNumber = [System.Convert]::ToInt32($littleEndianString, 16) | |
} | |
catch [System.OverflowException] | |
{ | |
# In case the input array contains multiple INT32 numbers, but the leading numbers are not 0 | |
return $retStr | |
} | |
[Byte[]] $bytes = [System.BitConverter]::GetBytes($littleEndianNumber) | |
foreach ($b in $bytes) | |
{ | |
$retStr += $b.ToString("X2"); | |
} | |
return $retStr | |
} | |
Trace-Execution "Querying processor information." | |
$currentCpuMicrocodeBinaryArray = Invoke-CimMethod -CimSession $cimSession -NameSpace "root/cimv2" -Class "StdRegProv" -MethodName "GetBinaryValue" -Arguments @{ | |
'hDefKey' = [uint32]'0x80000002' | |
'sSubKeyName' = "HARDWARE\DESCRIPTION\System\CentralProcessor\0" | |
'sValueName' = "Update Revision" | |
} | |
$currentCpuMicrocodeVersion = GetCpuMicrocodeVersionString -littleEndianBytes $currentCpuMicrocodeBinaryArray.uValue | |
$processorWmiResults = Get-CimInstance -CimSession $cimSession -Class Win32_Processor | |
$physicalMachine.Processors = [Processor[]]@() | |
$processorWmiResults | % ` | |
{ | |
$physicalMachine.Processors += (New-Object Processor -Property @{ NumberOfEnabledCores=$_.NumberOfEnabledCore; | |
HasVMMonitorModeExtensions=$_.VMMonitorModeExtensions; | |
IsVirtualizationEnabled=$_.VirtualizationFirmwareEnabled; | |
SupportsSecondLevelAddressTranslation=$_.SecondLevelAddressTranslationExtensions; | |
Name = $_.DeviceId; | |
Caption = $_.Caption; | |
Model = $_.Name; | |
Socket = $_.SocketDesignation; | |
TotalCores = $_.NumberOfCores; | |
TotalThreads = $_.NumberOfLogicalProcessors; | |
MicrocodeVersion = $currentCpuMicrocodeVersion | |
}) | |
} | |
$pnpSignedDrivers = Get-CimInstance -CimSession $cimSession -ClassName "Win32_PnPSignedDriver" ` | |
| Where-Object FriendlyName -ne $null ` | |
| Select-Object FriendlyName, DriverVersion, DriverDate | |
foreach ($processor in $physicalMachine.Processors) | |
{ | |
foreach ($pnpSignedDriver in $pnpSignedDrivers) | |
{ | |
if ($pnpSignedDriver.FriendlyName -eq $processor.Model) | |
{ | |
$processor.DriverVersion = $pnpSignedDriver.DriverVersion | |
$processor.DriverDate = $pnpSignedDriver.DriverDate | |
} | |
} | |
} | |
Trace-Execution "Found processors with the following properties:" | |
Trace-Execution ($physicalMachine.Processors | out-string) | |
} | |
# | |
# End: Processor information | |
# | |
# | |
# Start: Disk information | |
# | |
It ($BareMetalLocalizedStrings.TestRetrieveDiskInformation -f $machineName) ` | |
{ | |
Trace-Execution "Querying disk information." | |
$systemDisk = Get-Disk -CimSession $cimSession | ? { $_.IsSystem } | |
$physicalMachine.SystemDisk = New-Object Disk -Property @{ BusType=$systemDisk.BusType; Size=$systemDisk.Size } | |
$physicalMachine.NonSystemDisks = [Disk[]]@() | |
Get-PhysicalDisk -CimSession $cimSession | ? DeviceId -ne $systemDisk.Number | % ` | |
{ | |
$physicalMachine.NonSystemDisks += New-Object Disk -Property @{ | |
BusType=$_.BusType | |
Size=$_.Size | |
FriendlyName = $_.FriendlyName | |
SerialNumber = $_.SerialNumber | |
Manufacturer = $_.Manufacturer | |
DeviceId = $_.DeviceId | |
Model = $_.Model | |
MediaType = $_.MediaType | |
HealthStatus = $_.HealthStatus | |
FirmwareVersion = $_.FirmwareVersion | |
} | |
} | |
$physicalMachine.NonSystemDisks = ($physicalMachine.NonSystemDisks | sort) | |
$physicalMachine.NonSystemDisks[0].IndexWithinSimilarDisks = 0 | |
for ($i = 1; $i -lt $physicalMachine.NonSystemDisks.Count; $i++) | |
{ | |
if ($physicalMachine.NonSystemDisks[$i].BusType -eq $physicalMachine.NonSystemDisks[$i-1].BusType -and | |
$physicalMachine.NonSystemDisks[$i].Size * $toleranceMultiplier -ge $physicalMachine.NonSystemDisks[$i-1].Size -and | |
$physicalMachine.NonSystemDisks[$i].Size -le $physicalMachine.NonSystemDisks[$i-1].Size * $toleranceMultiplier) | |
{ | |
$physicalMachine.NonSystemDisks[$i].IndexWithinSimilarDisks = $physicalMachine.NonSystemDisks[$i-1].IndexWithinSimilarDisks + 1 | |
} | |
else | |
{ | |
$physicalMachine.NonSystemDisks[$i].IndexWithinSimilarDisks = 0 | |
} | |
} | |
# Get information check for physical disk that attached to current machine only | |
$hostLocalStorageSubSystems = Get-StorageSubSystem -CimSession $cimSession | Where-Object { $_.FriendlyName -like "Cluster*" } | |
if ($hostLocalStorageSubSystems.Count -eq 0) | |
{ | |
# No SOFS enabled, try to get Windows Storage itself | |
$hostLocalStorageSubSystems = Get-StorageSubSystem -CimSession $cimSession | Where-Object { $_.FriendlyName -like "Windows Storage*" } | |
} | |
$hostLocalStorageNodes = Get-StorageNode -StorageSubSystem $hostLocalStorageSubSystems[0] -CimSession $cimSession | |
$currentHostStorageNode = $hostLocalStorageNodes | Where-Object { $_.Name -like "$($machineName)*" } | |
Get-PhysicalDisk -PhysicallyConnected -StorageNode $currentHostStorageNode -CimSession $cimSession | Where-Object Model -ne "Virtual Disk" | ForEach-Object ` | |
{ | |
$physicalMachine.PhysicalDisks += New-Object Disk -Property @{ | |
BusType=$_.BusType | |
Size=$_.Size | |
FriendlyName = $_.FriendlyName | |
SerialNumber = $_.SerialNumber | |
Manufacturer = $_.Manufacturer | |
DeviceId = $_.DeviceId | |
Model = $_.Model | |
MediaType = $_.MediaType | |
HealthStatus = $_.HealthStatus | |
FirmwareVersion = $_.FirmwareVersion | |
UniqueId = $_.UniqueId.Trim() | |
} | |
} | |
Trace-Execution "Found system disk with the following properties:" | |
Trace-Execution ($physicalMachine.SystemDisk | out-string) | |
Trace-Execution "Found non-system disks with the following properties:" | |
Trace-Execution ($physicalMachine.NonSystemDisks | out-string) | |
} | |
# | |
# End: Disk information | |
# | |
# | |
# Start: BIOS information | |
# | |
It ($BareMetalLocalizedStrings.TestRetrieveBIOSInformation -f $machineName) ` | |
{ | |
Trace-Execution "Querying BIOS information." | |
# BIOS properties need to be collected | |
Get-CimInstance -CimSession $cimSession -ClassName "Win32_BIOS" | Select-Object -First 1 | ForEach-Object ` | |
{ | |
$physicalMachine.Bios = New-Object Bios -Property @{ | |
Name = $_.Name | |
Manufacturer = $_.Manufacturer | |
SerialNumber = $_.SerialNumber | |
Version = $_.Version | |
SMBIOSBIOSVersion = $_.SMBIOSBIOSVersion | |
SMBIOSMajorVersion = $_.SMBIOSMajorVersion | |
SMBIOSMinorVersion = $_.SMBIOSMinorVersion | |
} | |
} | |
Get-CimInstance -CimSession $cimSession -ClassName "Win32_ComputerSystemProduct" | Select-Object -First 1 | ForEach-Object ` | |
{ | |
$physicalMachine.Bios.UUID = $_.UUID | |
} | |
} | |
# | |
# End: BIOS information | |
# | |
# | |
# Start: Network adapter information | |
# | |
# | |
# Start: Memory information | |
# | |
{ | |
# Get memory type string from CIM memory type number | |
# Refer to https://docs.microsoft.com/en-us/windows/desktop/cimwin32prov/win32-physicalmemory | |
function Get-MemoryTypeString | |
{ | |
[CmdletBinding()] | |
Param ( | |
[Parameter(Mandatory = $true)] | |
[string] $MemoryType | |
) | |
switch ($MemoryType) | |
{ | |
"0" | |
{ | |
$retVal = "Unknown" | |
break | |
} | |
"1" | |
{ | |
$retVal = "Other" | |
break | |
} | |
"2" | |
{ | |
$retVal = "DRAM" | |
break | |
} | |
"3" | |
{ | |
$retVal = "Synchronous DRAM" | |
break | |
} | |
"4" | |
{ | |
$retVal = "Cache DRAM" | |
break | |
} | |
"5" | |
{ | |
$retVal = "EDO" | |
break | |
} | |
"6" | |
{ | |
$retVal = "EDRAM" | |
break | |
} | |
"7" | |
{ | |
$retVal = "VRAM" | |
break | |
} | |
"8" | |
{ | |
$retVal = "SRAM" | |
break | |
} | |
"9" | |
{ | |
$retVal = "RAM" | |
break | |
} | |
"10" | |
{ | |
$retVal = "ROM" | |
break | |
} | |
"11" | |
{ | |
$retVal = "Flash" | |
break | |
} | |
"12" | |
{ | |
$retVal = "EEPROM" | |
break | |
} | |
"13" | |
{ | |
$retVal = "FEPROM" | |
break | |
} | |
"14" | |
{ | |
$retVal = "EPROM" | |
break | |
} | |
"15" | |
{ | |
$retVal = "CDRAM" | |
break | |
} | |
"16" | |
{ | |
$retVal = "3DRAM" | |
break | |
} | |
"17" | |
{ | |
$retVal = "SDRAM" | |
break | |
} | |
"18" | |
{ | |
$retVal = "SGRAM" | |
break | |
} | |
"19" | |
{ | |
$retVal = "RDRAM" | |
break | |
} | |
"20" | |
{ | |
$retVal = "DDR" | |
break | |
} | |
"21" | |
{ | |
$retVal = "DDR2" | |
break | |
} | |
"22" | |
{ | |
$retVal = "DDR2 FB-DIMM" | |
break | |
} | |
"24" | |
{ | |
$retVal = "DDR3" | |
break | |
} | |
"25" | |
{ | |
$retVal = "FBD2" | |
break | |
} | |
default | |
{ | |
$retVal = "Unknown" | |
break | |
} | |
} | |
return $retVal | |
} | |
Trace-Execution "Querying Memory information." | |
$memoryWmiResults = Get-CimInstance -CimSession $cimSession -Class Win32_PhysicalMemory | |
$physicalMachine.Memories = [Memory[]]@() | |
$memoryWmiResults | ForEach-Object ` | |
{ | |
$currentMemoryTypeString = Get-MemoryTypeString -MemoryType $_.MemoryType | |
$physicalMachine.Memories += (New-Object Memory -Property @{ Name = $_.Name; | |
Manufacturer = $_.Manufacturer; | |
Capacity = $_.Capacity; | |
SerialNumber = $_.SerialNumber; | |
MemoryType = $currentMemoryTypeString; | |
DeviceLocator = $_.DeviceLocator; | |
ConfiguredClockSpeed = $_.ConfiguredClockSpeed; | |
Model = $_.Model; | |
PoweredOn = $_.PoweredOn; | |
Status = $_.Status }) | |
} | |
Trace-Execution "Found memories with the following properties:" | |
Trace-Execution ($physicalMachine.Memories | out-string) | |
} | |
# | |
# Start: SCSI Adapter information | |
# | |
It ($BareMetalLocalizedStrings.TestRetrieveScsiAdapterInformation -f $machineName) ` | |
{ | |
Trace-Execution "Querying SCSI Adapter information." | |
# SCSI properties need to be collected | |
$scsiAdapterWmiResults = Get-CimInstance -CimSession $cimSession -ClassName "Win32_PnPSignedDriver" ` | |
| Where-Object DeviceClass -eq "SCSIADAPTER" ` | |
| Where-Object Location -ne $null | |
$physicalMachine.ScsiAdapters = [ScsiAdapter[]]@() | |
$scsiAdapterWmiResults | ForEach-Object ` | |
{ | |
$physicalMachine.ScsiAdapters += (New-Object ScsiAdapter -Property @{ | |
DeviceName = $_.DeviceName | |
Description = $_.Description | |
DriverVersion = $_.DriverVersion | |
DriverDate = $_.DriverDate | |
HardWareID = $_.HardWareID | |
}) | |
} | |
Trace-Execution "Found SCSI adapters with the following properties:" | |
Trace-Execution ($physicalMachine.ScsiAdapters | out-string) | |
Trace-Execution "Completed querying machine information for machine $machineName with address $physicalMachine" | |
} | |
# | |
# Start: GPU Adapter information | |
# | |
It ($BareMetalLocalizedStrings.TestRetrieveGPUInformation -f $machineName) ` | |
{ | |
Trace-Execution "Querying GPU information." | |
# GPU properties need to be collected | |
$gpuWmiResults = Get-CimInstance -CimSession $cimSession -ClassName "Win32_VideoController" ` | |
| Where-Object Name -NotLike "*Microsoft*" | |
$physicalMachine.Gpu = [PcieDevices[]]@() | |
$gpuWmiResults | ForEach-Object ` | |
{ | |
$physicalMachine.Gpu += (New-Object PcieDevices -Property @{ | |
Id = $_.DeviceId | |
Name = $_.Name | |
DriverVersion = $_.DriverVersion | |
PNPDeviceID = $_.PNPDeviceID | |
Status = $_.Status | |
ManufacturerName = $_.AdapterCompatibility | |
}) | |
} | |
Trace-Execution "Found GPU adapters with the following properties:" | |
Trace-Execution ($gpuData | out-string) | |
Remove-CimSession -CimSession $cimSession | |
Trace-Execution "Completed querying machine information for machine $machineName with address $physicalMachine" | |
} | |
$physicalMachines += $physicalMachine | |
# BIOS JSON be saved into hardware inventory information (ECE store and stamp info file) | |
$biosJson = [PSCustomObject]@{ | |
'@odata.context' = '/redfish/v1/$metadata#ComputerSystem.ComputerSystem' | |
'@odata.id' = "/redfish/v1/Systems/$machineName" | |
'@odata.type' = "#ComputerSystem.v1_5_0.ComputerSystem" | |
Id = $machineName | |
Name = $machineName | |
SerialNumber = $physicalMachine.Bios.SerialNumber | |
BiosVersion = $physicalMachine.Bios.SMBIOSBIOSVersion | |
UUID = $physicalMachine.Bios.UUID | |
Oem = [PSCustomObject]@{ | |
$Parameters.oemPublisher = [PSCustomObject]@{ | |
Name = $physicalMachine.Bios.Name | |
BIOS_Manufacturer = $physicalMachine.Bios.Manufacturer | |
BIOS_Version = $physicalMachine.Bios.Version | |
BIOS_SMBIOSMajorVersion = $physicalMachine.Bios.SMBIOSMajorVersion | |
BIOS_SMBIOSMinorVersion = $physicalMachine.Bios.SMBIOSMinorVersion | |
MachineId = $machineId | |
} | |
} | |
} | |
$processorsJson = [PSCustomObject[]]@() | |
foreach ($processor in $physicalMachine.Processors) | |
{ | |
$processorsJson += [PSCustomObject]@{ | |
'@odata.context' = '/redfish/v1/$metadata#Processor.Processor' | |
'@odata.id' = "/redfish/v1/Systems/$machineName/Processors/$($processor.Name)" | |
'@odata.type' = "#Processor.v1_3_0.Processor" | |
Id = $processor.Name | |
Name = $processor.Name | |
Model = $processor.Model | |
Socket = $processor.Socket | |
TotalCores = $processor.TotalCores | |
TotalThreads = $processor.TotalThreads | |
Oem = [PSCustomObject]@{ | |
$Parameters.oemPublisher = [PSCustomObject]@{ | |
Caption = $processor.Caption | |
NumberOfEnabledCores = $processor.NumberOfEnabledCores | |
MicrocodeVersion = $processor.MicrocodeVersion | |
DriverDate = $processor.DriverDate | |
DriverVersion = $processor.DriverVersion | |
} | |
} | |
} | |
} | |
$netAdaptersJson = [PSCustomObject[]] @() | |
foreach ($netAdapter in $physicalMachine.NetworkAdapters) | |
{ | |
$netAdaptersJson += [PSCustomObject]@{ | |
'@odata.context' = '/redfish/v1/$metadata#EthernetInterface.EthernetInterface' | |
'@odata.id' = "/redfish/v1/Systems/$machineName/EthernetInterfaces/$($netAdapter.InterfaceIndex)" | |
'@odata.type' = "#EthernetInterface.v1_4_0.EthernetInterface" | |
Id = $netAdapter.InterfaceIndex | |
Name = $netAdapter.InterfaceDescription | |
SpeedMbps = $netAdapter.Speed / 1000000 | |
MtuSize = $netAdapter.MtuSize | |
Oem = [PSCustomObject]@{ | |
$Parameters.oemPublisher = [PSCustomObject]@{ | |
DriverName = $netAdapter.DriverName | |
IsRdmaEnabled = $netAdapter.IsRdmaEnabled | |
DriverInformation = $netAdapter.DriverInformation | |
} | |
} | |
} | |
} | |
$diskDrivesJson = [PSCustomObject[]] @() | |
foreach ($diskDrive in $physicalMachine.PhysicalDisks) | |
{ | |
$diskStatus = "Warning" | |
switch ($diskDrive.HealthStatus) | |
{ | |
"Healthy" { $diskStatus = "OK" } | |
"Warning" { $diskStatus = "Warning" } | |
"Unhealthy" { $diskStatus = "Critical" } | |
Default { $diskStatus = "Warning" } | |
} | |
$diskDrivesJson += [PSCustomObject]@{ | |
'@odata.context' = '/redfish/v1/$metadata#Drive.Drive' | |
'@odata.id' = "/redfish/v1/Systems/$machineName/Storage/1/Drives/$($diskDrive.SerialNumber)" | |
'@odata.type' = "#Drive.v1_4_0.Drive" | |
Id = $diskDrive.SerialNumber | |
Name = $diskDrive.FriendlyName | |
SerialNumber = $diskDrive.SerialNumber | |
Protocol = $diskDrive.BusType | |
Manufacturer = $diskDrive.Manufacturer | |
Model = $diskDrive.Model | |
MediaType = $diskDrive.MediaType | |
Status = [PSCustomObject] @{ | |
Health = $diskStatus | |
} | |
CapacityBytes = $diskDrive.Size | |
Revision = $diskDrive.FirmwareVersion | |
Oem = [PSCustomObject]@{ | |
$Parameters.oemPublisher = [PSCustomObject]@{ | |
DeviceId = $diskDrive.DeviceId | |
UniqueId = $diskDrive.UniqueId | |
} | |
} | |
} | |
} | |
$memoriesJson = [PSCustomObject[]] @() | |
foreach ($memory in $physicalMachine.Memories) | |
{ | |
$currentMemoryCapacityMiB = $memory.Capacity/(1024*1024) | |
$memoriesJson += [PSCustomObject] @{ | |
'@odata.context' = '/redfish/v1/$metadata#Memory.Memory' | |
'@odata.id' = "/redfish/v1/Systems/$machineName/Memory/$($memory.DeviceLocator)" | |
'@odata.type' = "#Memory.v1_5_0.Memory" | |
Id = $memory.DeviceLocator | |
Name = $memory.Name | |
CapacityMiB = $currentMemoryCapacityMiB | |
DeviceLocator = $memory.DeviceLocator | |
Manufacturer = $memory.Manufacturer | |
MemoryType = $memory.MemoryType | |
OperatingSpeedMhz = $memory.ConfiguredClockSpeed | |
PartNumber = $memory.PartNumber | |
SerialNumber = $memory.SerialNumber | |
Oem = [PSCustomObject]@{ | |
$Parameters.oemPublisher = [PSCustomObject]@{ | |
Model = $memory.Model | |
PoweredOn = $memory.PoweredOn | |
Status = $memory.Status | |
} | |
} | |
} | |
} | |
$gpuDataJson = [PSCustomObject[]] @() | |
foreach ($gpudata in $physicalMachine.GPU) | |
{ | |
$gpuDataJson += [PSCustomObject] @{ | |
'@odata.context' = '/redfish/v1/$metadata#PCIeDevice.PCIeDevice' | |
'@odata.id' = "/redfish/v1/Systems/$machineName/PCIeDevice/$($gpudata.Id)" | |
'@odata.type' = "#PCIeDevice.v1_3_1.PCIeDevice" | |
Id = $gpudata.Id | |
Name = $gpudata.Name | |
Oem = [PSCustomObject]@{ | |
$gpudata.ManufacturerName = [PSCustomObject]@{ | |
DriverVersion = $gpudata.DriverVersion | |
PNPDeviceID = $gpudata.PNPDeviceID | |
Status = $gpudata.Status | |
} | |
} | |
} | |
} | |
$physicalMachinesInventoryInfo += @{ | |
"$machineName" = @{ | |
"ComputerSystem" = $biosJson | |
"Memory" = $memoriesJson | |
"Processor" = $processorsJson | |
"Drive" = $diskDrivesJson | |
"EthernetInterface" = $netAdaptersJson | |
"ScsiAdapters" = $physicalMachine.ScsiAdapters | |
"PcieDevices" = $gpuDataJson | |
} | |
} | |
} | |
# Got inventory info for all hosts in $physicalMachinesInventoryInfo. Now output to JSON | |
$hostInventoryLog = $physicalMachinesRole.PublicInfo.HardwareInventoryInformaton.Path | |
$hostInventoryLog = $ExecutionContext.InvokeCommand.ExpandString($hostInventoryLog) | |
$hostInventoryLogFolder = Split-Path -Path $hostInventoryLog -Parent | |
if (-not (Test-Path -Path $hostInventoryLogFolder -PathType Container)) | |
{ | |
New-Item -Path $hostInventoryLogFolder -ItemType Container | |
} | |
if (Test-Path -Path $hostInventoryLog -PathType Leaf) | |
{ | |
Trace-Execution "Remove existing hardware inventory log file [$hostInventoryLog]." | |
Remove-Item -Path $hostInventoryLog -Force -ErrorAction Ignore | Out-null | |
} | |
# Now getting the information formatted as expected. For example | |
# "TimeOfCollection": | |
# "HardwareInventoryInfo": { | |
# "s-cluster" : { | |
# "NODE1": { | |
# ... | |
# }, | |
# "NODE2": { | |
# ... | |
# }, | |
# ... | |
# "NODEx": { | |
# ... | |
# } | |
# } | |
# } | |
$clusters = $ClusterRole.Clusters.Node | |
$finalInventoryJsonData = @{} | |
foreach ($cluster in $clusters) | |
{ | |
# Get machines' name in current cluster | |
$currentClusterMachines = ($physicalMachinesRole.Nodes.Node | Where-Object {$_.RefClusterId -eq $cluster.Id}).Name | |
# Prepare information for current cluster | |
$currentClusterInfo = @{} | |
foreach ($machine in $currentClusterMachines) | |
{ | |
if ($physicalMachinesInventoryInfo.$machine) | |
{ | |
$currentClusterInfo += @{ $machine = $physicalMachinesInventoryInfo.$machine } | |
} | |
} | |
$finalInventoryJsonData += @{ $cluster.Name = $currentClusterInfo} | |
} | |
Trace-Execution "Saving new host hardware inventory information at [$hostInventoryLog]..." | |
$hardwareInventoryInfoJson = @{ | |
"TimeOfCollection" = (Get-Date).ToString("yyyy-MM-ddTHH:mm:ss") | |
"HardwareInventoryInfo" = $finalInventoryJsonData | |
} | |
ConvertTo-Json $hardwareInventoryInfoJson -Depth 99 | Add-Content -Path $hostInventoryLog | |
################################################################################################################################################### | |
# Stage 2: Emit the hardware information for telemetry purpose | |
################################################################################################################################################### | |
$physicalMachines | ForEach-Object { | |
Send-HardwareTelemetry -DeploymentGuid $Parameters.DeploymentGuid -ValidationRequirements ($physicalMachinesRole.PrivateInfo.ValidationRequirements.InnerXml) -HardwareInformation ($_ | ConvertTo-Json -Depth 10 -Compress) | |
} | |
Trace-Execution "Hardware information for Deployment $($Parameters.DeploymentGuid) has been emitted." | |
################################################################################################################################################### | |
# Stage 3: Run validations based on the machine information that has been retrieved. | |
################################################################################################################################################### | |
# Validate this is not a virtual machine. | |
if ($isVirtualizedDeployment) | |
{ | |
foreach ($physicalMachine in $physicalMachines) | |
{ | |
It ($BareMetalLocalizedStrings.TestNotVirtualMachine -f @($physicalMachine.ComputerName)) ` | |
{ | |
$physicalMachine.IsVirtualMachine | Should Be $true | |
} | |
} | |
} | |
# Validate Hyper-V support. Validation is based partially on code in http://wincode/View.aspx?File=%2f%2fdepot%2fth2%2fvm%2fsetup%2fServerManager%2fHyper-V%2fPlugin%2fHardwareDetectionUtilities.cs%232&SDPort=vmdepot.sys-ntgroup.ntdev.microsoft.com:2022&Query=VirtualizationFirmwareEnabled&Catalog=th2(dev) | |
foreach ($physicalMachine in $physicalMachines) | |
{ | |
if ($physicalMachine.IsHyperVServiceRunning) | |
{ | |
Trace-Execution "The Hyper-V service is running on physical machine $($physicalMachine.ComputerName). There is no need to check for virtualization support on this machine." | |
continue | |
} | |
It ($BareMetalLocalizedStrings.TestVMMonitorModeExtensions -f @($($physicalMachine.ComputerName))) ` | |
{ | |
($physicalMachine.Processors.HasVMMonitorModeExtensions | Measure-Object -Minimum).Minimum | Should Be $true | |
} | |
It ($BareMetalLocalizedStrings.TestVirtualizationIsEnabled -f @($($physicalMachine.ComputerName))) ` | |
{ | |
($physicalMachine.Processors.IsVirtualizationEnabled | Measure-Object -Minimum).Minimum | Should Be $true | |
} | |
It ($BareMetalLocalizedStrings.TestSecondLevelAddressTranslation -f @($($physicalMachine.ComputerName))) ` | |
{ | |
($physicalMachine.Processors.SupportsSecondLevelAddressTranslation | Measure-Object -Minimum).Minimum | Should Be $true | |
} | |
It ($BareMetalLocalizedStrings.TestDataExecutionPrevention -f @($($physicalMachine.ComputerName))) ` | |
{ | |
$physicalMachine.IsDataExecutionPreventionEnabled | Should Be $true | |
} | |
} | |
# Validate the OS version | |
foreach ($physicalMachine in $physicalMachines) | |
{ | |
It ($BareMetalLocalizedStrings.TestOperatingSystemVersion -f @($physicalMachine.ComputerName)) ` | |
{ | |
New-Object Version($physicalMachine.OSVersion) | Should Not BeLessThan $minimumOperatingSystemVersion | |
} | |
} | |
# In the case of OneNode, validate the OS SKU is a Datacenter SKU. | |
if ($isOneNode) | |
{ | |
It ($BareMetalLocalizedStrings.TestDatacenterSku -f @($env:COMPUTERNAME)) ` | |
{ | |
$serverDatacenterSku = 8 | |
$serverDatacenterEvalSku = 80 | |
($physicalMachine.OSSku -in @($serverDatacenterSku, $serverDatacenterEvalSku)) | Should Be $true | |
} | |
} | |
# In the case of OneNode, validate that the computer name is not AzureStack, because this is used as the domain name. | |
if ($isOneNode) | |
{ | |
It ($BareMetalLocalizedStrings.TestComputerNameIsNotAzureStack -f @($env:COMPUTERNAME)) ` | |
{ | |
$env:COMPUTERNAME | Should Not Be 'AzureStack' | |
} | |
} | |
# In the case of OneNode, validate that the computer is not already joined to a domain. | |
if ($isOneNode) | |
{ | |
It ($BareMetalLocalizedStrings.TestNotPartOfDomain -f @($env:COMPUTERNAME)) ` | |
{ | |
$physicalMachine.IsPartOfDomain | Should Be $false | |
} | |
} | |
# Validate the number of cores on each of the machines meets the required minimum. | |
if ($isVirtualizedDeployment) | |
{ | |
foreach ($physicalMachine in $physicalMachines) | |
{ | |
It ($BareMetalLocalizedStrings.TestMinimumNumberOfCores -f @($minimumNumberOfCoresPerMachine.ToString(), $physicalMachine.ComputerName)) ` | |
{ | |
($physicalMachine.Processors.NumberOfEnabledCores | Measure-Object -Sum).Sum | Should Not BeLessThan $minimumNumberOfCoresPerMachine | |
} | |
} | |
} | |
# Validate the amount of memory on each of the machines meets the required minimum. | |
if ($isVirtualizedDeployment) | |
{ | |
foreach ($physicalMachine in $physicalMachines) | |
{ | |
It ($BareMetalLocalizedStrings.TestMinimumPhysicalMemory -f @($minimumPhysicalMemoryPerMachineGB.ToString(), $physicalMachine.ComputerName)) ` | |
{ | |
($physicalMachine.TotalPhysicalMemory * $toleranceMultiplier) | Should Not BeLessThan ($minimumPhysicalMemoryPerMachineGB * 1GB) | |
} | |
} | |
} | |
# For multi-node, validate that each machine has the minimum required number of network adapters with the required speed. | |
if (-not $isOneNode) | |
{ | |
foreach ($physicalMachine in $physicalMachines) | |
{ | |
It ($BareMetalLocalizedStrings.TestMinimumNumberOfNetworkAdapters -f @($minimumNumberOfAdaptersPerMachine.ToString(), | |
$minimumAdapterSpeedBitsPerSecond.ToString(), | |
$($physicalMachine.ComputerName))) ` | |
{ | |
($physicalMachine.NetworkAdapters | ? Speed -ge $minimumAdapterSpeedBitsPerSecond).Count | | |
Should Not BeLessThan $minimumNumberOfAdaptersPerMachine | |
} | |
} | |
} | |
# For multi-node, validate that all machines have the same network adapter configuration in terms of speed and whether RDMA is enabled. | |
if (-not $isOneNode) | |
{ | |
foreach ($physicalMachine in $physicalMachines) | |
{ | |
if ($physicalMachine -eq $physicalMachines[0]) | |
{ | |
continue; | |
} | |
It ($BareMetalLocalizedStrings.TestSameNetworkAdapterConfiguration -f @($($physicalMachine.ComputerName), | |
$($physicalMachines[0].ComputerName))) ` | |
{ | |
$physicalMachine.NetworkAdapters.Count | Should Be $physicalMachines[0].NetworkAdapters.Count | |
# Adapters have already been sorted. We only need to compare each network adapter to the one with the same index on the other machine. | |
for ($i = 0; $i -lt $physicalMachine.NetworkAdapters.Count; $i++) | |
{ | |
$physicalMachine.NetworkAdapters[$i] | Should Be $physicalMachines[0].NetworkAdapters[$i] | |
} | |
} | |
} | |
} | |
# Validate the size of the system disk. | |
It ($BareMetalLocalizedStrings.TestSystemDiskMeetsMinimumSize -f @($physicalMachine.ComputerName, | |
$minimumSizeOfSystemDiskGB.ToString())) ` | |
{ | |
($physicalMachine.SystemDisk.Size * $toleranceMultiplier) | Should Not BeLessThan ($minimumSizeOfSystemDiskGB * 1GB) | |
} | |
# $disksUnion is the minimum-size superset of all disks. It is calculated to allow for performing checks even if some machines are missing some disks. | |
foreach ($physicalMachine in $physicalMachines) | |
{ | |
$combinedListOfDisksOnAllMachines += $physicalMachine.NonSystemDisks | |
} | |
$combinedListOfDisksOnAllMachines = ($combinedListOfDisksOnAllMachines | sort) | |
$disksUnion = @($combinedListOfDisksOnAllMachines[0]) | |
for ($i = 1; $i -lt $combinedListOfDisksOnAllMachines.Count; $i++) | |
{ | |
if ($combinedListOfDisksOnAllMachines[$i].BusType -ne $combinedListOfDisksOnAllMachines[$i-1].BusType -or | |
$combinedListOfDisksOnAllMachines[$i].IndexWithinSimilarDisks -ne $combinedListOfDisksOnAllMachines[$i-1].IndexWithinSimilarDisks -or | |
($combinedListOfDisksOnAllMachines[$i].Size * $toleranceMultiplier) -lt $combinedListOfDisksOnAllMachines[$i-1].Size -or | |
$combinedListOfDisksOnAllMachines[$i].Size -gt ($combinedListOfDisksOnAllMachines[$i-1].Size * $toleranceMultiplier)) | |
{ | |
# Disk does not have the same properties as the previous disks (Comparison with only one | |
# disk is sufficent because the disks have been sorted). Include disk in union. | |
$disksUnion += $combinedListOfDisksOnAllMachines[$i] | |
} | |
} | |
# Validate that the machines have at least the minimum number of required data disks that meet the minimum required size. | |
It ($BareMetalLocalizedStrings.TestNumberOfDisksMeetingMinimumSize -f @($minimumNumberOfDataDisksPerMachine.ToString(), | |
$minimumSizeOfDataDisksGB.ToString(), | |
$physicalMachine.ComputerName)) ` | |
{ | |
if ($isOneNode) | |
{ | |
$supportedBusTypes = @('SATA', 'SAS', 'RAID', 'NVMe') | |
} | |
else | |
{ | |
$supportedBusTypes = @('SATA', 'SAS', 'NVMe') | |
} | |
(($disksUnion | ? BusType -in $supportedBusTypes | sort -Property Size -Descending | | |
select -Skip ($minimumNumberOfDataDisksPerMachine-1) -First 1 -ExpandProperty Size) * $toleranceMultiplier) | | |
Should Not BeLessThan ($minimumSizeOfDataDisksGB * 1GB) | |
} | |
# For multi-node, validate that the number of disks that may be missing from any machine compared to other machines is not more than the maximum. | |
if (-not $isOneNode) | |
{ | |
foreach ($physicalMachine in $physicalMachines) | |
{ | |
It ($BareMetalLocalizedStrings.TestMaximumMissingDisksPerMachine -f @($maximumNumberOfMissingDisksPerMachine.ToString(), | |
$physicalMachine.ComputerName)) ` | |
{ | |
($disksUnion.Count - $physicalMachine.NonSystemDisks.Count) | Should Not BeGreaterThan $maximumNumberOfMissingDisksPerMachine | |
} | |
} | |
} | |
} | |
# SIG # Begin signature block | |
# MIIkWAYJKoZIhvcNAQcCoIIkSTCCJEUCAQExDzANBglghkgBZQMEAgEFADB5Bgor | |
# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG | |
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCBjDMN+D61uPMUu | |
# bVMHPDOv4RiJSm/OfpegnFZy7IAj9KCCDYEwggX/MIID56ADAgECAhMzAAABUZ6N | |
# j0Bxow5BAAAAAAFRMA0GCSqGSIb3DQEBCwUAMH4xCzAJBgNVBAYTAlVTMRMwEQYD | |
# VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy | |
# b3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNpZ25p | |
# bmcgUENBIDIwMTEwHhcNMTkwNTAyMjEzNzQ2WhcNMjAwNTAyMjEzNzQ2WjB0MQsw | |
# CQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9u | |
# ZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMR4wHAYDVQQDExVNaWNy | |
# b3NvZnQgQ29ycG9yYXRpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB | |
# AQCVWsaGaUcdNB7xVcNmdfZiVBhYFGcn8KMqxgNIvOZWNH9JYQLuhHhmJ5RWISy1 | |
# oey3zTuxqLbkHAdmbeU8NFMo49Pv71MgIS9IG/EtqwOH7upan+lIq6NOcw5fO6Os | |
# +12R0Q28MzGn+3y7F2mKDnopVu0sEufy453gxz16M8bAw4+QXuv7+fR9WzRJ2CpU | |
# 62wQKYiFQMfew6Vh5fuPoXloN3k6+Qlz7zgcT4YRmxzx7jMVpP/uvK6sZcBxQ3Wg | |
# B/WkyXHgxaY19IAzLq2QiPiX2YryiR5EsYBq35BP7U15DlZtpSs2wIYTkkDBxhPJ | |
# IDJgowZu5GyhHdqrst3OjkSRAgMBAAGjggF+MIIBejAfBgNVHSUEGDAWBgorBgEE | |
# AYI3TAgBBggrBgEFBQcDAzAdBgNVHQ4EFgQUV4Iarkq57esagu6FUBb270Zijc8w | |
# UAYDVR0RBEkwR6RFMEMxKTAnBgNVBAsTIE1pY3Jvc29mdCBPcGVyYXRpb25zIFB1 | |
# ZXJ0byBSaWNvMRYwFAYDVQQFEw0yMzAwMTIrNDU0MTM1MB8GA1UdIwQYMBaAFEhu | |
# ZOVQBdOCqhc3NyK1bajKdQKVMFQGA1UdHwRNMEswSaBHoEWGQ2h0dHA6Ly93d3cu | |
# bWljcm9zb2Z0LmNvbS9wa2lvcHMvY3JsL01pY0NvZFNpZ1BDQTIwMTFfMjAxMS0w | |
# Ny0wOC5jcmwwYQYIKwYBBQUHAQEEVTBTMFEGCCsGAQUFBzAChkVodHRwOi8vd3d3 | |
# Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2NlcnRzL01pY0NvZFNpZ1BDQTIwMTFfMjAx | |
# MS0wNy0wOC5jcnQwDAYDVR0TAQH/BAIwADANBgkqhkiG9w0BAQsFAAOCAgEAWg+A | |
# rS4Anq7KrogslIQnoMHSXUPr/RqOIhJX+32ObuY3MFvdlRElbSsSJxrRy/OCCZdS | |
# se+f2AqQ+F/2aYwBDmUQbeMB8n0pYLZnOPifqe78RBH2fVZsvXxyfizbHubWWoUf | |
# NW/FJlZlLXwJmF3BoL8E2p09K3hagwz/otcKtQ1+Q4+DaOYXWleqJrJUsnHs9UiL | |
# crVF0leL/Q1V5bshob2OTlZq0qzSdrMDLWdhyrUOxnZ+ojZ7UdTY4VnCuogbZ9Zs | |
# 9syJbg7ZUS9SVgYkowRsWv5jV4lbqTD+tG4FzhOwcRQwdb6A8zp2Nnd+s7VdCuYF | |
# sGgI41ucD8oxVfcAMjF9YX5N2s4mltkqnUe3/htVrnxKKDAwSYliaux2L7gKw+bD | |
# 1kEZ/5ozLRnJ3jjDkomTrPctokY/KaZ1qub0NUnmOKH+3xUK/plWJK8BOQYuU7gK | |
# YH7Yy9WSKNlP7pKj6i417+3Na/frInjnBkKRCJ/eYTvBH+s5guezpfQWtU4bNo/j | |
# 8Qw2vpTQ9w7flhH78Rmwd319+YTmhv7TcxDbWlyteaj4RK2wk3pY1oSz2JPE5PNu | |
# Nmd9Gmf6oePZgy7Ii9JLLq8SnULV7b+IP0UXRY9q+GdRjM2AEX6msZvvPCIoG0aY | |
# HQu9wZsKEK2jqvWi8/xdeeeSI9FN6K1w4oVQM4Mwggd6MIIFYqADAgECAgphDpDS | |
# AAAAAAADMA0GCSqGSIb3DQEBCwUAMIGIMQswCQYDVQQGEwJVUzETMBEGA1UECBMK | |
# V2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0 | |
# IENvcnBvcmF0aW9uMTIwMAYDVQQDEylNaWNyb3NvZnQgUm9vdCBDZXJ0aWZpY2F0 | |
# ZSBBdXRob3JpdHkgMjAxMTAeFw0xMTA3MDgyMDU5MDlaFw0yNjA3MDgyMTA5MDla | |
# MH4xCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdS | |
# ZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMT | |
# H01pY3Jvc29mdCBDb2RlIFNpZ25pbmcgUENBIDIwMTEwggIiMA0GCSqGSIb3DQEB | |
# AQUAA4ICDwAwggIKAoICAQCr8PpyEBwurdhuqoIQTTS68rZYIZ9CGypr6VpQqrgG | |
# OBoESbp/wwwe3TdrxhLYC/A4wpkGsMg51QEUMULTiQ15ZId+lGAkbK+eSZzpaF7S | |
# 35tTsgosw6/ZqSuuegmv15ZZymAaBelmdugyUiYSL+erCFDPs0S3XdjELgN1q2jz | |
# y23zOlyhFvRGuuA4ZKxuZDV4pqBjDy3TQJP4494HDdVceaVJKecNvqATd76UPe/7 | |
# 4ytaEB9NViiienLgEjq3SV7Y7e1DkYPZe7J7hhvZPrGMXeiJT4Qa8qEvWeSQOy2u | |
# M1jFtz7+MtOzAz2xsq+SOH7SnYAs9U5WkSE1JcM5bmR/U7qcD60ZI4TL9LoDho33 | |
# X/DQUr+MlIe8wCF0JV8YKLbMJyg4JZg5SjbPfLGSrhwjp6lm7GEfauEoSZ1fiOIl | |
# XdMhSz5SxLVXPyQD8NF6Wy/VI+NwXQ9RRnez+ADhvKwCgl/bwBWzvRvUVUvnOaEP | |
# 6SNJvBi4RHxF5MHDcnrgcuck379GmcXvwhxX24ON7E1JMKerjt/sW5+v/N2wZuLB | |
# l4F77dbtS+dJKacTKKanfWeA5opieF+yL4TXV5xcv3coKPHtbcMojyyPQDdPweGF | |
# RInECUzF1KVDL3SV9274eCBYLBNdYJWaPk8zhNqwiBfenk70lrC8RqBsmNLg1oiM | |
# CwIDAQABo4IB7TCCAekwEAYJKwYBBAGCNxUBBAMCAQAwHQYDVR0OBBYEFEhuZOVQ | |
# BdOCqhc3NyK1bajKdQKVMBkGCSsGAQQBgjcUAgQMHgoAUwB1AGIAQwBBMAsGA1Ud | |
# DwQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFHItOgIxkEO5FAVO | |
# 4eqnxzHRI4k0MFoGA1UdHwRTMFEwT6BNoEuGSWh0dHA6Ly9jcmwubWljcm9zb2Z0 | |
# LmNvbS9wa2kvY3JsL3Byb2R1Y3RzL01pY1Jvb0NlckF1dDIwMTFfMjAxMV8wM18y | |
# Mi5jcmwwXgYIKwYBBQUHAQEEUjBQME4GCCsGAQUFBzAChkJodHRwOi8vd3d3Lm1p | |
# Y3Jvc29mdC5jb20vcGtpL2NlcnRzL01pY1Jvb0NlckF1dDIwMTFfMjAxMV8wM18y | |
# Mi5jcnQwgZ8GA1UdIASBlzCBlDCBkQYJKwYBBAGCNy4DMIGDMD8GCCsGAQUFBwIB | |
# FjNodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2RvY3MvcHJpbWFyeWNw | |
# cy5odG0wQAYIKwYBBQUHAgIwNB4yIB0ATABlAGcAYQBsAF8AcABvAGwAaQBjAHkA | |
# XwBzAHQAYQB0AGUAbQBlAG4AdAAuIB0wDQYJKoZIhvcNAQELBQADggIBAGfyhqWY | |
# 4FR5Gi7T2HRnIpsLlhHhY5KZQpZ90nkMkMFlXy4sPvjDctFtg/6+P+gKyju/R6mj | |
# 82nbY78iNaWXXWWEkH2LRlBV2AySfNIaSxzzPEKLUtCw/WvjPgcuKZvmPRul1LUd | |
# d5Q54ulkyUQ9eHoj8xN9ppB0g430yyYCRirCihC7pKkFDJvtaPpoLpWgKj8qa1hJ | |
# Yx8JaW5amJbkg/TAj/NGK978O9C9Ne9uJa7lryft0N3zDq+ZKJeYTQ49C/IIidYf | |
# wzIY4vDFLc5bnrRJOQrGCsLGra7lstnbFYhRRVg4MnEnGn+x9Cf43iw6IGmYslmJ | |
# aG5vp7d0w0AFBqYBKig+gj8TTWYLwLNN9eGPfxxvFX1Fp3blQCplo8NdUmKGwx1j | |
# NpeG39rz+PIWoZon4c2ll9DuXWNB41sHnIc+BncG0QaxdR8UvmFhtfDcxhsEvt9B | |
# xw4o7t5lL+yX9qFcltgA1qFGvVnzl6UJS0gQmYAf0AApxbGbpT9Fdx41xtKiop96 | |
# eiL6SJUfq/tHI4D1nvi/a7dLl+LrdXga7Oo3mXkYS//WsyNodeav+vyL6wuA6mk7 | |
# r/ww7QRMjt/fdW1jkT3RnVZOT7+AVyKheBEyIXrvQQqxP/uozKRdwaGIm1dxVk5I | |
# RcBCyZt2WwqASGv9eZ/BvW1taslScxMNelDNMYIWLTCCFikCAQEwgZUwfjELMAkG | |
# A1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQx | |
# HjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEoMCYGA1UEAxMfTWljcm9z | |
# b2Z0IENvZGUgU2lnbmluZyBQQ0EgMjAxMQITMwAAAVGejY9AcaMOQQAAAAABUTAN | |
# BglghkgBZQMEAgEFAKCBrjAZBgkqhkiG9w0BCQMxDAYKKwYBBAGCNwIBBDAcBgor | |
# BgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAvBgkqhkiG9w0BCQQxIgQgN8Bc9FSk | |
# 8kjAYMW4BmgBoTkeWLixZPUESgxCzA9pE+wwQgYKKwYBBAGCNwIBDDE0MDKgFIAS | |
# AE0AaQBjAHIAbwBzAG8AZgB0oRqAGGh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbTAN | |
# BgkqhkiG9w0BAQEFAASCAQAa3huY7aEY7gU0UzcAS2uYeuSJUdpjGDXZXAiMHFQa | |
# YDGonlsABE1sW+aOmVZJu+jWy6DmB4oQRaepY+vWgYTYwGUqGxS3F9fJU/zqyIsd | |
# CTIjhxbeU540rS9U+yEfhDKuQDhD/M0U6+r6qYd2BdS8LiJ5TguNiE+HK9kkXtcx | |
# KfjsG7RT60sYv0BRwYzhAPD+JayzGGsA6G5d1/1vDFNQybAfbneGb72I9JrNjK9O | |
# 4vkXGQv0ZTN+3AVtqpxeFBSCzTkqaEF6uX7lS3j2gF8eHuW4LFJnIdJCWEcuPiLx | |
# cpbujrX4j4fTurBTeSA2ERirPow6krsYP6hinSf+VVEUoYITtzCCE7MGCisGAQQB | |
# gjcDAwExghOjMIITnwYJKoZIhvcNAQcCoIITkDCCE4wCAQMxDzANBglghkgBZQME | |
# AgEFADCCAVUGCyqGSIb3DQEJEAEEoIIBRASCAUAwggE8AgEBBgorBgEEAYRZCgMB | |
# MDEwDQYJYIZIAWUDBAIBBQAEIBJo7Ly5gCr25XZQDrroahGZ6Pq+67+gFRsFyAXj | |
# ghlIAgZdPyaQfvQYEzIwMTkwODE1MDgyODEwLjE0M1owBIACAfSggdSkgdEwgc4x | |
# CzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRt | |
# b25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKTAnBgNVBAsTIE1p | |
# Y3Jvc29mdCBPcGVyYXRpb25zIFB1ZXJ0byBSaWNvMSYwJAYDVQQLEx1UaGFsZXMg | |
# VFNTIEVTTjoxNDhDLUM0QjktMjA2NjElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUt | |
# U3RhbXAgU2VydmljZaCCDyIwggT1MIID3aADAgECAhMzAAAA1acj5XiVagn/AAAA | |
# AADVMA0GCSqGSIb3DQEBCwUAMHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNo | |
# aW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29y | |
# cG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEw | |
# MB4XDTE4MDgyMzIwMjY0NVoXDTE5MTEyMzIwMjY0NVowgc4xCzAJBgNVBAYTAlVT | |
# MRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQK | |
# ExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKTAnBgNVBAsTIE1pY3Jvc29mdCBPcGVy | |
# YXRpb25zIFB1ZXJ0byBSaWNvMSYwJAYDVQQLEx1UaGFsZXMgVFNTIEVTTjoxNDhD | |
# LUM0QjktMjA2NjElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUtU3RhbXAgU2Vydmlj | |
# ZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMIUCSuuF/E0gjL87onw | |
# du50M1BwUxQwtrZWEQYCmLMhGhNR/3eQxBD9lqY4FYxhRZ1bxfsIjs7SKvcF0m1Y | |
# fhgK5j9BdZvzueedkEY8MVUu41zB18dn+Nmaelz5vFz9BLq5hJPAEdZg3ZR+4dP7 | |
# 19tVXJP/iVkslLTTKH/YqT8VciemXOUaCsK/Re7xdBe8qBShsOwbgD7xVYwgmevz | |
# +bSZ3UsWClccBB9kMhNHhniIKkuMo4BNJ5te+yp8KBVyzontKmtOhS1XKNSIywEW | |
# AzRGKZtlP36DVGOy9SGajweV0RjHkqZMUD1tEiIaDoBpiZXacHPne1FQGwSjK4NM | |
# lEsCAwEAAaOCARswggEXMB0GA1UdDgQWBBQpaKMxlHZD2V9dDZuf07KE9mI3hDAf | |
# BgNVHSMEGDAWgBTVYzpcijGQ80N7fEYbxTNoWoVtVTBWBgNVHR8ETzBNMEugSaBH | |
# hkVodHRwOi8vY3JsLm1pY3Jvc29mdC5jb20vcGtpL2NybC9wcm9kdWN0cy9NaWNU | |
# aW1TdGFQQ0FfMjAxMC0wNy0wMS5jcmwwWgYIKwYBBQUHAQEETjBMMEoGCCsGAQUF | |
# BzAChj5odHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpL2NlcnRzL01pY1RpbVN0 | |
# YVBDQV8yMDEwLTA3LTAxLmNydDAMBgNVHRMBAf8EAjAAMBMGA1UdJQQMMAoGCCsG | |
# AQUFBwMIMA0GCSqGSIb3DQEBCwUAA4IBAQB52cvC6cTRwKstDtImv9pnoAv99sC2 | |
# wQLy4rmBzWL2kI/sleBNXo74cqdAWoYfLRCQo+DuW2m5/0mMe4DpvPDLYCQSSJkA | |
# tu/hUL80wik5ZqaNvIfp7h1/CJ/KMuIgd8WbhNZxLyG5S4W7As667QNifweCrjFq | |
# ZMV9XtBO2wjz9Qti1RjdOImhX7V4kdCYKStdQg2xJhUU9ZtPqGprthaPqto9Fheq | |
# s37Cw2vO3tAjYqMXTZCvybz+4bUHxIWvqvJIXLpuPPz1zCvCotLfBzzBiV9miIEB | |
# Xsnv2qIIkdDnISqlrIZ1nXdRHdXl4ZhBf3wAZR/EwILvKaksKFYzrk4LMIIGcTCC | |
# BFmgAwIBAgIKYQmBKgAAAAAAAjANBgkqhkiG9w0BAQsFADCBiDELMAkGA1UEBhMC | |
# VVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNV | |
# BAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEyMDAGA1UEAxMpTWljcm9zb2Z0IFJv | |
# b3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IDIwMTAwHhcNMTAwNzAxMjEzNjU1WhcN | |
# MjUwNzAxMjE0NjU1WjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3Rv | |
# bjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0 | |
# aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDCCASIw | |
# DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKkdDbx3EYo6IOz8E5f1+n9plGt0 | |
# VBDVpQoAgoX77XxoSyxfxcPlYcJ2tz5mK1vwFVMnBDEfQRsalR3OCROOfGEwWbEw | |
# RA/xYIiEVEMM1024OAizQt2TrNZzMFcmgqNFDdDq9UeBzb8kYDJYYEbyWEeGMoQe | |
# dGFnkV+BVLHPk0ySwcSmXdFhE24oxhr5hoC732H8RsEnHSRnEnIaIYqvS2SJUGKx | |
# Xf13Hz3wV3WsvYpCTUBR0Q+cBj5nf/VmwAOWRH7v0Ev9buWayrGo8noqCjHw2k4G | |
# kbaICDXoeByw6ZnNPOcvRLqn9NxkvaQBwSAJk3jN/LzAyURdXhacAQVPIk0CAwEA | |
# AaOCAeYwggHiMBAGCSsGAQQBgjcVAQQDAgEAMB0GA1UdDgQWBBTVYzpcijGQ80N7 | |
# fEYbxTNoWoVtVTAZBgkrBgEEAYI3FAIEDB4KAFMAdQBiAEMAQTALBgNVHQ8EBAMC | |
# AYYwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBTV9lbLj+iiXGJo0T2UkFvX | |
# zpoYxDBWBgNVHR8ETzBNMEugSaBHhkVodHRwOi8vY3JsLm1pY3Jvc29mdC5jb20v | |
# cGtpL2NybC9wcm9kdWN0cy9NaWNSb29DZXJBdXRfMjAxMC0wNi0yMy5jcmwwWgYI | |
# KwYBBQUHAQEETjBMMEoGCCsGAQUFBzAChj5odHRwOi8vd3d3Lm1pY3Jvc29mdC5j | |
# b20vcGtpL2NlcnRzL01pY1Jvb0NlckF1dF8yMDEwLTA2LTIzLmNydDCBoAYDVR0g | |
# AQH/BIGVMIGSMIGPBgkrBgEEAYI3LgMwgYEwPQYIKwYBBQUHAgEWMWh0dHA6Ly93 | |
# d3cubWljcm9zb2Z0LmNvbS9QS0kvZG9jcy9DUFMvZGVmYXVsdC5odG0wQAYIKwYB | |
# BQUHAgIwNB4yIB0ATABlAGcAYQBsAF8AUABvAGwAaQBjAHkAXwBTAHQAYQB0AGUA | |
# bQBlAG4AdAAuIB0wDQYJKoZIhvcNAQELBQADggIBAAfmiFEN4sbgmD+BcQM9naOh | |
# IW+z66bM9TG+zwXiqf76V20ZMLPCxWbJat/15/B4vceoniXj+bzta1RXCCtRgkQS | |
# +7lTjMz0YBKKdsxAQEGb3FwX/1z5Xhc1mCRWS3TvQhDIr79/xn/yN31aPxzymXlK | |
# kVIArzgPF/UveYFl2am1a+THzvbKegBvSzBEJCI8z+0DpZaPWSm8tv0E4XCfMkon | |
# /VWvL/625Y4zu2JfmttXQOnxzplmkIz/amJ/3cVKC5Em4jnsGUpxY517IW3DnKOi | |
# PPp/fZZqkHimbdLhnPkd/DjYlPTGpQqWhqS9nhquBEKDuLWAmyI4ILUl5WTs9/S/ | |
# fmNZJQ96LjlXdqJxqgaKD4kWumGnEcua2A5HmoDF0M2n0O99g/DhO3EJ3110mCII | |
# YdqwUB5vvfHhAN/nMQekkzr3ZUd46PioSKv33nJ+YWtvd6mBy6cJrDm77MbL2IK0 | |
# cs0d9LiFAR6A+xuJKlQ5slvayA1VmXqHczsI5pgt6o3gMy4SKfXAL1QnIffIrE7a | |
# KLixqduWsqdCosnPGUFN4Ib5KpqjEWYw07t0MkvfY3v1mYovG8chr1m1rtxEPJdQ | |
# cdeh0sVV42neV8HR3jDA/czmTfsNv11P6Z0eGTgvvM9YBS7vDaBQNdrvCScc1bN+ | |
# NR4Iuto229Nfj950iEkSoYIDsDCCApgCAQEwgf6hgdSkgdEwgc4xCzAJBgNVBAYT | |
# AlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYD | |
# VQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKTAnBgNVBAsTIE1pY3Jvc29mdCBP | |
# cGVyYXRpb25zIFB1ZXJ0byBSaWNvMSYwJAYDVQQLEx1UaGFsZXMgVFNTIEVTTjox | |
# NDhDLUM0QjktMjA2NjElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUtU3RhbXAgU2Vy | |
# dmljZaIlCgEBMAkGBSsOAwIaBQADFQCtwyS80dl18F3Q3UUfOTGebtaesaCB3jCB | |
# 26SB2DCB1TELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNV | |
# BAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEpMCcG | |
# A1UECxMgTWljcm9zb2Z0IE9wZXJhdGlvbnMgUHVlcnRvIFJpY28xJzAlBgNVBAsT | |
# Hm5DaXBoZXIgTlRTIEVTTjo1N0Y2LUMxRTAtNTU0QzErMCkGA1UEAxMiTWljcm9z | |
# b2Z0IFRpbWUgU291cmNlIE1hc3RlciBDbG9jazANBgkqhkiG9w0BAQUFAAIFAOD/ | |
# ZMYwIhgPMjAxOTA4MTUxMjU3NDJaGA8yMDE5MDgxNjEyNTc0MlowdzA9BgorBgEE | |
# AYRZCgQBMS8wLTAKAgUA4P9kxgIBADAKAgEAAgIXtQIB/zAHAgEAAgIYWDAKAgUA | |
# 4QC2RgIBADA2BgorBgEEAYRZCgQCMSgwJjAMBgorBgEEAYRZCgMBoAowCAIBAAID | |
# B6EgoQowCAIBAAIDB6EgMA0GCSqGSIb3DQEBBQUAA4IBAQDBgvAxiKFLeeT0hB1Y | |
# Tx6QE7j+AZnFUzikHZ63ysIelO3sSOIlL95ZcMFhpFibvTBldIqXttijClwsrX08 | |
# cZzj7mI7SkZf4MueKY7fXNu2oAMaQci67y+Geo9uoN2RI0Sy32de0Ha+Xnb/PM7M | |
# 2hwBibw2nYKEF7BKH9jx7kX/mho9HAoc5VzawuUrlufVsWqV8dsvFcBqaOq7gShx | |
# kxejh6paJz0UCrO6L+5ePXZ3EYLGHLHyOvZOBIHdkSeUrrPPZyFTxnZHwSUtC5fS | |
# HyetvEcZ6US7299P5JIWEO9+QzeoCSzAkSskC2s3CbMdGtY/q35PwY/CIusUc0l1 | |
# SGUcMYIC9TCCAvECAQEwgZMwfDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hp | |
# bmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jw | |
# b3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENBIDIwMTAC | |
# EzMAAADVpyPleJVqCf8AAAAAANUwDQYJYIZIAWUDBAIBBQCgggEyMBoGCSqGSIb3 | |
# DQEJAzENBgsqhkiG9w0BCRABBDAvBgkqhkiG9w0BCQQxIgQgyGlMFOmhYMKcYevJ | |
# 462GJKJPvp/b6OkHp2cm5QgdxxcwgeIGCyqGSIb3DQEJEAIMMYHSMIHPMIHMMIGx | |
# BBStwyS80dl18F3Q3UUfOTGebtaesTCBmDCBgKR+MHwxCzAJBgNVBAYTAlVTMRMw | |
# EQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVN | |
# aWNyb3NvZnQgQ29ycG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0 | |
# YW1wIFBDQSAyMDEwAhMzAAAA1acj5XiVagn/AAAAAADVMBYEFNFDfAWvn2fpcC4T | |
# kL+KEq5Te55SMA0GCSqGSIb3DQEBCwUABIIBABnq/RH7vrYyv8D9r/kNRjzdXqZ7 | |
# QLf4Zc0dD912qL/c5D5FA/oK0bNcHPP9Ag04IBQC7LU9HJoegpONjtaq5uKADofk | |
# AnJjLTHR5gJaVmgpVMVo+29GWYQfncIpK4vbFlJTgSpAHf50rXC7NBwm+MsL0dFW | |
# X48bKLceVC6+bPHXYcqvWK1CQUBgXNnN9MgzDD0kgJSyHvBuMTYnTdkYBojRnRHO | |
# dVxY2+UXwle12VSmK3xhGHYT4+luH2K1Hne04wttUx5nJD+peXx7wjsX5RCXZUlc | |
# vTus+izrjXKqzyLsiYEb/54Q8XDxwgtGfza26rQJGKC6YoLqdF5mMcJij0c= | |
# SIG # End signature block |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment