[CmdletBinding()] param( [switch]$RequireDomainJoined, [switch]$RequireRemoteAccess, [switch]$RequireBrokerHealth, [string]$RemoteDesktopPrincipal = 'LCI\SG-Laboratorio-Usuarios-RDP', [switch]$Enforce ) $ErrorActionPreference = 'Stop' $providerClassId = '{D789CFD8-5AD4-489F-9B83-7EB5D9D09335}' $passwordProviderClassId = '{60B78E88-EAD8-445C-9CFD-0B87F74EA6CD}' $providerRegistryPath = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Authentication\Credential Providers\$providerClassId" $passwordProviderRegistryPath = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Authentication\Credential Providers\$passwordProviderClassId" $classRegistryPath = "HKLM:\SOFTWARE\Classes\CLSID\$providerClassId\InprocServer32" $defaultProviderPolicyPath = 'HKLM:\SOFTWARE\Policies\Microsoft\Windows\System' $settingsPath = Join-Path $env:ProgramData 'SGU\CredentialProvider\settings.json' $issues = [Collections.Generic.List[string]]::new() $computer = Get-CimInstance Win32_ComputerSystem if ($RequireDomainJoined -and -not $computer.PartOfDomain) { $issues.Add('The computer is not joined to a domain.') } $providerRegistered = Test-Path -LiteralPath $providerRegistryPath if (-not $providerRegistered) { $issues.Add('The SGU Credential Provider registration is missing.') } $registeredDll = $null if (Test-Path -LiteralPath $classRegistryPath) { $registeredDll = (Get-Item -LiteralPath $classRegistryPath).GetValue('') } $providerBinaryPresent = $registeredDll -and (Test-Path -LiteralPath $registeredDll -PathType Leaf) if (-not $providerBinaryPresent) { $issues.Add('The registered SGU COM binary is missing.') } $configuredDefault = $null try { $configuredDefault = Get-ItemPropertyValue ` -LiteralPath $defaultProviderPolicyPath ` -Name DefaultCredentialProvider ` -ErrorAction Stop } catch { # Report the missing or unreadable policy as a failed enrollment check. } $defaultProviderConfigured = $configuredDefault -eq $providerClassId if (-not $defaultProviderConfigured) { $issues.Add('The SGU provider is not assigned as the machine default credential provider.') } $passwordProviderPreserved = Test-Path -LiteralPath $passwordProviderRegistryPath if (-not $passwordProviderPreserved) { $issues.Add('The built-in Microsoft password provider registration is missing.') } $settings = $null try { $settings = Get-Content -LiteralPath $settingsPath -Raw | ConvertFrom-Json } catch { $issues.Add('The SGU provider settings file is missing or invalid.') } $clientCertificatePresent = $false $serverCertificateTrusted = $false $brokerHealth = $null if ($settings) { $clientCertificate = Get-ChildItem Cert:\LocalMachine\My | Where-Object Thumbprint -eq $settings.ClientCertificateThumbprint | Select-Object -First 1 $clientCertificatePresent = $clientCertificate -and $clientCertificate.HasPrivateKey if (-not $clientCertificatePresent) { $issues.Add('The client mTLS certificate with private key is missing.') } $serverCertificate = Get-ChildItem Cert:\LocalMachine\Root,Cert:\LocalMachine\CA | Where-Object Thumbprint -eq $settings.ServerCertificateThumbprint | Select-Object -First 1 $serverCertificateTrusted = [bool]$serverCertificate if (-not $serverCertificateTrusted) { $issues.Add('The broker certificate is not trusted by LocalMachine.') } if ($RequireBrokerHealth -and $clientCertificatePresent) { try { $healthUri = ([Uri]$settings.BrokerEndpoint).GetLeftPart([UriPartial]::Authority) + '/health/live' $health = Invoke-RestMethod -Uri $healthUri -Certificate $clientCertificate -TimeoutSec 10 $brokerHealth = [string]$health.status if ($brokerHealth -ne 'ok') { $issues.Add('The broker health endpoint did not return ok.') } } catch { $issues.Add("The broker health check failed: $($_.Exception.Message)") } } } $dotNetRuntimePresent = $false $dotnetCandidates = @( (Get-Command dotnet -ErrorAction SilentlyContinue | Select-Object -ExpandProperty Source -ErrorAction SilentlyContinue), (Join-Path $env:ProgramFiles 'dotnet\dotnet.exe') ) | Where-Object { $_ -and (Test-Path -LiteralPath $_ -PathType Leaf) } | Select-Object -Unique foreach ($dotnet in $dotnetCandidates) { if (& $dotnet --list-runtimes | Select-String '^Microsoft\.NETCore\.App 10\.') { $dotNetRuntimePresent = $true break } } if (-not $dotNetRuntimePresent) { $issues.Add('The Microsoft .NET 10 x64 runtime is missing.') } $remoteAccessReady = $null if ($RequireRemoteAccess) { $remoteDesktopUsersSid = [Security.Principal.SecurityIdentifier]::new('S-1-5-32-555') $remoteDesktopUsersGroup = ($remoteDesktopUsersSid.Translate([Security.Principal.NTAccount]).Value -split '\\', 2)[1] $rdpMembers = @(Get-LocalGroupMember -Group $remoteDesktopUsersGroup -ErrorAction SilentlyContinue) $remoteAccessReady = (Get-Service TermService).Status -eq 'Running' -and (Get-Service WinRM).Status -eq 'Running' -and (Get-ItemPropertyValue 'HKLM:\SYSTEM\CurrentControlSet\Control\Terminal Server' -Name fDenyTSConnections) -eq 0 -and $rdpMembers.Name -contains $RemoteDesktopPrincipal if (-not $remoteAccessReady) { $issues.Add('RDP/WinRM or the authorized domain group is not fully configured.') } } $result = [pscustomobject]@{ ComputerName = $env:COMPUTERNAME Domain = $computer.Domain DomainJoined = [bool]$computer.PartOfDomain ProviderRegistered = $providerRegistered ProviderBinary = $registeredDll ProviderBinaryPresent = [bool]$providerBinaryPresent DefaultProviderConfigured = $defaultProviderConfigured PasswordProviderPreserved = $passwordProviderPreserved SettingsPresent = [bool]$settings ClientCertificatePresent = [bool]$clientCertificatePresent ServerCertificateTrusted = $serverCertificateTrusted DotNetRuntimePresent = $dotNetRuntimePresent BrokerHealth = $brokerHealth RemoteAccessReady = $remoteAccessReady IsValid = $issues.Count -eq 0 Issues = $issues.ToArray() } $result if ($Enforce -and -not $result.IsValid) { throw "SGU client enrollment is invalid: $($issues -join ' ')" }