Public/Get-EFFleetSummary.ps1
|
function Get-EFFleetSummary { <# .SYNOPSIS Checks several Windows computers without changing them. .DESCRIPTION Runs the same EndpointForge computer checkup on each named computer and returns one combined result. This command is read-only: it never installs EndpointForge, turns on remote management, changes a Windows setting, or runs a fix. Each remote computer must already allow PowerShell remoting and must already have EndpointForge 0.4.0 or later installed. Your account must have permission to connect. These requirements are intentionally not changed for you. EndpointForge calls its list of expected Windows settings a baseline in scripts. In the menu and documentation, that is described simply as a checklist. .PARAMETER ComputerName One or more computer names to check. Duplicate names are checked once. .PARAMETER Baseline The checklist to use. Supply the built-in checklist name, a checklist JSON file, or a checklist object. The default is EnterpriseRecommended. .PARAMETER Credential An optional account that already has permission to connect. The credential is used only for the remote connection and is not included in the returned report. .PARAMETER ThrottleLimit The largest number of remote checks that may run at the same time. .PARAMETER IncludeSoftware Also collects installed-software details. This makes the report larger and can take longer. It still does not change a computer. .PARAMETER MinimumFreeSpacePercent The system-drive free-space level that should produce a warning. .PARAMETER MaximumUptimeDays The number of days running without a restart that should produce a warning. .EXAMPLE Get-EFFleetSummary -ComputerName PC-101,PC-102 Checks two computers with the built-in checklist and returns one combined result. .EXAMPLE Get-EFFleetSummary -ComputerName (Get-Content .\computers.txt) -Credential (Get-Credential) Checks names from a text file using an account that already has remote access. .OUTPUTS EndpointForge.FleetSummary .LINK Get-EFEndpointSummary #> [CmdletBinding()] param( [Parameter(Mandatory, Position = 0)] [ValidateNotNullOrEmpty()] [string[]]$ComputerName, [Parameter(Position = 1)] [AllowNull()] [object]$Baseline = 'EnterpriseRecommended', [pscredential]$Credential, [ValidateRange(1, 128)] [int]$ThrottleLimit = 16, [switch]$IncludeSoftware, [ValidateRange(1, 99)] [int]$MinimumFreeSpacePercent = 15, [ValidateRange(1, 3650)] [int]$MaximumUptimeDays = 30 ) $targets = [Collections.Generic.List[string]]::new() $seenTargets = [Collections.Generic.HashSet[string]]::new([StringComparer]::OrdinalIgnoreCase) foreach ($name in $ComputerName) { $trimmedName = [string]$name if ($null -ne $trimmedName) { $trimmedName = $trimmedName.Trim() } if ([string]::IsNullOrWhiteSpace($trimmedName)) { throw [System.ArgumentException]::new('ComputerName cannot contain an empty value.') } if ($seenTargets.Add($trimmedName)) { $targets.Add($trimmedName) } } if ($targets.Count -eq 0) { throw [System.ArgumentException]::new('Provide at least one computer name.') } $resolvedBaseline = Resolve-EFBaseline -Baseline $Baseline $startedAtUtc = [DateTime]::UtcNow $remoteScript = { param($Checklist, $CollectSoftware, $FreeSpaceThreshold, $UptimeThreshold) Import-Module EndpointForge -MinimumVersion 0.4.0 -Force -ErrorAction Stop $parameters = @{ Baseline = $Checklist IncludeSoftware = [bool]$CollectSoftware MinimumFreeSpacePercent = [int]$FreeSpaceThreshold MaximumUptimeDays = [int]$UptimeThreshold NoProgress = $true } $checkup = Get-EFEndpointSummary @parameters [pscustomobject]@{ RemoteComputerName = $env:COMPUTERNAME Checkup = $checkup } } $invokeParameters = @{ ComputerName = $targets.ToArray() ScriptBlock = $remoteScript ArgumentList = @($resolvedBaseline, [bool]$IncludeSoftware, $MinimumFreeSpacePercent, $MaximumUptimeDays) ThrottleLimit = $ThrottleLimit ErrorAction = 'SilentlyContinue' ErrorVariable = 'fleetRemoteErrors' } if ($PSBoundParameters.ContainsKey('Credential')) { $invokeParameters.Credential = $Credential } $fleetRemoteErrors = @() $responses = @() try { $responses = @(Invoke-Command @invokeParameters) } catch { $fleetRemoteErrors += $_ } $resultList = [Collections.Generic.List[object]]::new() $completedTargets = [Collections.Generic.HashSet[string]]::new([StringComparer]::OrdinalIgnoreCase) foreach ($response in $responses) { if ($null -eq $response) { continue } $remoteTarget = [string](Get-EFPropertyValue -InputObject $response -Name 'PSComputerName' -Default '') if ([string]::IsNullOrWhiteSpace($remoteTarget)) { $remoteTarget = [string](Get-EFPropertyValue -InputObject $response -Name 'RemoteComputerName' -Default '') } $checkup = Get-EFPropertyValue -InputObject $response -Name 'Checkup' if ($null -eq $checkup) { continue } $requestedTarget = @($targets | Where-Object { $_ -ieq $remoteTarget } | Select-Object -First 1) if ($requestedTarget.Count -eq 0) { $requestedTarget = @($remoteTarget) } $requestedName = [string]$requestedTarget[0] if (-not [string]::IsNullOrWhiteSpace($requestedName)) { $null = $completedTargets.Add($requestedName) } $resultList.Add([pscustomobject]@{ PSTypeName = 'EndpointForge.FleetComputerResult' RequestedComputerName = $requestedName ComputerName = [string](Get-EFPropertyValue -InputObject $checkup -Name 'ComputerName' -Default $remoteTarget) OverallStatus = [string](Get-EFPropertyValue -InputObject $checkup -Name 'OverallStatus' -Default 'Incomplete') Score = Get-EFPropertyValue -InputObject $checkup -Name 'Score' IssueCount = [int](Get-EFPropertyValue -InputObject $checkup -Name 'IssueCount' -Default 0) UnknownCount = [int](Get-EFPropertyValue -InputObject $checkup -Name 'UnknownCount' -Default 0) CompletedAtUtc = Get-EFPropertyValue -InputObject $checkup -Name 'CompletedAtUtc' NextStep = [string](Get-EFPropertyValue -InputObject $checkup -Name 'NextStep' -Default '') Checkup = $checkup }) } $failureList = [Collections.Generic.List[object]]::new() foreach ($target in $targets) { if ($completedTargets.Contains($target)) { continue } $matchingErrors = @($fleetRemoteErrors | Where-Object { $errorTarget = '' if ($null -ne $_.OriginInfo) { $errorTarget = [string]$_.OriginInfo.PSComputerName } if ([string]::IsNullOrWhiteSpace($errorTarget)) { $errorTarget = [string]$_.TargetObject } $errorTarget -ieq $target -or [string]$_.Exception.Message -match [regex]::Escape($target) }) $message = if ($matchingErrors.Count -gt 0) { [string]$matchingErrors[0].Exception.Message } else { 'The computer did not return a checkup. Confirm its name, network access, PowerShell remoting permission, and that EndpointForge 0.4.0 or later is already installed there.' } $failureList.Add([pscustomobject]@{ PSTypeName = 'EndpointForge.FleetFailure' ComputerName = [string]$target Message = $message }) } $healthyCount = @($resultList | Where-Object OverallStatus -eq 'Healthy').Count $warningCount = @($resultList | Where-Object OverallStatus -eq 'Warning').Count $criticalCount = @($resultList | Where-Object OverallStatus -eq 'Critical').Count $incompleteCount = @($resultList | Where-Object OverallStatus -eq 'Incomplete').Count $failedCount = $failureList.Count $exitCode = if ($failedCount -gt 0 -or $incompleteCount -gt 0) { 3 } elseif ($criticalCount -gt 0) { 2 } elseif ($warningCount -gt 0) { 1 } else { 0 } $summary = if ($failedCount -gt 0) { "$($resultList.Count) of $($targets.Count) computer(s) were checked; $failedCount could not be checked." } elseif ($criticalCount -gt 0) { "All $($targets.Count) computer(s) were checked; $criticalCount need urgent attention." } elseif ($warningCount -gt 0 -or $incompleteCount -gt 0) { "All $($targets.Count) computer(s) were checked; some results need attention or were incomplete." } else { "All $($targets.Count) computer(s) were checked and look good." } $nextStep = if ($failedCount -gt 0) { 'Review Failures. EndpointForge will not turn on remote access or install itself on another computer.' } elseif ($criticalCount -gt 0 -or $warningCount -gt 0) { 'Review Results, then check an affected computer directly before approving any fixes.' } elseif ($incompleteCount -gt 0) { 'Review incomplete results and permissions, then run the check again.' } else { 'No action is required.' } [pscustomobject]@{ PSTypeName = 'EndpointForge.FleetSummary' SchemaVersion = '1.0' ChecklistName = [string]$resolvedBaseline.Name BaselineName = [string]$resolvedBaseline.Name BaselineVersion = [string]$resolvedBaseline.Version StartedAtUtc = $startedAtUtc CompletedAtUtc = [DateTime]::UtcNow TargetCount = $targets.Count SucceededCount = $resultList.Count FailedCount = $failedCount HealthyCount = $healthyCount WarningCount = $warningCount CriticalCount = $criticalCount IncompleteCount = $incompleteCount IsComplete = $failedCount -eq 0 -and $incompleteCount -eq 0 ExitCode = $exitCode Summary = $summary NextStep = $nextStep Results = $resultList.ToArray() Failures = $failureList.ToArray() } } |