Add local Windows Server inventory collection and validation; include core package
This commit is contained in:
17
powershell/Get-ServerCertificates.ps1
Normal file
17
powershell/Get-ServerCertificates.ps1
Normal file
@@ -0,0 +1,17 @@
|
||||
function Get-SamsaraCertificates {
|
||||
param($Result)
|
||||
$collection = @{ stores = @() }
|
||||
Invoke-SamsaraRead $Result 'Local machine certificate store list' {
|
||||
$collection.stores = @(Get-ChildItem -LiteralPath 'Cert:\LocalMachine' -ErrorAction Stop)
|
||||
}
|
||||
foreach ($store in $collection.stores) {
|
||||
Invoke-SamsaraRead $Result 'Local machine public certificate metadata' {
|
||||
Get-ChildItem -LiteralPath $store.PSPath -ErrorAction Stop | ForEach-Object {
|
||||
$Result.data += [pscustomobject]@{ subject = [string]$_.Subject; issuer = [string]$_.Issuer
|
||||
expires = $_.NotAfter.ToUniversalTime().ToString('o'); thumbprint = [string]$_.Thumbprint
|
||||
store = [string]$store.PSChildName }
|
||||
}
|
||||
}
|
||||
}
|
||||
$Result.limitations += 'Public LocalMachine certificate metadata only. Private keys, password-protected files, CurrentUser stores, application bindings and off-store certificates are not read.'
|
||||
}
|
||||
56
powershell/Get-ServerInventory.ps1
Normal file
56
powershell/Get-ServerInventory.ps1
Normal file
@@ -0,0 +1,56 @@
|
||||
#requires -Version 5.1
|
||||
[CmdletBinding()]
|
||||
param([ValidateSet('all','system','roles','services','network','tasks','software','storage','shares','certificates')][string]$Section = 'all')
|
||||
|
||||
$ErrorActionPreference = 'Stop'
|
||||
$ProgressPreference = 'SilentlyContinue'
|
||||
$WarningPreference = 'SilentlyContinue'
|
||||
$InformationPreference = 'SilentlyContinue'
|
||||
$env:PSModulePath = Join-Path $PSHOME 'Modules'
|
||||
[Console]::OutputEncoding = New-Object System.Text.UTF8Encoding($false)
|
||||
if ([Environment]::OSVersion.Platform -ne [PlatformID]::Win32NT) {
|
||||
[Console]::Error.WriteLine('This collector requires a local Windows Server execution host.')
|
||||
exit 2
|
||||
}
|
||||
. (Join-Path $PSScriptRoot 'Inventory.Common.ps1')
|
||||
foreach ($file in @('System','Roles','Services','Network','Tasks','Software','Storage','Certificates')) {
|
||||
. (Join-Path $PSScriptRoot ('Get-Server' + $file + '.ps1'))
|
||||
}
|
||||
$identity = [Security.Principal.WindowsIdentity]::GetCurrent()
|
||||
try {
|
||||
$principal = New-Object Security.Principal.WindowsPrincipal($identity)
|
||||
$script:SamsaraIsElevated = $principal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
|
||||
} finally { $identity.Dispose() }
|
||||
$collectors = [ordered]@{
|
||||
system = { param($r) Get-SamsaraSystem $r }; roles = { param($r) Get-SamsaraRoles $r }
|
||||
services = { param($r) Get-SamsaraServices $r }; network = { param($r) Get-SamsaraNetwork $r }
|
||||
tasks = { param($r) Get-SamsaraTasks $r }; software = { param($r) Get-SamsaraSoftware $r }
|
||||
storage = { param($r) Get-SamsaraStorage $r }; shares = { param($r) Get-SamsaraShares $r }
|
||||
certificates = { param($r) Get-SamsaraCertificates $r }
|
||||
}
|
||||
if ($Section -ne 'all') {
|
||||
$result = Invoke-SamsaraSection $Section $collectors[$Section]
|
||||
ConvertTo-Json -InputObject $result -Depth 12 -Compress
|
||||
exit 0
|
||||
}
|
||||
# Direct PowerShell use emits the same complete inventory envelope as the Python backend.
|
||||
$inventory = [ordered]@{ roles=@(); services=@(); network=@(); tasks=@(); software=@(); storage=@(); certificates=@() }
|
||||
$checks = [ordered]@{}
|
||||
$collectedAt = Get-SamsaraTimestamp
|
||||
$server = $null
|
||||
foreach ($name in $collectors.Keys) {
|
||||
$result = Invoke-SamsaraSection $name $collectors[$name]
|
||||
$checks[$name] = ConvertTo-SamsaraCheck $result
|
||||
if ($name -eq 'system') {
|
||||
$server = $result.data
|
||||
if ([int]$server.product_type -notin @(2,3) -or $server.os_version -notin @('2016','2019','2022','2025')) {
|
||||
[Console]::Error.WriteLine('Unable to verify a supported Windows Server identity. No inventory emitted.')
|
||||
exit 2
|
||||
}
|
||||
} elseif ($name -eq 'shares') { $inventory.storage += @($result.data) }
|
||||
else { $inventory[$name] = @($result.data) }
|
||||
}
|
||||
ConvertTo-Json -InputObject ([ordered]@{ schema_version='1.0'; synthetic=$false; collected_at=$collectedAt
|
||||
server=$server; checks=$checks; inventory=$inventory }) -Depth 14 -Compress
|
||||
|
||||
exit 0
|
||||
16
powershell/Get-ServerNetwork.ps1
Normal file
16
powershell/Get-ServerNetwork.ps1
Normal file
@@ -0,0 +1,16 @@
|
||||
function Get-SamsaraNetwork {
|
||||
param($Result)
|
||||
$processes = @{}
|
||||
Invoke-SamsaraRead $Result 'Process names' {
|
||||
Get-Process -ErrorAction Stop | ForEach-Object { $processes[[int]$_.Id] = [string]$_.ProcessName }
|
||||
}
|
||||
Invoke-SamsaraRead $Result 'TCP connections' {
|
||||
$Result.data = @(Get-NetTCPConnection -ErrorAction Stop | ForEach-Object {
|
||||
$processName = if ($processes.ContainsKey([int]$_.OwningProcess)) { $processes[[int]$_.OwningProcess] } else { 'Unavailable or process exited' }
|
||||
[pscustomobject]@{ state = [string]$_.State; local_address = [string]$_.LocalAddress; local_port = [int]$_.LocalPort
|
||||
remote_address = [string]$_.RemoteAddress; remote_port = [int]$_.RemotePort
|
||||
pid = [int]$_.OwningProcess; process = $processName; direction = 'Unknown' }
|
||||
})
|
||||
}
|
||||
$Result.limitations += 'Point-in-time TCP only. Connection direction, historical activity, UDP and other network compartments are unverified. Process names may race process exit or be hidden.'
|
||||
}
|
||||
10
powershell/Get-ServerRoles.ps1
Normal file
10
powershell/Get-ServerRoles.ps1
Normal file
@@ -0,0 +1,10 @@
|
||||
function Get-SamsaraRoles {
|
||||
param($Result)
|
||||
Invoke-SamsaraRead $Result 'Server roles and features' {
|
||||
$Result.data = @(Get-WindowsFeature -ErrorAction Stop | Where-Object { $_.Installed } | ForEach-Object {
|
||||
[pscustomobject]@{ name = [string]$_.Name; display_name = [string]$_.DisplayName
|
||||
state = [string]$_.InstallState; feature_type = [string]$_.FeatureType }
|
||||
})
|
||||
}
|
||||
$Result.limitations += 'Installed roles/features only; installed state does not prove current use. Requires local ServerManager availability.'
|
||||
}
|
||||
11
powershell/Get-ServerServices.ps1
Normal file
11
powershell/Get-ServerServices.ps1
Normal file
@@ -0,0 +1,11 @@
|
||||
function Get-SamsaraServices {
|
||||
param($Result)
|
||||
Invoke-SamsaraRead $Result 'Windows services' {
|
||||
# Do not query PathName: it can include secret-bearing command arguments.
|
||||
$Result.data = @(Get-CimInstance -ClassName Win32_Service -Property Name,DisplayName,State,StartMode,StartName,ProcessId -ErrorAction Stop | ForEach-Object {
|
||||
[pscustomobject]@{ name = [string]$_.Name; display_name = [string]$_.DisplayName
|
||||
state = [string]$_.State; startup = [string]$_.StartMode; account = [string]$_.StartName; pid = [int]$_.ProcessId }
|
||||
})
|
||||
}
|
||||
$Result.limitations += 'All visible service states are inventoried, including running services. Executable command lines and credentials are not collected; service accounts do not prove consumers.'
|
||||
}
|
||||
37
powershell/Get-ServerSoftware.ps1
Normal file
37
powershell/Get-ServerSoftware.ps1
Normal file
@@ -0,0 +1,37 @@
|
||||
function Read-SamsaraSoftwareEntries {
|
||||
param($Result, $Uninstall, [string]$View)
|
||||
foreach ($name in $Uninstall.GetSubKeyNames()) {
|
||||
$entry = $null
|
||||
try {
|
||||
$entry = $Uninstall.OpenSubKey($name, $false)
|
||||
if ($null -eq $entry) { throw 'Software entry not readable.' }
|
||||
$display = [string]$entry.GetValue('DisplayName')
|
||||
if ($display) {
|
||||
$Result.data += [pscustomobject]@{ name = $display; publisher = [string]$entry.GetValue('Publisher')
|
||||
version = [string]$entry.GetValue('DisplayVersion'); installation_date = [string]$entry.GetValue('InstallDate')
|
||||
registry_view = $View }
|
||||
}
|
||||
} catch { Add-SamsaraError $Result $_ 'Machine software entry' }
|
||||
finally { if ($entry) { $entry.Dispose() } }
|
||||
}
|
||||
}
|
||||
|
||||
function Get-SamsaraSoftware {
|
||||
param($Result)
|
||||
# Explicit registry views avoid 32-bit redirection and require no installer queries.
|
||||
foreach ($view in @([Microsoft.Win32.RegistryView]::Registry64, [Microsoft.Win32.RegistryView]::Registry32)) {
|
||||
Invoke-SamsaraRead $Result ('Machine software registry ' + [string]$view) {
|
||||
$hive = $null; $uninstall = $null
|
||||
try {
|
||||
$hive = [Microsoft.Win32.RegistryKey]::OpenBaseKey([Microsoft.Win32.RegistryHive]::LocalMachine, $view)
|
||||
$uninstall = $hive.OpenSubKey('SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall', $false)
|
||||
if ($null -eq $uninstall) { return }
|
||||
Read-SamsaraSoftwareEntries $Result $uninstall ([string]$view)
|
||||
} finally {
|
||||
if ($uninstall) { $uninstall.Dispose() }
|
||||
if ($hive) { $hive.Dispose() }
|
||||
}
|
||||
}
|
||||
}
|
||||
$Result.limitations += 'Machine uninstall registrations in both registry views only. Per-user, portable, container and unregistered software may be absent. Install dates are raw registry values and may be empty. No installer consistency checks are triggered.'
|
||||
}
|
||||
23
powershell/Get-ServerStorage.ps1
Normal file
23
powershell/Get-ServerStorage.ps1
Normal file
@@ -0,0 +1,23 @@
|
||||
function Get-SamsaraStorage {
|
||||
param($Result)
|
||||
Invoke-SamsaraRead $Result 'Local volumes' {
|
||||
$Result.data = @(Get-Volume -ErrorAction Stop | ForEach-Object {
|
||||
[pscustomobject]@{ type = 'Volume'; name = [string]$_.FileSystemLabel
|
||||
path = if ($_.DriveLetter) { [string]$_.DriveLetter + ':\' } else { [string]$_.Path }
|
||||
filesystem = [string]$_.FileSystem; capacity_gib = [math]::Round(([double]$_.Size / 1GB), 2)
|
||||
free_gib = [math]::Round(([double]$_.SizeRemaining / 1GB), 2) }
|
||||
})
|
||||
}
|
||||
$Result.limitations += 'Local volume metadata only. Share contents, mounted remote storage, storage-array dependencies, snapshots and backups are not inspected.'
|
||||
}
|
||||
|
||||
function Get-SamsaraShares {
|
||||
param($Result)
|
||||
Invoke-SamsaraRead $Result 'SMB shares' {
|
||||
$Result.data = @(Get-SmbShare -ErrorAction Stop | ForEach-Object {
|
||||
[pscustomobject]@{ type = 'SMB Share'; name = [string]$_.Name; path = [string]$_.Path
|
||||
capacity_gib = $null; free_gib = $null; special = [bool]$_.Special }
|
||||
})
|
||||
}
|
||||
$Result.limitations += 'Configured SMB shares, including administrative shares. No share contents, ACLs, credentials, sessions or consumers queried. Existence does not establish use.'
|
||||
}
|
||||
34
powershell/Get-ServerSystem.ps1
Normal file
34
powershell/Get-ServerSystem.ps1
Normal file
@@ -0,0 +1,34 @@
|
||||
function Get-SamsaraSystem {
|
||||
param($Result)
|
||||
$Result.data = [ordered]@{
|
||||
hostname = [Environment]::MachineName; platform = 'windows'
|
||||
collection_source = 'Local Windows read-only collection'
|
||||
operating_system = 'Not collected'; os_version = 'Not collected'; os_build = 'Not collected'
|
||||
product_type = 0; last_boot = 'Not collected'; ip_addresses = @(); network_adapters = @()
|
||||
domain = 'Not collected'; domain_joined = $null
|
||||
}
|
||||
Invoke-SamsaraRead $Result 'OS identity' {
|
||||
$os = Get-CimInstance -ClassName Win32_OperatingSystem -Property Caption,Version,BuildNumber,LastBootUpTime,ProductType -ErrorAction Stop
|
||||
$years = @{ '14393' = '2016'; '17763' = '2019'; '20348' = '2022'; '26100' = '2025' }
|
||||
if ([int]$os.ProductType -notin @(2,3) -or -not $years.ContainsKey([string]$os.BuildNumber)) {
|
||||
throw 'Unsupported Windows release or workstation edition.'
|
||||
}
|
||||
$Result.data.operating_system = [string]$os.Caption
|
||||
$Result.data.os_version = $years[[string]$os.BuildNumber]
|
||||
$Result.data.os_build = [string]$os.BuildNumber
|
||||
$Result.data.kernel_version = [string]$os.Version
|
||||
$Result.data.product_type = [int]$os.ProductType
|
||||
$Result.data.last_boot = $os.LastBootUpTime.ToUniversalTime().ToString('o')
|
||||
}
|
||||
Invoke-SamsaraRead $Result 'Computer domain membership' {
|
||||
$computer = Get-CimInstance -ClassName Win32_ComputerSystem -Property Domain,PartOfDomain -ErrorAction Stop
|
||||
$Result.data.domain = [string]$computer.Domain
|
||||
$Result.data.domain_joined = [bool]$computer.PartOfDomain
|
||||
}
|
||||
Invoke-SamsaraRead $Result 'Network adapter configuration' {
|
||||
$adapters = @(Get-CimInstance -ClassName Win32_NetworkAdapterConfiguration -Filter 'IPEnabled = True' -Property Description,IPAddress -ErrorAction Stop)
|
||||
$Result.data.network_adapters = @($adapters | ForEach-Object { [string]$_.Description })
|
||||
$Result.data.ip_addresses = @($adapters | ForEach-Object { foreach ($address in $_.IPAddress) { [string]$address } })
|
||||
}
|
||||
$Result.limitations += 'Identity and addresses are local observations. No DNS probes or Active Directory enrichment were performed.'
|
||||
}
|
||||
20
powershell/Get-ServerTasks.ps1
Normal file
20
powershell/Get-ServerTasks.ps1
Normal file
@@ -0,0 +1,20 @@
|
||||
function Get-SamsaraTasks {
|
||||
param($Result)
|
||||
Invoke-SamsaraRead $Result 'Scheduled tasks' {
|
||||
$Result.data = @(Get-ScheduledTask -ErrorAction Stop | ForEach-Object {
|
||||
$task = $_
|
||||
$triggers = @($task.Triggers | ForEach-Object {
|
||||
$type = if ($_.CimClass) { [string]$_.CimClass.CimClassName } else { 'Unknown trigger' }
|
||||
# Select only known scheduler metadata; never serialize entire CIM objects.
|
||||
$repetition = if ($_.Repetition) { [string]$_.Repetition.Interval } else { '' }
|
||||
'{0}; start={1}; enabled={2}; daysInterval={3}; weeksInterval={4}; daysOfWeek={5}; repeat={6}' -f $type, $_.StartBoundary, $_.Enabled, $_.DaysInterval, $_.WeeksInterval, $_.DaysOfWeek, $repetition
|
||||
})
|
||||
$executables = @($task.Actions | ForEach-Object { Get-SamsaraSafeExecutable ([string]$_.Execute) })
|
||||
[pscustomobject]@{ name = [string]$task.TaskName; path = [string]$task.TaskPath
|
||||
enabled = [bool]$task.Settings.Enabled; state = [string]$task.State
|
||||
account = [string]$task.Principal.UserId; executables = ($executables -join '; ')
|
||||
triggers = if ($triggers.Count) { $triggers -join ' | ' } else { 'No trigger metadata visible; manual or external invocation remains possible' } }
|
||||
})
|
||||
}
|
||||
$Result.limitations += 'Visible Task Scheduler registrations only. Arguments, inline scripts, task XML, event queries and execution history are omitted. Non-executable actions may lack an executable name. Trigger fields are descriptive, not a prediction of execution.'
|
||||
}
|
||||
66
powershell/Inventory.Common.ps1
Normal file
66
powershell/Inventory.Common.ps1
Normal file
@@ -0,0 +1,66 @@
|
||||
# Shared helpers, compatible with Windows PowerShell 5.1. No raw errors are serialized.
|
||||
function Get-SamsaraTimestamp { [DateTime]::UtcNow.ToString('o') }
|
||||
|
||||
function Add-SamsaraError {
|
||||
param($Result, $Record, [string]$Scope)
|
||||
$code = 'collection_failed'
|
||||
if ($Record.CategoryInfo.Category -in @('PermissionDenied', 'SecurityError') -or
|
||||
$Record.Exception -is [UnauthorizedAccessException] -or
|
||||
$Record.Exception.HResult -eq -2147024891) { $code = 'access_denied' }
|
||||
elseif ($Record.Exception -is [System.Management.Automation.CommandNotFoundException]) { $code = 'command_unavailable' }
|
||||
$message = switch ($code) {
|
||||
'access_denied' { 'Current account cannot read this scope.' }
|
||||
'command_unavailable' { 'Required local inventory command is unavailable.' }
|
||||
default { 'Local query could not complete; raw diagnostics are excluded.' }
|
||||
}
|
||||
$Result.errors += [pscustomobject]@{ code = $code; scope = $Scope; message = $message }
|
||||
if ($code -eq 'access_denied') { $Result.permission_limitations += $Scope + ': read access denied.' }
|
||||
}
|
||||
|
||||
function Invoke-SamsaraRead {
|
||||
param($Result, [string]$Scope, [scriptblock]$Query)
|
||||
$Result.attempted_subchecks++
|
||||
try {
|
||||
& $Query
|
||||
$Result.successful_subchecks++
|
||||
} catch { Add-SamsaraError $Result $_ $Scope }
|
||||
}
|
||||
|
||||
function Get-SamsaraSafeExecutable {
|
||||
param([string]$Value)
|
||||
# Task action arguments, scripts and environment values are deliberately omitted.
|
||||
$name = [IO.Path]::GetFileName(($Value -replace '\\', '/'))
|
||||
if ($name -match '^[A-Za-z0-9_. -]+\.(exe|com|cmd|bat|ps1|vbs|js|wsf|msc)$') { return $name }
|
||||
return '[executable name omitted]'
|
||||
}
|
||||
|
||||
function Invoke-SamsaraSection {
|
||||
param([string]$Name, [scriptblock]$Collector)
|
||||
$result = [ordered]@{
|
||||
section = $Name; status = 'complete'; message = 'Local read-only query completed within the documented scope.'
|
||||
started_at = Get-SamsaraTimestamp; completed_at = $null
|
||||
errors = @(); permission_limitations = @(); limitations = @()
|
||||
attempted_subchecks = 0; successful_subchecks = 0; data = @()
|
||||
}
|
||||
try { & $Collector $result } catch { Add-SamsaraError $result $_ $Name }
|
||||
if ($result.errors.Count -gt 0) {
|
||||
$result.status = if ($result.successful_subchecks -gt 0) { 'partial' } else { 'failed' }
|
||||
$result.message = 'One or more local queries failed; see sanitized errors and limitations.'
|
||||
}
|
||||
if (-not $script:SamsaraIsElevated -and $Name -in @('roles','services','network','tasks','shares','storage','certificates')) {
|
||||
$result.permission_limitations += 'Not elevated: protected objects may be hidden by the current account permissions.'
|
||||
if ($result.status -eq 'complete') { $result.status = 'partial' }
|
||||
}
|
||||
$result.completed_at = Get-SamsaraTimestamp
|
||||
[pscustomobject]$result
|
||||
}
|
||||
|
||||
function ConvertTo-SamsaraCheck {
|
||||
param($Section)
|
||||
[ordered]@{
|
||||
status = $Section.status; message = $Section.message
|
||||
started_at = $Section.started_at; completed_at = $Section.completed_at
|
||||
errors = @($Section.errors); permission_limitations = @($Section.permission_limitations)
|
||||
limitations = @($Section.limitations)
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,27 @@
|
||||
# Windows collection engine (planned)
|
||||
# Local Windows Server inventory
|
||||
|
||||
Phase 1 does not execute PowerShell. Phase 2 will introduce Get-ServerInventory.ps1 and independent Get-ServerServices.ps1, Get-ServerNetwork.ps1, Get-ServerTasks.ps1, Get-ServerSoftware.ps1, Get-ServerStorage.ps1 and Get-ServerCertificates.ps1 scripts, plus optional AD enrichment.
|
||||
Targets: Windows Server 2016/2019/2022/2025, Windows PowerShell 5.1, 64-bit execution. **Real Windows host validation is still pending in this Linux development workspace.** No remoting or credentials are used.
|
||||
|
||||
Use existing authorized PowerShell remoting from Windows. Never configure WinRM/SSH, restart services, install software, collect private keys or use Win32_Product. Inventory software via registry uninstall keys (both registry views). Return per-check failures without fabricating results. Least privilege is required; restricted inventory may produce partial results. Task/service command arguments need redaction before export.
|
||||
`Get-ServerInventory.ps1` is the fixed JSON driver. With no arguments it emits a full v1 inventory envelope. `-Section system|roles|services|network|tasks|software|storage|shares|certificates` emits an internal per-section JSON result. The Python collector invokes these independently to enforce time/output limits while preserving other sections after failures.
|
||||
|
||||
| Module | Local read-only source |
|
||||
| --- | --- |
|
||||
| Get-ServerSystem.ps1 | CIM OS/product type/build, boot, domain membership, adapter configuration |
|
||||
| Get-ServerRoles.ps1 | Installed Get-WindowsFeature entries |
|
||||
| Get-ServerServices.ps1 | Win32_Service metadata and service identities; no PathName/arguments |
|
||||
| Get-ServerNetwork.ps1 | Get-NetTCPConnection and Get-Process names/IDs |
|
||||
| Get-ServerTasks.ps1 | Get-ScheduledTask visible definitions, accounts, safe executable basenames, selected triggers |
|
||||
| Get-ServerSoftware.ps1 | HKLM uninstall registrations using explicit Registry32/Registry64 read-only handles |
|
||||
| Get-ServerStorage.ps1 | Get-Volume and Get-SmbShare; volumes/shares share the existing storage inventory category |
|
||||
| Get-ServerCertificates.ps1 | Public metadata in accessible LocalMachine certificate stores |
|
||||
| Inventory.Common.ps1 | Section timing, safe errors and permission limitations |
|
||||
|
||||
No Win32_Product, installer operations, private-key export, task execution, service changes, registry writes or remoting setup. No execution-policy bypass; use approved script signing/distribution if enterprise policy blocks scripts. Non-elevated sensitive checks are conservatively partial even if their visible query succeeds. Elevation is never attempted automatically. Domain Administrator privileges are not required; assess with the least-privileged authorized account and treat omitted evidence explicitly.
|
||||
|
||||
The Windows Server gate uses CIM ProductType 2/3 plus the release build map; Windows 10/11 are not Server targets even where their kernel builds overlap. The gate is enforced by Python and by full-envelope PowerShell use. `-Section system` can return failed/partial identity details for diagnostics, but Python will not accept an unverified identity as an assessment.
|
||||
|
||||
Direct full-script use has no Python timeout/cancellation supervisor; use the Python CLI or GUI for bounded execution. Both paths honor local script execution policy. Every script emits only selected public/administrative metadata, never raw CIM objects, exception text, task XML, task arguments or service command lines.
|
||||
|
||||
See README.md for exact GUI, headless and real-host validation instructions. `tests/powershell/Test-Collectors.ps1` parses scripts and mocks their data sources; this is synthetic testing, not real host validation.
|
||||
|
||||
Implementation references: [Get-WindowsFeature](https://learn.microsoft.com/en-us/powershell/module/servermanager/get-windowsfeature), [Win32_Service](https://learn.microsoft.com/en-us/windows/win32/cimwin32prov/win32-service), [Get-NetTCPConnection](https://learn.microsoft.com/en-us/powershell/module/nettcpip/get-nettcpconnection), [Get-ScheduledTask](https://learn.microsoft.com/en-us/powershell/module/scheduledtasks/get-scheduledtask), [registry software inventory](https://learn.microsoft.com/en-us/powershell/scripting/samples/working-with-software-installations), [certificate provider](https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.security/about/about_certificate_provider).
|
||||
|
||||
Reference in New Issue
Block a user