Created
January 8, 2020 10:30
-
-
Save mritsurgeon/f06587b9161711fb40fa91bf595a8af9 to your computer and use it in GitHub Desktop.
ASDK Bare Metal test Netsed edit
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<################################################### | |
# # | |
# 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