Private/Paths.ps1
|
#Requires -Version 5.1 function Get-SMBeatDefaultDataRoot { $pd = $env:ProgramData if ([string]::IsNullOrWhiteSpace($pd)) { if (-not [string]::IsNullOrWhiteSpace($env:HOME)) { return (Join-Path $env:HOME '.smbeat') } return (Join-Path ([System.IO.Path]::GetTempPath()) 'SMBeat') } Join-Path $pd 'SMBeat' } function Get-SMBeatDefaultReportRoot { $docs = [Environment]::GetFolderPath('MyDocuments') if ([string]::IsNullOrWhiteSpace($docs)) { if (-not [string]::IsNullOrWhiteSpace($env:HOME)) { $homeDocs = Join-Path $env:HOME 'Documents' return (Join-Path $homeDocs 'SMBeat-Reports') } return (Join-Path ([System.IO.Path]::GetTempPath()) 'SMBeat-Reports') } Join-Path $docs 'SMBeat-Reports' } function Get-SMBeatDataRoot { param( [string]$Path, [string]$ComputerName ) $root = $Path if ([string]::IsNullOrWhiteSpace($root)) { $root = Get-SMBeatDefaultDataRoot } if (-not [string]::IsNullOrWhiteSpace($ComputerName)) { $root = Join-Path $root $ComputerName } return $root } function Get-SMBeatSampleDirectory { param( [Parameter(Mandatory = $true)] [string]$DataRoot ) Join-Path $DataRoot 'samples' } function Get-SMBeatSampleFilePath { param( [Parameter(Mandatory = $true)] [string]$DataRoot, [Parameter(Mandatory = $true)] [datetime]$Utc ) $day = $Utc.ToString('yyyy-MM-dd', $script:SMBeatInvariant) Join-Path (Get-SMBeatSampleDirectory -DataRoot $DataRoot) ($day + '.jsonl') } function Get-SMBeatStatePath { param( [Parameter(Mandatory = $true)] [string]$DataRoot ) Join-Path $DataRoot 'state.json' } function Get-SMBeatHeartbeatPath { param( [Parameter(Mandatory = $true)] [string]$DataRoot ) Join-Path $DataRoot 'heartbeat.json' } function Get-SMBeatConfigPath { param( [Parameter(Mandatory = $true)] [string]$DataRoot ) Join-Path $DataRoot 'config.json' } function Get-SMBeatLogPath { param( [Parameter(Mandatory = $true)] [string]$DataRoot ) Join-Path $DataRoot 'collector.log' } function Get-SMBeatEventsPath { param( [Parameter(Mandatory = $true)] [string]$DataRoot ) Join-Path $DataRoot 'events.jsonl' } function Get-SMBeatBootStatePath { param( [Parameter(Mandatory = $true)] [string]$DataRoot ) Join-Path $DataRoot 'boot.json' } function Get-SMBeatModuleStatePath { param( [Parameter(Mandatory = $true)] [string]$DataRoot ) Join-Path $DataRoot 'module.json' } function Get-SMBeatEtwMapsPath { param( [Parameter(Mandatory = $true)] [string]$DataRoot ) Join-Path $DataRoot 'etw-maps.json' } function Get-SMBeatHostStatePath { param( [Parameter(Mandatory = $true)] [string]$DataRoot ) Join-Path $DataRoot 'host.json' } function Get-SMBeatStopRequestPath { param( [Parameter(Mandatory = $true)] [string]$DataRoot ) Join-Path $DataRoot 'stop.request' } function Get-SMBeatFlushRequestPath { param( [Parameter(Mandatory = $true)] [string]$DataRoot ) Join-Path $DataRoot 'flush.request' } function Get-SMBeatCollectorScriptPath { Join-Path $script:SMBeatModuleRoot 'Scripts\Invoke-SMBeatCollector.ps1' } function Get-SMBeatPowerShellExePath { Join-Path $env:SystemRoot 'System32\WindowsPowerShell\v1.0\powershell.exe' } function Get-SMBeatModuleVersion { $mod = Get-Module -Name SMBeat -ErrorAction SilentlyContinue if ($mod -and $mod.Version) { return $mod.Version.ToString() } $psd1 = Join-Path $script:SMBeatModuleRoot 'SMBeat.psd1' if (Test-Path -LiteralPath $psd1) { $data = Import-PowerShellDataFile -Path $psd1 -ErrorAction SilentlyContinue if ($data -and $data.ModuleVersion) { return [string]$data.ModuleVersion } } return 'unknown' } function Compare-SMBeatModuleVersion { param( [string]$Left, [string]$Right ) $leftVer = $null $rightVer = $null if (-not [version]::TryParse($Left, [ref]$leftVer)) { return -1 } if (-not [version]::TryParse($Right, [ref]$rightVer)) { return 1 } $leftVer.CompareTo($rightVer) } function Get-SMBeatTcp445InboundFixedVersion { '0.7.3' } function Test-SMBeatTcp445InboundTrusted { param( [string]$CollectorVersion, [AllowEmptyCollection()] [object[]]$Events, [datetime]$WindowStartUtc = [datetime]::MinValue ) $need = Get-SMBeatTcp445InboundFixedVersion $collectorKnown = -not [string]::IsNullOrWhiteSpace($CollectorVersion) $collectorFixed = $false if ($collectorKnown) { if ((Compare-SMBeatModuleVersion -Left $CollectorVersion -Right $need) -lt 0) { return $false } $collectorFixed = $true } $startRe = [regex]'version=(\d+\.\d+\.\d+)' $updateRe = [regex]'(\d+\.\d+\.\d+)\s*->\s*(\d+\.\d+\.\d+)' $hasWindow = $WindowStartUtc -gt [datetime]::MinValue foreach ($ev in @($Events)) { if ($null -eq $ev) { continue } $msg = '' if ($ev.PSObject.Properties['Message']) { $msg = [string]$ev.Message } $ts = [datetime]::MinValue if ($ev.PSObject.Properties['Utc'] -and $ev.Utc) { try { $ts = [datetime]$ev.Utc } catch { $ts = [datetime]::MinValue } } $inWindow = (-not $hasWindow) -or ($ts -eq [datetime]::MinValue) -or ($ts -ge $WindowStartUtc) $started = $startRe.Match($msg) if ($started.Success -and $inWindow) { if ((Compare-SMBeatModuleVersion -Left $started.Groups[1].Value -Right $need) -lt 0) { return $false } } $updated = $updateRe.Match($msg) if (-not $updated.Success) { continue } $from = $updated.Groups[1].Value $to = $updated.Groups[2].Value $toFixed = (Compare-SMBeatModuleVersion -Left $to -Right $need) -ge 0 $fromBroken = (Compare-SMBeatModuleVersion -Left $from -Right $need) -lt 0 if (-not $toFixed) { if ($inWindow) { return $false } if (-not $collectorFixed) { return $false } continue } if ($fromBroken -and $inWindow) { return $false } } $true } function Get-SMBeatInstallDestination { $pf = $env:ProgramFiles if ([string]::IsNullOrWhiteSpace($pf)) { return '' } Join-Path $pf 'WindowsPowerShell\Modules\SMBeat' } function Test-SMBeatModuleInstalled { $dest = Get-SMBeatInstallDestination if ([string]::IsNullOrWhiteSpace($dest)) { return $false } Test-Path -LiteralPath (Join-Path $dest 'SMBeat.psd1') } function Get-SMBeatModuleCopyNames { @('SMBeat.psd1', 'SMBeat.psm1', 'Public', 'Private', 'Scripts', 'en-US', 'LICENSE') } function Copy-SMBeatModuleToDestination { param( [Parameter(Mandatory = $true)] [string]$Destination, [string]$SourceRoot ) if ([string]::IsNullOrWhiteSpace($SourceRoot)) { $SourceRoot = $script:SMBeatModuleRoot } if ([string]::IsNullOrWhiteSpace($Destination)) { throw 'SMBeat install destination is empty.' } if (-not (Test-Path -LiteralPath $Destination)) { New-Item -ItemType Directory -Path $Destination -Force | Out-Null } foreach ($name in Get-SMBeatModuleCopyNames) { $from = Join-Path $SourceRoot $name if (Test-Path -LiteralPath $from) { Copy-Item -LiteralPath $from -Destination $Destination -Recurse -Force } } return $Destination } function Get-SMBeatLatestInstalledVersion { $list = @(Get-Module -Name SMBeat -ListAvailable -ErrorAction SilentlyContinue | Sort-Object Version) if ($list.Count -gt 0) { return $list[$list.Count - 1].Version.ToString() } return (Get-SMBeatModuleVersion) } function Update-SMBeatInstalledModule { $methods = New-Object System.Collections.Generic.List[string] $dest = Get-SMBeatInstallDestination if (Get-Command Update-Module -ErrorAction SilentlyContinue) { try { Update-Module -Name SMBeat -Force -ErrorAction Stop $methods.Add('gallery') | Out-Null } catch { } } if (-not [string]::IsNullOrWhiteSpace($dest)) { try { Copy-SMBeatModuleToDestination -Destination $dest | Out-Null $methods.Add('copy') | Out-Null } catch { } } $path = $dest if ([string]::IsNullOrWhiteSpace($path)) { $path = $script:SMBeatModuleRoot } [PSCustomObject]@{ Methods = $methods.ToArray() ModulePath = $path Version = Get-SMBeatLatestInstalledVersion } } function ConvertTo-SMBeatHashtable { param( $Object ) $hash = @{} if ($null -eq $Object) { return $hash } if ($Object -is [hashtable]) { return $Object } foreach ($prop in $Object.PSObject.Properties) { $hash[$prop.Name] = $prop.Value } return $hash } function Get-SMBeatPropertyValue { param( $Object, [Parameter(Mandatory = $true)] [string]$Name, $Default ) if ($null -eq $Object) { return $Default } if ($Object -is [hashtable]) { if ($Object.ContainsKey($Name)) { return $Object[$Name] } return $Default } $prop = $Object.PSObject.Properties[$Name] if ($null -eq $prop) { return $Default } return $prop.Value } function Get-SMBeatDefaultConfig { [PSCustomObject]@{ IntervalSec = $script:SMBeatDefaultIntervalSec RetentionDays = $script:SMBeatDefaultRetentionDays Path = Get-SMBeatDefaultDataRoot IncludeNics = $true IncludeEtwShares = $true IncludeSmbPerf = $true IncludeAdminShares = $false Mode = 'tcp445+etw' MailTo = '' SmtpServer = '' MailFrom = '' } } function Read-SMBeatConfig { param( [string]$DataRoot ) if ([string]::IsNullOrWhiteSpace($DataRoot)) { $DataRoot = Get-SMBeatDefaultDataRoot } $path = Get-SMBeatConfigPath -DataRoot $DataRoot $json = Read-SMBeatTextFile -Path $path $defaults = Get-SMBeatDefaultConfig if ($null -eq $json) { return $defaults } $parsed = ConvertFrom-SMBeatJson -Json $json $result = ConvertTo-SMBeatHashtable -Object $defaults foreach ($prop in $parsed.PSObject.Properties) { $result[$prop.Name] = $prop.Value } [PSCustomObject]$result } function Save-SMBeatConfig { param( [Parameter(Mandatory = $true)] [string]$DataRoot, [Parameter(Mandatory = $true)] $Config ) $path = Get-SMBeatConfigPath -DataRoot $DataRoot Write-SMBeatTextFile -Path $path -Text (ConvertTo-SMBeatJson -InputObject $Config) } |