Private/Kinds/System.ps1
|
# The System Kind: what the machine is. # # Two Checks here are about age rather than a reading, so the Gatherer records when it # looked. The Judge computes the age from that rather than from the clock, which keeps it # pure and lets a fixture pin an age exactly. function Get-SystemData { [CmdletBinding()] [OutputType([psobject])] param([hashtable]$Parameters = @{}) $computer = Get-CimInstance Win32_ComputerSystem -ErrorAction SilentlyContinue $os = Get-CimInstance Win32_OperatingSystem -ErrorAction SilentlyContinue $bios = Get-CimInstance Win32_BIOS -ErrorAction SilentlyContinue $cpu = @(Get-CimInstance Win32_Processor -ErrorAction SilentlyContinue)[0] $current = Get-ItemProperty 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion' -ErrorAction SilentlyContinue $power = Get-ItemProperty 'HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager\Power' -ErrorAction SilentlyContinue $modules = @(Get-CimInstance Win32_PhysicalMemory -ErrorAction SilentlyContinue | ForEach-Object { [pscustomobject]@{ CapacityBytes = $_.Capacity; ConfiguredClockSpeed = $_.ConfiguredClockSpeed } }) # What the box at the top of the Report shows beyond the above: the board, the # graphics adapters with their driver, the drives, and every driver that is not # Microsoft's - the ones a vendor's update tool would replace. # What ipconfig /all says, of every adapter that has an address: Windows keeps it in # the adapter's configuration, and the name a person knows it by ("Ethernet", "WLAN") # and its speed in the adapter itself. $adapters = @{} foreach ($adapter in @(Get-CimInstance Win32_NetworkAdapter -ErrorAction SilentlyContinue)) { $adapters[[string]$adapter.Index] = $adapter } $network = @(Get-CimInstance Win32_NetworkAdapterConfiguration -Filter 'IPEnabled = TRUE' -ErrorAction SilentlyContinue | ForEach-Object { $adapter = $adapters[[string]$_.Index] [pscustomobject]@{ Alias = $adapter.NetConnectionID Description = $_.Description MacAddress = $_.MACAddress SpeedBps = $adapter.Speed Addresses = @($_.IPAddress) Subnets = @($_.IPSubnet) Gateways = @($_.DefaultIPGateway) DnsServers = @($_.DNSServerSearchOrder) DnsSuffix = $_.DNSDomain DhcpEnabled = $_.DHCPEnabled DhcpServer = $_.DHCPServer LeaseObtained = $_.DHCPLeaseObtained LeaseExpires = $_.DHCPLeaseExpires } }) $board = @(Get-CimInstance Win32_BaseBoard -ErrorAction SilentlyContinue)[0] $graphics = @(Get-CimInstance Win32_VideoController -ErrorAction SilentlyContinue | ForEach-Object { [pscustomobject]@{ Name = $_.Name; DriverVersion = $_.DriverVersion; DriverDate = $_.DriverDate PnpDeviceId = "$($_.PNPDeviceID)" Horizontal = $_.CurrentHorizontalResolution; Vertical = $_.CurrentVerticalResolution } }) $disks = @(Get-PhysicalDisk -ErrorAction SilentlyContinue | ForEach-Object { [pscustomobject]@{ Name = $_.FriendlyName; SizeBytes = $_.Size; MediaType = "$($_.MediaType)"; BusType = "$($_.BusType)" } }) $drivers = @(Get-CimInstance Win32_PnPSignedDriver -ErrorAction SilentlyContinue | Where-Object { $_.DeviceName -and $_.DriverProviderName -and $_.DriverProviderName -ne 'Microsoft' } | ForEach-Object { [pscustomobject]@{ Class = $_.DeviceClass; Device = $_.DeviceName; Provider = $_.DriverProviderName Version = $_.DriverVersion; Date = $_.DriverDate } }) [pscustomobject]@{ PSTypeName = 'Gutcheck.Data.System' GatheredAt = Get-Date Manufacturer = $computer.Manufacturer Model = $computer.Model SerialNumber = $bios.SerialNumber OsCaption = $os.Caption OsDisplayVersion = $current.DisplayVersion OsBuildNumber = $os.BuildNumber OsUpdateBuildRevision = $current.UBR BiosVersion = $bios.SMBIOSBIOSVersion BiosReleaseDate = $bios.ReleaseDate ProcessorName = $cpu.Name ProcessorCores = $cpu.NumberOfCores ProcessorLogicalCount = $cpu.NumberOfLogicalProcessors TotalPhysicalMemoryBytes = $computer.TotalPhysicalMemory MemoryModules = $modules LastBootUpTime = $os.LastBootUpTime FastStartupEnabled = $power.HiberbootEnabled OsArchitecture = $os.OSArchitecture OsInstallDate = $os.InstallDate Domain = $computer.Domain PartOfDomain = $computer.PartOfDomain BaseBoard = ('{0} {1}' -f $board.Manufacturer, $board.Product).Trim() Graphics = $graphics Disks = $disks Drivers = $drivers HostName = $computer.DNSHostName NetworkAdapters = $network } } function ConvertTo-SystemPrefixLength { <# .SYNOPSIS A subnet as the number after the slash: "255.255.255.0" is 24, and "64" is 64. Pure. .DESCRIPTION Windows gives an IPv4 subnet as a mask and an IPv6 one as the length already. Nothing for what is neither, and nothing for a mask that is not one. #> [CmdletBinding()] [OutputType([int])] param([AllowNull()][AllowEmptyString()][string]$Subnet) $text = "$Subnet".Trim() if ($text -match '^\d{1,3}$') { return [int]$text } if ($text -notmatch '^\d{1,3}(\.\d{1,3}){3}$') { return } $bits = '' foreach ($part in ($text -split '\.')) { if ([int]$part -gt 255) { return } $bits += [Convert]::ToString([int]$part, 2).PadLeft(8, '0') } if ($bits -notmatch '^1*0*$') { return } ($bits -replace '0', '').Length } function Get-SystemNetworkAddress { <# .SYNOPSIS An adapter's addresses as "address/length", IPv4 or IPv6 as asked. Pure. .DESCRIPTION A link-local IPv6 address (fe80::) is left out: every adapter has one, and it says nothing about where the machine is. #> [CmdletBinding()] [OutputType([string])] param([AllowNull()]$Adapter, [ValidateSet('IPv4', 'IPv6')][string]$Family = 'IPv4') $addresses = Get-DataCollection $Adapter 'Addresses' $subnets = Get-DataCollection $Adapter 'Subnets' for ($i = 0; $i -lt $addresses.Count; $i++) { $address = "$($addresses[$i])".Trim() if (-not $address) { continue } $isV6 = $address.Contains(':') if ($isV6 -ne ($Family -eq 'IPv6')) { continue } if ($isV6 -and $address -match '^(?i)fe80:') { continue } $length = $null if ($i -lt $subnets.Count) { $length = ConvertTo-SystemPrefixLength -Subnet "$($subnets[$i])" } if ($null -ne $length) { '{0}/{1}' -f $address, $length } else { $address } } } function Format-SystemLinkSpeed { <# .SYNOPSIS A link speed as people say it: 1000000000 is "1 Gbit/s". Pure; nothing for no speed. #> [CmdletBinding()] [OutputType([string])] param([AllowNull()]$BitsPerSecond) $speed = ConvertTo-Number $BitsPerSecond # An adapter without a link reports the largest number there is. if ($null -eq $speed -or $speed -le 0 -or $speed -gt 1e12) { return '' } if ($speed -ge 1e9) { return '{0:0.##} Gbit/s' -f ($speed / 1e9) } '{0:0.##} Mbit/s' -f ($speed / 1e6) } function Format-SystemDate { <# .SYNOPSIS A date as the box shows it, or nothing for what is no date. Pure. #> [CmdletBinding()] [OutputType([string])] param([AllowNull()]$Value, [string]$Format = 'dd.MM.yyyy') if ($null -eq $Value -or "$Value" -eq '') { return '' } try { ([datetime]$Value).ToString($Format) } catch { '' } } function Get-SystemInformationRow { <# .SYNOPSIS What the box at the top of the Report says about the machine: Group, Name, Value, in the order they are shown. Pure. .DESCRIPTION Only what the machine reported. A value it did not report is a row left out, not a row that says "unknown": the box is for reading at a glance, and the Findings already say what could not be read. #> [CmdletBinding()] [OutputType([psobject])] param([AllowNull()]$Data) $rows = New-Object 'System.Collections.Generic.List[psobject]' $add = { param([string]$Group, [string]$Name, $Value) $text = "$Value".Trim() if ($text) { $rows.Add([pscustomobject]@{ Group = $Group; Name = $Name; Value = $text }) } } $device = Get-Text 'System.Info.Group.Device' & $add $device (Get-Text 'System.Info.Manufacturer') (Get-DataProperty $Data 'Manufacturer') & $add $device (Get-Text 'System.Info.Model') (Get-DataProperty $Data 'Model') & $add $device (Get-Text 'System.Info.Serial') (Get-DataProperty $Data 'SerialNumber') & $add $device (Get-Text 'System.Info.Board') (Get-DataProperty $Data 'BaseBoard') $bios = "$(Get-DataProperty $Data 'BiosVersion')".Trim() $biosDate = Format-SystemDate (Get-DataProperty $Data 'BiosReleaseDate') if ($bios -and $biosDate) { $bios = (Get-Text 'System.Info.VersionOf') -f $bios, $biosDate } & $add $device (Get-Text 'System.Info.Bios') $bios $windows = Get-Text 'System.Info.Group.Windows' & $add $windows (Get-Text 'System.Info.Edition') (Get-DataProperty $Data 'OsCaption') & $add $windows (Get-Text 'System.Info.Version') (Get-DataProperty $Data 'OsDisplayVersion') $build = "$(Get-DataProperty $Data 'OsBuildNumber')" $revision = "$(Get-DataProperty $Data 'OsUpdateBuildRevision')" if ($build -and $revision) { $build = '{0}.{1}' -f $build, $revision } & $add $windows (Get-Text 'System.Info.Build') $build & $add $windows (Get-Text 'System.Info.Architecture') (Get-DataProperty $Data 'OsArchitecture') & $add $windows (Get-Text 'System.Info.InstallDate') (Format-SystemDate (Get-DataProperty $Data 'OsInstallDate')) & $add $windows (Get-Text 'System.Info.LastBoot') (Format-SystemDate (Get-DataProperty $Data 'LastBootUpTime') 'dd.MM.yyyy HH:mm') $domainLabel = Get-Text 'System.Info.Workgroup' if ([bool](Get-DataProperty $Data 'PartOfDomain')) { $domainLabel = Get-Text 'System.Info.Domain' } & $add $windows $domainLabel (Get-DataProperty $Data 'Domain') $hardware = Get-Text 'System.Info.Group.Hardware' $cpu = "$(Get-DataProperty $Data 'ProcessorName')".Trim() if ($cpu) { & $add $hardware (Get-Text 'System.Info.Processor') ((Get-Text 'Value.System.Processor') -f $cpu, (Get-DataProperty $Data 'ProcessorCores'), (Get-DataProperty $Data 'ProcessorLogicalCount')) } $bytes = ConvertTo-Number (Get-DataProperty $Data 'TotalPhysicalMemoryBytes') if ($null -ne $bytes -and $bytes -gt 0) { $memory = '{0} GB' -f [math]::Round($bytes / 1GB, 1) $modules = Get-DataCollection $Data 'MemoryModules' if ($modules.Count) { $sizes = @($modules | ForEach-Object { ConvertTo-Number (Get-DataProperty $_ 'CapacityBytes') } | Where-Object { $_ } | ForEach-Object { '{0} GB' -f [math]::Round($_ / 1GB) }) $memory = (Get-Text 'System.Info.MemoryModules') -f $memory, $modules.Count, ($sizes -join ' + ') } & $add $hardware (Get-Text 'System.Info.Memory') $memory } foreach ($adapter in (Get-DataCollection $Data 'Graphics')) { $name = "$(Get-DataProperty $adapter 'Name')".Trim() $h = ConvertTo-Number (Get-DataProperty $adapter 'Horizontal') $v = ConvertTo-Number (Get-DataProperty $adapter 'Vertical') if ($name -and $h -and $v) { $name = (Get-Text 'System.Info.Resolution') -f $name, $h, $v } & $add $hardware (Get-Text 'System.Info.Graphics') $name } foreach ($disk in (Get-DataCollection $Data 'Disks')) { $name = "$(Get-DataProperty $disk 'Name')".Trim() if (-not $name) { continue } $facts = @() $size = ConvertTo-Number (Get-DataProperty $disk 'SizeBytes') if ($size) { $facts += '{0:N0} GB' -f ($size / 1GB) } foreach ($fact in 'MediaType', 'BusType') { $text = "$(Get-DataProperty $disk $fact)".Trim() if ($text -and $text -ne 'Unspecified' -and $text -ne '0') { $facts += $text } } if ($facts.Count) { $name = '{0} - {1}' -f $name, ($facts -join ', ') } & $add $hardware (Get-Text 'System.Info.Disk') $name } # The network, as ipconfig /all would say it: one group per adapter that has an address. $hostName = "$(Get-DataProperty $Data 'HostName')".Trim() foreach ($adapter in (Get-DataCollection $Data 'NetworkAdapters')) { $alias = "$(Get-DataProperty $adapter 'Alias')".Trim() if (-not $alias) { $alias = "$(Get-DataProperty $adapter 'Description')".Trim() } $net = (Get-Text 'System.Info.Group.Network') -f $alias $what = "$(Get-DataProperty $adapter 'Description')".Trim() $speed = Format-SystemLinkSpeed (Get-DataProperty $adapter 'SpeedBps') if ($what -and $speed) { $what = '{0}, {1}' -f $what, $speed } & $add $net (Get-Text 'System.Info.Adapter') $what if ($hostName) { $suffix = "$(Get-DataProperty $adapter 'DnsSuffix')".Trim() $name = $hostName if ($suffix) { $name = '{0}.{1}' -f $hostName, $suffix } & $add $net (Get-Text 'System.Info.HostName') $name } & $add $net (Get-Text 'System.Info.IPv4') (@(Get-SystemNetworkAddress -Adapter $adapter -Family IPv4) -join ', ') & $add $net (Get-Text 'System.Info.IPv6') (@(Get-SystemNetworkAddress -Adapter $adapter -Family IPv6) -join ', ') & $add $net (Get-Text 'System.Info.Gateway') ((Get-DataCollection $adapter 'Gateways') -join ', ') & $add $net (Get-Text 'System.Info.DnsServers') ((Get-DataCollection $adapter 'DnsServers') -join ', ') $dhcp = Get-Text 'System.Info.DhcpOff' if ([bool](Get-DataProperty $adapter 'DhcpEnabled')) { $dhcp = Get-Text 'System.Info.DhcpOn' $server = "$(Get-DataProperty $adapter 'DhcpServer')".Trim() if ($server) { $dhcp = (Get-Text 'System.Info.DhcpServer') -f $server } $until = Format-SystemDate (Get-DataProperty $adapter 'LeaseExpires') 'dd.MM.yyyy HH:mm' if ($until) { $dhcp = (Get-Text 'System.Info.DhcpLease') -f $dhcp, $until } } & $add $net (Get-Text 'System.Info.Dhcp') $dhcp & $add $net (Get-Text 'System.Info.Mac') (Get-DataProperty $adapter 'MacAddress') } $driver = Get-Text 'System.Info.Group.Drivers' foreach ($adapter in (Get-DataCollection $Data 'Graphics')) { $version = "$(Get-DataProperty $adapter 'DriverVersion')".Trim() if (-not $version) { continue } $date = Format-SystemDate (Get-DataProperty $adapter 'DriverDate') if ($date) { $version = (Get-Text 'System.Info.VersionOf') -f $version, $date } & $add $driver (Get-Text 'System.Info.GraphicsDriver') ('{0}: {1}' -f "$(Get-DataProperty $adapter 'Name')".Trim(), $version) } $drivers = Get-DataCollection $Data 'Drivers' if ($drivers.Count) { # Named by its title, in quotation marks: the Report makes a link of that. & $add $driver (Get-Text 'System.Info.OtherDrivers') ((Get-Text 'System.Info.SeeDrivers') -f $drivers.Count, (Get-Text 'Title.System.Drivers')) } $rows } function ConvertTo-SystemSection { [CmdletBinding()] [OutputType([psobject])] param([AllowNull()]$Data, [AllowNull()]$Situation) # What the Hints of this Report assumed about the machine stands before what the # machine is: it is what a reader has to know to read the rest. New-Section -Title (Get-Text 'Title.System.Information') -Placement Top -Row @( Get-SituationRow -Situation $Situation Get-SystemInformationRow -Data $Data) # Everything ipconfig /all says, as a table: the box shows the same, and a table can # be copied into a ticket. New-Section -Title (Get-Text 'Title.System.IpConfiguration') -Row @( foreach ($adapter in (Get-DataCollection $Data 'NetworkAdapters')) { $row = [ordered]@{} $row[(Get-Text 'Column.System.Adapter')] = Get-DataProperty $adapter 'Alias' $row[(Get-Text 'Column.System.Description')] = Get-DataProperty $adapter 'Description' $row[(Get-Text 'Column.System.Speed')] = Format-SystemLinkSpeed (Get-DataProperty $adapter 'SpeedBps') $row[(Get-Text 'Column.System.MacAddress')] = Get-DataProperty $adapter 'MacAddress' $row[(Get-Text 'Column.System.IPv4')] = @(Get-SystemNetworkAddress -Adapter $adapter -Family IPv4) -join ', ' $row[(Get-Text 'Column.System.IPv6')] = @(Get-SystemNetworkAddress -Adapter $adapter -Family IPv6) -join ', ' $row[(Get-Text 'Column.System.Gateway')] = (Get-DataCollection $adapter 'Gateways') -join ', ' $row[(Get-Text 'Column.System.DnsServers')] = (Get-DataCollection $adapter 'DnsServers') -join ', ' $row[(Get-Text 'Column.System.DnsSuffix')] = Get-DataProperty $adapter 'DnsSuffix' $row[(Get-Text 'Column.System.Dhcp')] = Get-TriStateText ([bool](Get-DataProperty $adapter 'DhcpEnabled')) $row[(Get-Text 'Column.System.DhcpServer')] = Get-DataProperty $adapter 'DhcpServer' $row[(Get-Text 'Column.System.LeaseObtained')] = Format-SystemDate (Get-DataProperty $adapter 'LeaseObtained') 'yyyy-MM-dd HH:mm' $row[(Get-Text 'Column.System.LeaseExpires')] = Format-SystemDate (Get-DataProperty $adapter 'LeaseExpires') 'yyyy-MM-dd HH:mm' [pscustomobject]$row } ) # By class and then by device, so that the network adapters stand together. New-Section -Title (Get-Text 'Title.System.Drivers') -Row @( (Get-DataCollection $Data 'Drivers') | Sort-Object { "$(Get-DataProperty $_ 'Class')" }, { "$(Get-DataProperty $_ 'Device')" } | ForEach-Object { $row = [ordered]@{} # What Device Manager calls the class, and the name Windows keeps it under. $row[(Get-Text 'Column.System.DriverClass')] = Get-SystemDriverClassText -Class (Get-DataProperty $_ 'Class') $row[(Get-Text 'Column.System.DriverDevice')] = Get-DataProperty $_ 'Device' $row[(Get-Text 'Column.System.DriverProvider')] = Get-DataProperty $_ 'Provider' $row[(Get-Text 'Column.System.DriverVersion')] = Get-DataProperty $_ 'Version' $row[(Get-Text 'Column.System.DriverDate')] = Format-SystemDate (Get-DataProperty $_ 'Date') 'yyyy-MM-dd' [pscustomobject]$row } ) } function Get-SystemDriverClassText { <# .SYNOPSIS A device setup class in the words of Device Manager, with the name Windows keeps it under behind it: "Grafikkarten (DISPLAY)". Pure. .DESCRIPTION The names are those of Microsoft's system-defined device setup classes. A class that is not among them - a vendor's own - is shown as it is. #> [CmdletBinding()] [OutputType([string])] param([AllowNull()][AllowEmptyString()][string]$Class) $name = switch ("$Class".Trim().ToUpperInvariant()) { 'AUDIOENDPOINT' { Get-Text 'Value.System.DriverClass.AudioEndpoint' } 'BATTERY' { Get-Text 'Value.System.DriverClass.Battery' } 'BIOMETRIC' { Get-Text 'Value.System.DriverClass.Biometric' } 'BLUETOOTH' { Get-Text 'Value.System.DriverClass.Bluetooth' } 'CAMERA' { Get-Text 'Value.System.DriverClass.Camera' } 'DISKDRIVE' { Get-Text 'Value.System.DriverClass.DiskDrive' } 'DISPLAY' { Get-Text 'Value.System.DriverClass.Display' } 'EXTENSION' { Get-Text 'Value.System.DriverClass.Extension' } 'FIRMWARE' { Get-Text 'Value.System.DriverClass.Firmware' } 'HDC' { Get-Text 'Value.System.DriverClass.Hdc' } 'HIDCLASS' { Get-Text 'Value.System.DriverClass.Hid' } 'IMAGE' { Get-Text 'Value.System.DriverClass.Image' } 'KEYBOARD' { Get-Text 'Value.System.DriverClass.Keyboard' } 'MEDIA' { Get-Text 'Value.System.DriverClass.Media' } 'MONITOR' { Get-Text 'Value.System.DriverClass.Monitor' } 'MOUSE' { Get-Text 'Value.System.DriverClass.Mouse' } 'NET' { Get-Text 'Value.System.DriverClass.Net' } 'PORTS' { Get-Text 'Value.System.DriverClass.Ports' } 'PRINTER' { Get-Text 'Value.System.DriverClass.Printer' } 'PRINTQUEUE' { Get-Text 'Value.System.DriverClass.PrintQueue' } 'PROCESSOR' { Get-Text 'Value.System.DriverClass.Processor' } 'SCSIADAPTER' { Get-Text 'Value.System.DriverClass.ScsiAdapter' } 'SECURITYDEVICES' { Get-Text 'Value.System.DriverClass.SecurityDevices' } 'SMARTCARDREADER' { Get-Text 'Value.System.DriverClass.SmartCardReader' } 'SOFTWARECOMPONENT' { Get-Text 'Value.System.DriverClass.SoftwareComponent' } 'SOFTWAREDEVICE' { Get-Text 'Value.System.DriverClass.SoftwareDevice' } 'SYSTEM' { Get-Text 'Value.System.DriverClass.System' } 'USB' { Get-Text 'Value.System.DriverClass.Usb' } } if (-not $name) { return "$Class" } (Get-Text 'Value.System.DriverClass') -f $name, $Class } function ConvertTo-SystemFinding { [CmdletBinding()] [OutputType([psobject])] param( [AllowNull()]$Data, [hashtable]$Parameters = @{} ) New-ModelFinding -Data $Data -Parameters $Parameters New-OperatingSystemFinding -Data $Data -Parameters $Parameters New-BiosFinding -Data $Data -Parameters $Parameters New-ProcessorFinding -Data $Data -Parameters $Parameters New-RamInstalledFinding -Data $Data -Parameters $Parameters New-RamModuleFinding -Data $Data -Parameters $Parameters New-UptimeFinding -Data $Data -Parameters $Parameters New-GraphicsDriverFinding -Data $Data -Parameters $Parameters } function New-GraphicsDriverFinding { <# .SYNOPSIS The graphics driver and its date, one Finding for each adapter. Pure. .DESCRIPTION INFO whatever its age: an old driver that works is no warning. What its age is for is to be read with what else was found - a display that hangs, a blue screen of the display driver - and that reading is a Lead's. An adapter that reports no date gets no Finding: nothing can be said of its age. Three years is this module's own rule of thumb, like the two years of the BIOS, and a Check Definition can say otherwise. #> [CmdletBinding()] param([AllowNull()]$Data, [hashtable]$Parameters) $noteDays = ConvertTo-Number (Get-Parameter $Parameters 'GraphicsDriverAgeNoteDays' 1095) $asOf = Get-DataProperty $Data 'GatheredAt' foreach ($adapter in (Get-DataCollection $Data 'Graphics')) { $date = (Get-DataProperty $adapter 'DriverDate') -as [datetime] if (-not $date -or -not $asOf) { continue } # A display adapter that is software - a remote desktop session's, a virtual # display's - is no graphics card and has no driver anybody could update. if ("$(Get-DataProperty $adapter 'PnpDeviceId')" -match '(?i)^(SWD|ROOT)\\') { continue } # Every driver that ships with Windows is dated 21 June 2006, whenever it was # written: Microsoft does that so that a manufacturer's driver always counts as # newer (Raymond Chen, The Old New Thing, "Why are all Windows drivers dated # June 21, 2006?", 2017). That date says nothing about the driver's age. $inbox = $date.Year -eq 2006 -and $date.Month -eq 6 -and $date.Day -in 20, 21, 22 $old = -not $inbox -and ([datetime]$asOf - $date).TotalDays -gt $noteDays $hint = '' $signal = @() if ($old) { $hint = (Get-Text 'Hint.System.GraphicsDriverOld') -f [math]::Round($noteDays / 365) $signal = @('graphics-driver-old') } New-Finding -Category System -Check (Get-Text 'Check.System.GraphicsDriver') -Severity INFO ` -Value ((Get-Text 'Value.System.GraphicsDriver') -f "$(Get-DataProperty $adapter 'Name')".Trim(), "$(Get-DataProperty $adapter 'DriverVersion')".Trim(), $date) ` -Hint $hint -Signal $signal -Subject "$(Get-DataProperty $adapter 'Name')".Trim() } } function New-ModelFinding { [CmdletBinding()] param([AllowNull()]$Data, [hashtable]$Parameters) $manufacturer = Get-DataProperty $Data 'Manufacturer' $model = Get-DataProperty $Data 'Model' if (-not $manufacturer -and -not $model) { return New-UnavailableFinding -Category System -Check (Get-Text 'Check.System.Model') ` -Hint (Get-Text 'Hint.System.TheMachineDidNotReport4') } New-Finding -Category System -Check (Get-Text 'Check.System.Model') -Severity INFO ` -Value ('{0} {1} (S/N {2})' -f $manufacturer, $model, (Get-DataProperty $Data 'SerialNumber')) } function New-OperatingSystemFinding { [CmdletBinding()] param([AllowNull()]$Data, [hashtable]$Parameters) $caption = Get-DataProperty $Data 'OsCaption' if (-not $caption) { return New-UnavailableFinding -Category System -Check (Get-Text 'Check.System.OS') ` -Hint (Get-Text 'Hint.System.TheOperatingSystemDidNot') } New-Finding -Category System -Check (Get-Text 'Check.System.OS') -Severity INFO -Value ((Get-Text 'Value.System.OperatingSystem') -f $caption, (Get-DataProperty $Data 'OsDisplayVersion'), (Get-DataProperty $Data 'OsBuildNumber'), (Get-DataProperty $Data 'OsUpdateBuildRevision')) } function New-BiosFinding { [CmdletBinding()] param([AllowNull()]$Data, [hashtable]$Parameters) $warnDays = Get-Parameter $Parameters 'BiosAgeWarnDays' 730 $failDays = Get-Parameter $Parameters 'BiosAgeFailDays' ([double]::MaxValue) $released = Get-DataProperty $Data 'BiosReleaseDate' if (-not $released) { return New-UnavailableFinding -Category System -Check (Get-Text 'Check.System.BIOS') ` -Hint (Get-Text 'Hint.System.TheMachineDidNotReport') } $ageDays = ((Get-DataProperty $Data 'GatheredAt') - [datetime]$released).TotalDays New-Finding -Category System -Check (Get-Text 'Check.System.BIOS') -Severity (Get-Severity $ageDays $warnDays $failDays) ` -Value ('{0} ({1:yyyy-MM-dd})' -f (Get-DataProperty $Data 'BiosVersion'), [datetime]$released) ` -Hint (Get-Text 'Hint.System.BIOSOlderThan2Years') } function New-ProcessorFinding { [CmdletBinding()] param([AllowNull()]$Data, [hashtable]$Parameters) $name = Get-DataProperty $Data 'ProcessorName' if (-not $name) { return New-UnavailableFinding -Category System -Check (Get-Text 'Check.System.CPU') ` -Hint (Get-Text 'Hint.System.TheMachineDidNotReport5') } New-Finding -Category System -Check (Get-Text 'Check.System.CPU') -Severity INFO -Value ((Get-Text 'Value.System.Processor') -f ([string]$name).Trim(), (Get-DataProperty $Data 'ProcessorCores'), (Get-DataProperty $Data 'ProcessorLogicalCount')) } function New-RamInstalledFinding { [CmdletBinding()] param([AllowNull()]$Data, [hashtable]$Parameters) # Graded downwards: here it is the small number that is the problem. $warnBelow = Get-Parameter $Parameters 'RamInstalledWarnBelowGB' 15 $failBelow = Get-Parameter $Parameters 'RamInstalledFailBelowGB' 7.5 $bytes = ConvertTo-Number (Get-DataProperty $Data 'TotalPhysicalMemoryBytes') if ($null -eq $bytes -or $bytes -le 0) { return New-UnavailableFinding -Category System -Check (Get-Text 'Check.System.RAMInstalled') ` -Hint (Get-Text 'Hint.System.TheMachineDidNotReport2') } $gb = [math]::Round($bytes / 1GB, 1) New-Finding -Category System -Check (Get-Text 'Check.System.RAMInstalled') -Severity (Get-SeverityBelow $gb $warnBelow $failBelow) ` -Value "$gb GB" ` -Hint (Get-Text 'Hint.System.OfficeTeamsBrowserBusinessApps') } function New-RamModuleFinding { [CmdletBinding()] param([AllowNull()]$Data, [hashtable]$Parameters) $modules = (Get-DataCollection $Data 'MemoryModules') if (-not $modules.Count) { return New-UnavailableFinding -Category System -Check (Get-Text 'Check.System.RAMModules') ` -Hint (Get-Text 'Hint.System.TheMachineDidNotReport3') } # A single module means single-channel, which is a real performance finding, but the # script reported it as INFO with a Hint rather than a WARN. Carried across unchanged. $hint = '' if ($modules.Count -eq 1) { $hint = (Get-Text 'Hint.System.SingleChannelMemory') } # Not a bare division: gathered data also reaches a Judge through a CliXML round trip, # where a capacity can arrive as text and dividing it would throw rather than report. $described = $modules | ForEach-Object { $capacity = ConvertTo-Number $_.CapacityBytes if ($null -eq $capacity) { '? GB @ {0} MHz' -f $_.ConfiguredClockSpeed } else { '{0} GB @ {1} MHz' -f ($capacity / 1GB), $_.ConfiguredClockSpeed } } New-Finding -Category System -Check (Get-Text 'Check.System.RAMModules') -Severity INFO -Value ($described -join ', ') -Hint $hint } function New-UptimeFinding { [CmdletBinding()] param([AllowNull()]$Data, [hashtable]$Parameters) $warnDays = Get-Parameter $Parameters 'UptimeWarnDays' 7 $failDays = Get-Parameter $Parameters 'UptimeFailDays' ([double]::MaxValue) $booted = Get-DataProperty $Data 'LastBootUpTime' if (-not $booted) { return New-UnavailableFinding -Category System -Check (Get-Text 'Check.System.Uptime') ` -Hint (Get-Text 'Hint.System.TheOperatingSystemDidNot2') } $days = ((Get-DataProperty $Data 'GatheredAt') - [datetime]$booted).TotalDays $fastStartup = Get-Text 'Value.System.FastStartupOff' if ((ConvertTo-Number (Get-DataProperty $Data 'FastStartupEnabled')) -eq 1) { $fastStartup = Get-Text 'Value.System.FastStartupOn' } New-Finding -Category System -Check (Get-Text 'Check.System.Uptime') -Severity (Get-Severity $days $warnDays $failDays) ` -Value ((Get-Text 'Value.System.Uptime') -f $days, $fastStartup) ` -Hint (Get-Text 'Hint.System.ShutDownWithFastStartup') } |