Private/Finding.ps1
|
# The Finding primitive. # # A Finding is one judged observation about the Target Machine. Creating one returns a # value: it records nothing and prints nothing. Collecting Findings, ordering them and # showing them is the caller's job. # The four permitted Severity values, worst first. The Report orders by this array. $script:SeverityValues = @('FAIL', 'WARN', 'INFO', 'OK') # The areas a Finding can belong to. Closed, like Severity and Privilege, because the # Report groups by Category: a misspelling in one Kind would otherwise add a group rather # than fail, and a Technician would read two Storage sections and trust both. Carried over # from the Gutcheck script, except that its 'Script' is 'Gutcheck' here - Findings about # the Run itself outlive the script #16 deletes. A new Category needs a module release, # which is the rule a new Kind already follows. $script:CategoryValues = @( 'System', 'Power', 'CPU', 'Memory', 'Storage', 'Network', 'Stability', 'Security', 'Updates', 'Startup', 'Integrity', 'Apps', 'Access', 'Gutcheck' ) function New-Finding { [CmdletBinding()] [OutputType([psobject])] param( # The Check this Finding came from, named as the Technician recognises it. [Parameter(Mandatory)][string]$Check, # The area of the machine the Finding belongs to; the Report groups by it. [Parameter(Mandatory)][string]$Category, # What the Check observed. Rendered to a string here so no later stage has to. [AllowNull()][AllowEmptyString()]$Value, [ValidateSet('OK', 'INFO', 'WARN', 'FAIL')][string]$Severity = 'INFO', # What a first-level Technician should do about it. Absent on OK Findings. [AllowEmptyString()][string]$Hint = '', # The rights the data was gathered with. A Judge cannot know this, so it defaults # to the weaker of the two and the Part stamps the truth on with Set-FindingPrivilege. [ValidateSet('user', 'admin')][string]$Privilege = 'user' ) $severityValue = $Severity.ToUpperInvariant() # Validated here rather than with ValidateSet so the permitted values live in one # place, and so the error can say what was wrong and what was allowed. $categoryValue = $script:CategoryValues | Where-Object { $_ -eq $Category } | Select-Object -First 1 if (-not $categoryValue) { throw ("'{0}' is not a Gutcheck Category. Permitted: {1}." -f $Category, ($script:CategoryValues -join ', ')) } # A Hint tells a Technician what to do about a Finding, so an OK Finding has none. # Dropping it here lets a Judge decide Severity and Hint together and pass both # unconditionally, which is how the thresholds read most plainly. $hintValue = $Hint if ($severityValue -eq 'OK') { $hintValue = '' } [pscustomobject]@{ PSTypeName = 'Gutcheck.Finding' Severity = $severityValue Category = $categoryValue Check = $Check Value = "$Value" Hint = $hintValue Privilege = $Privilege.ToLowerInvariant() } } function New-UnavailableFinding { <# .SYNOPSIS The Finding a Judge returns when the data it needed is not there. .DESCRIPTION A counter that would not load, a device that would not answer, an empty collection: a real machine produces all three, and none of them is a clean result. Every Judge says so the same way, so a Technician learns one phrase rather than one per Check. #> [CmdletBinding()] [OutputType([psobject])] param( [Parameter(Mandatory)][string]$Check, [Parameter(Mandatory)][string]$Category, [AllowEmptyString()][string]$Hint = '' ) New-Finding -Category $Category -Check $Check -Value (Get-Text 'Value.Shared.NotAvailable') -Severity INFO -Hint $Hint } function Set-FindingPrivilege { <# .SYNOPSIS Stamps the Privilege a Part gathered with onto the Findings it produced. .DESCRIPTION Judges are pure and cannot know what rights their data was gathered with, so a Part stamps its own Privilege onto everything it collected. This is the single place Privilege is assigned, on both the Main and the Elevated Part. #> [CmdletBinding()] [OutputType([psobject])] param( [Parameter(Mandatory, ValueFromPipeline)]$Finding, [Parameter(Mandatory)][ValidateSet('user', 'admin')][string]$Privilege ) process { $Finding.Privilege = $Privilege.ToLowerInvariant() $Finding } } function Get-CurrentPrivilege { <# .SYNOPSIS The Privilege this process gathers data with. .DESCRIPTION Not the same question as which Part is running: a Main Part that the Technician started elevated carries admin Privilege. #> [CmdletBinding()] [OutputType([string])] param() $identity = [Security.Principal.WindowsIdentity]::GetCurrent() if (([Security.Principal.WindowsPrincipal]$identity).IsInRole( [Security.Principal.WindowsBuiltInRole]::Administrator)) { return 'admin' } 'user' } |