feat(aw-rus): sync deploy stack, install kit, and health checks

This commit is contained in:
igor04091968
2026-05-21 15:16:23 +03:00
parent 45f9907450
commit 99143b108b
67 changed files with 6449 additions and 681 deletions
+29 -1
View File
@@ -749,6 +749,33 @@ function New-LaunchLock {
return `$lockPath
}
function Get-SessionMarkerToken {
param([int]`$SessionId)
try {
`$explorer = Get-Process -Name 'explorer' -ErrorAction SilentlyContinue |
Where-Object { `$_.SessionId -eq `$SessionId } |
Sort-Object StartTime |
Select-Object -First 1
if (`$explorer -and `$explorer.StartTime) {
return `$explorer.StartTime.ToUniversalTime().ToString('yyyyMMddTHHmmssZ')
}
}
catch {
}
try {
`$currentProcess = Get-Process -Id `$PID -ErrorAction Stop
if (`$currentProcess.StartTime) {
return `$currentProcess.StartTime.ToUniversalTime().ToString('yyyyMMddTHHmmssZ')
}
}
catch {
}
return [string]`$SessionId
}
function Invoke-AwJsonPost {
param(
[Parameter(Mandatory = `$true)][string]`$Uri,
@@ -864,7 +891,8 @@ function Send-LogonMarkerIfNeeded {
return
}
`$markerFile = Join-Path `$markerDir ("logon-{0}-{1}.marker" -f `$env:USERNAME, `$SessionId)
`$sessionMarkerToken = Get-SessionMarkerToken -SessionId `$SessionId
`$markerFile = Join-Path `$markerDir ("logon-{0}-{1}-{2}.marker" -f `$env:USERNAME, `$SessionId, `$sessionMarkerToken)
if (Test-Path -LiteralPath `$markerFile) {
return
}
+338
View File
@@ -0,0 +1,338 @@
[CmdletBinding()]
param(
[string[]]$ExpectedUsers = @(),
[switch]$IncludeUnexpectedProfiles
)
Set-StrictMode -Version Latest
$ErrorActionPreference = 'Stop'
function Get-UninstallEntries {
param(
[Parameter(Mandatory = $true)]
[string]$NamePattern
)
$paths = @(
'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*',
'HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*'
)
return @(
Get-ItemProperty $paths -ErrorAction SilentlyContinue |
Where-Object {
$_.PSObject.Properties.Name -contains 'DisplayName' -and
-not [string]::IsNullOrWhiteSpace([string]$_.DisplayName) -and
([string]$_.DisplayName -match $NamePattern)
} |
Select-Object DisplayName, DisplayVersion, Publisher
)
}
function Get-CryptoProToolPath {
param(
[Parameter(Mandatory = $true)]
[string]$LeafName
)
$candidate = Join-Path 'C:\Program Files\Crypto Pro\CSP' $LeafName
if (Test-Path -LiteralPath $candidate) {
return $candidate
}
return $null
}
function Get-CryptoProStoreMapForCurrentUser {
param(
[string]$CertmgrPath
)
$map = @{}
if ([string]::IsNullOrWhiteSpace($CertmgrPath) -or -not (Test-Path -LiteralPath $CertmgrPath)) {
return $map
}
$raw = & $CertmgrPath -list -store uMy 2>&1
if ($LASTEXITCODE -ne 0) {
return $map
}
$current = @{}
foreach ($line in @($raw | ForEach-Object { [string]$_ })) {
$trimmed = $line.Trim()
if ($trimmed -match '^SHA1 Thumbprint\s*:\s*(.+)$') {
$current.thumbprint = $Matches[1].Trim().ToUpperInvariant()
continue
}
if ($trimmed -match '^Embedded License\s*:\s*(.+)$') {
$current.embeddedLicense = $Matches[1].Trim()
continue
}
if ($trimmed -match '^Container\s*:\s*(.+)$') {
$current.container = $Matches[1].Trim()
continue
}
if ($trimmed -match '^\[ErrorCode:') {
if ($current.ContainsKey('thumbprint')) {
$map[$current.thumbprint] = [pscustomobject]@{
embeddedLicense = if ($current.ContainsKey('embeddedLicense')) { [string]$current.embeddedLicense } else { $null }
container = if ($current.ContainsKey('container')) { [string]$current.container } else { $null }
}
}
$current = @{}
}
}
return $map
}
function Test-CertificateEmbeddedLicense {
param(
[Parameter(Mandatory = $true)]
[System.Security.Cryptography.X509Certificates.X509Certificate2]$Certificate,
[string]$CspTestPath
)
$result = [ordered]@{
ok = $null
status = $null
error = $null
}
if ([string]::IsNullOrWhiteSpace($CspTestPath) -or -not (Test-Path -LiteralPath $CspTestPath)) {
$result.error = 'csptest_missing'
return [pscustomobject]$result
}
$tempPath = Join-Path $env:TEMP ("cryptopro-audit-{0}.cer" -f ([Guid]::NewGuid().ToString('N')))
try {
Export-Certificate -Cert $Certificate -FilePath $tempPath -Force | Out-Null
$raw = & $CspTestPath -certlic -check -certfile $tempPath 2>&1
if ($LASTEXITCODE -eq 0) {
$licenseLine = @($raw | Where-Object { [string]$_ -match '^License:\s*' } | Select-Object -First 1)
if ($licenseLine.Count -gt 0) {
$statusText = (($licenseLine[0] -replace '^License:\s*', '').Trim())
$result.status = $statusText
$result.ok = ($statusText -match '^Good license\b')
}
else {
$result.ok = $false
$result.error = 'license_line_missing'
}
}
else {
$result.ok = $false
$result.error = ('csptest_exit_{0}' -f $LASTEXITCODE)
}
}
catch {
$result.ok = $false
$result.error = $_.Exception.Message
}
finally {
Remove-Item -LiteralPath $tempPath -Force -ErrorAction SilentlyContinue
}
return [pscustomobject]$result
}
function Get-ProfileMap {
$profileMap = @{}
foreach ($profile in @(Get-CimInstance Win32_UserProfile | Where-Object { -not $_.Special })) {
$leaf = Split-Path -Leaf ([string]$profile.LocalPath)
if ([string]::IsNullOrWhiteSpace($leaf)) { continue }
$profileMap[$leaf.ToUpperInvariant()] = [pscustomobject]@{
user = $leaf
sid = [string]$profile.SID
localPath = [string]$profile.LocalPath
loaded = [bool]$profile.Loaded
}
}
return $profileMap
}
function Get-UserAuditRows {
param(
[Parameter(Mandatory = $true)]
[string]$RequestedUser,
[pscustomobject]$Profile,
[string]$CspTestPath,
[hashtable]$CurrentUserStoreMap,
[bool]$ExpectedSigner
)
if ($null -eq $Profile) {
return @(
[pscustomobject]@{
requestedUser = $RequestedUser
profileUser = $null
profilePath = $null
profileLoaded = $false
sid = $null
storeFilePath = $null
thumbprint = $null
subject = $null
issuer = $null
notAfterUtc = $null
hasPrivateKey = $false
embeddedLicenseOk = $null
embeddedLicenseStatus = $null
container = $null
expectedSigner = $ExpectedSigner
actionNeeded = 'profile_missing_or_no_login'
}
)
}
$certDir = Join-Path $Profile.localPath 'AppData\Roaming\Microsoft\SystemCertificates\My\Certificates'
$files = @()
if (Test-Path -LiteralPath $certDir) {
$files = @(Get-ChildItem -LiteralPath $certDir -File -ErrorAction SilentlyContinue)
}
if ($files.Count -eq 0) {
return @(
[pscustomobject]@{
requestedUser = $RequestedUser
profileUser = $Profile.user
profilePath = $Profile.localPath
profileLoaded = [bool]$Profile.loaded
sid = $Profile.sid
storeFilePath = $certDir
thumbprint = $null
subject = $null
issuer = $null
notAfterUtc = $null
hasPrivateKey = $false
embeddedLicenseOk = $null
embeddedLicenseStatus = $null
container = $null
expectedSigner = $ExpectedSigner
actionNeeded = 'certificate_missing'
}
)
}
$rows = @()
foreach ($file in $files) {
try {
$cert = Get-PfxCertificate -FilePath $file.FullName
$thumbprint = [string]$cert.Thumbprint
$license = Test-CertificateEmbeddedLicense -Certificate $cert -CspTestPath $CspTestPath
$storeMeta = $null
if ($CurrentUserStoreMap.ContainsKey($thumbprint.ToUpperInvariant())) {
$storeMeta = $CurrentUserStoreMap[$thumbprint.ToUpperInvariant()]
}
$actionNeeded = 'manual_review'
if ($cert.NotAfter -lt (Get-Date)) {
$actionNeeded = 'renew_certificate'
}
elseif (-not [bool]$cert.HasPrivateKey) {
$actionNeeded = 'attach_private_key_or_token'
}
elseif ($license.ok -eq $true) {
$actionNeeded = 'ready'
}
elseif ($license.ok -eq $false) {
$actionNeeded = 'embedded_license_missing_or_invalid'
}
$rows += [pscustomobject]@{
requestedUser = $RequestedUser
profileUser = $Profile.user
profilePath = $Profile.localPath
profileLoaded = [bool]$Profile.loaded
sid = $Profile.sid
storeFilePath = $file.FullName
thumbprint = $thumbprint
subject = [string]$cert.Subject
issuer = [string]$cert.Issuer
notAfterUtc = $cert.NotAfter.ToUniversalTime().ToString('o')
hasPrivateKey = [bool]$cert.HasPrivateKey
embeddedLicenseOk = $license.ok
embeddedLicenseStatus = $license.status
container = if ($null -ne $storeMeta) { [string]$storeMeta.container } else { $null }
expectedSigner = $ExpectedSigner
actionNeeded = $actionNeeded
}
}
catch {
$rows += [pscustomobject]@{
requestedUser = $RequestedUser
profileUser = $Profile.user
profilePath = $Profile.localPath
profileLoaded = [bool]$Profile.loaded
sid = $Profile.sid
storeFilePath = $file.FullName
thumbprint = $null
subject = $null
issuer = $null
notAfterUtc = $null
hasPrivateKey = $false
embeddedLicenseOk = $null
embeddedLicenseStatus = $null
container = $null
expectedSigner = $ExpectedSigner
actionNeeded = ('certificate_parse_error: {0}' -f $_.Exception.Message)
}
}
}
return $rows
}
$cspEntries = @(Get-UninstallEntries -NamePattern 'CryptoPro|КриптоПро|Крипто')
$cspMain = @($cspEntries | Where-Object { $_.DisplayName -match 'CSP' } | Select-Object -First 1)
$browserPlugin = @($cspEntries | Where-Object { $_.DisplayName -match 'Browser' } | Select-Object -First 1)
$cspTestPath = Get-CryptoProToolPath -LeafName 'csptest.exe'
$certmgrPath = Get-CryptoProToolPath -LeafName 'certmgr.exe'
$currentUserStoreMap = Get-CryptoProStoreMapForCurrentUser -CertmgrPath $certmgrPath
$profileMap = Get-ProfileMap
$targets = New-Object 'System.Collections.Generic.List[string]'
foreach ($expected in @($ExpectedUsers)) {
if (-not [string]::IsNullOrWhiteSpace([string]$expected)) {
$targets.Add([string]$expected)
}
}
if ($IncludeUnexpectedProfiles.IsPresent -or $targets.Count -eq 0) {
foreach ($profileKey in @($profileMap.Keys | Sort-Object)) {
$targets.Add([string]$profileMap[$profileKey].user)
}
}
$seen = New-Object 'System.Collections.Generic.HashSet[string]' ([System.StringComparer]::OrdinalIgnoreCase)
$rows = @()
foreach ($target in $targets) {
if (-not $seen.Add($target)) { continue }
$key = $target.ToUpperInvariant()
$profile = if ($profileMap.ContainsKey($key)) { $profileMap[$key] } else { $null }
$rows += Get-UserAuditRows -RequestedUser $target -Profile $profile -CspTestPath $cspTestPath -CurrentUserStoreMap $currentUserStoreMap -ExpectedSigner:([bool]($ExpectedUsers -contains $target))
}
$summary = [ordered]@{
expectedSignerCount = [int](@($ExpectedUsers | Where-Object { -not [string]::IsNullOrWhiteSpace([string]$_) }).Count)
readyCount = [int](@($rows | Where-Object { $_.actionNeeded -eq 'ready' }).Count)
missingProfileCount = [int](@($rows | Where-Object { $_.actionNeeded -eq 'profile_missing_or_no_login' }).Count)
missingCertificateCount = [int](@($rows | Where-Object { $_.actionNeeded -eq 'certificate_missing' }).Count)
noPrivateKeyCount = [int](@($rows | Where-Object { $_.actionNeeded -eq 'attach_private_key_or_token' }).Count)
licenseProblemCount = [int](@($rows | Where-Object { $_.actionNeeded -eq 'embedded_license_missing_or_invalid' }).Count)
renewalNeededCount = [int](@($rows | Where-Object { $_.actionNeeded -eq 'renew_certificate' }).Count)
}
[pscustomobject]@{
generatedAtUtc = (Get-Date).ToUniversalTime().ToString('o')
computerName = [string]$env:COMPUTERNAME
currentUser = [string]$env:USERNAME
cryptopro = [ordered]@{
cspInstalled = ($null -ne $cspMain)
cspVersion = if ($null -ne $cspMain) { [string]$cspMain.DisplayVersion } else { $null }
browserPluginInstalled = ($null -ne $browserPlugin)
browserPluginVersion = if ($null -ne $browserPlugin) { [string]$browserPlugin.DisplayVersion } else { $null }
cspTestPath = $cspTestPath
certmgrPath = $certmgrPath
}
expectedUsers = @($ExpectedUsers)
signers = @($rows)
summary = $summary
}