Files
SGU-CredentialProvider/scripts/Invoke-SguClientBootstrap.ps1
T

621 lines
27 KiB
PowerShell

#Requires -Version 5.1
[CmdletBinding(SupportsShouldProcess)]
param(
[ipaddress]$DomainControllerIPv4Address,
[string]$NetworkInterfaceAlias,
[ipaddress]$ClientIPv4Address,
[ValidateRange(1, 32)]
[int]$ClientPrefixLength = 24,
[PSCredential]$DomainCredential,
[string]$DomainName = 'lci.lasalle.mx',
[string]$DomainNetbios = 'LCI',
[string]$ComputerOuDn,
[string]$NewComputerName,
[ValidateSet('Direct', 'AzureP2S')]
[string]$ConnectivityMode = 'Direct',
[string]$VpnConnectionName = 'SGU Azure P2S',
[string]$VpnProfilePackagePath,
[string]$VpnClientCertificatePfxPath,
[securestring]$VpnClientCertificatePfxPassword,
[string]$VpnClientRootCertificatePath,
[string[]]$AzureNetworkPrefixes = @('10.77.0.0/16'),
[switch]$PauseOnError,
[switch]$SkipRestart
)
$ErrorActionPreference = 'Stop'
$brokerRecordName = 'sgu-auth'
$brokerDnsName = "$brokerRecordName.$DomainName"
$brokerEndpoint = "https://${brokerDnsName}:8443/v1/authenticate"
$temporaryRoot = Join-Path $env:ProgramData ("SGU\Bootstrap\Client-" + [Guid]::NewGuid().ToString('N'))
$bootstrapLogRoot = Join-Path $env:ProgramData 'SGU\Bootstrap\Client'
$bootstrapErrorLog = Join-Path $bootstrapLogRoot 'latest-error.log'
trap {
$failure = $_
$failureText = @(
"SGU client enrollment failed at $((Get-Date).ToString('s')).",
'',
$failure.Exception.Message,
'',
$failure.ScriptStackTrace
) -join [Environment]::NewLine
try {
New-Item -ItemType Directory -Path $bootstrapLogRoot -Force | Out-Null
[IO.File]::WriteAllText($bootstrapErrorLog, $failureText, [Text.UTF8Encoding]::new($false))
}
catch {
# Keep the original enrollment error when diagnostics cannot be written.
}
Write-Host ''
Write-Host 'SGU client enrollment did not complete.' -ForegroundColor Red
Write-Host $failure.Exception.Message -ForegroundColor Red
Write-Host "Diagnostic log: $bootstrapErrorLog" -ForegroundColor Yellow
if ($PauseOnError -and [Environment]::UserInteractive) {
Read-Host 'Press ENTER to close this window' | Out-Null
}
exit 1
}
function Assert-Administrator {
$identity = [Security.Principal.WindowsIdentity]::GetCurrent()
$principal = [Security.Principal.WindowsPrincipal]::new($identity)
if (-not $principal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) {
throw 'Run this bootstrap from an elevated Windows PowerShell session.'
}
}
function Assert-PackageManifest {
param([Parameter(Mandatory)][string]$PackageRoot)
$manifestPath = Join-Path $PackageRoot 'package-manifest.json'
if (-not (Test-Path -LiteralPath $manifestPath -PathType Leaf)) {
throw 'package-manifest.json is missing. Use the complete SGU client bootstrap release.'
}
$manifest = Get-Content -LiteralPath $manifestPath -Raw | ConvertFrom-Json
foreach ($entry in $manifest.Files) {
$path = Join-Path $PackageRoot ([string]$entry.Path)
if (-not (Test-Path -LiteralPath $path -PathType Leaf)) {
throw "Bootstrap package file is missing: $($entry.Path)"
}
$actual = (Get-FileHash -LiteralPath $path -Algorithm SHA256).Hash
if ($actual -ne [string]$entry.Sha256) {
throw "Bootstrap package integrity check failed: $($entry.Path)"
}
}
}
function Resolve-ClientInterfaceAlias {
param([string]$RequestedAlias)
if ($RequestedAlias) {
Get-NetAdapter -Name $RequestedAlias -ErrorAction Stop | Out-Null
return $RequestedAlias
}
$upAdapters = @(Get-NetAdapter | Where-Object Status -eq 'Up')
$withoutDefaultGateway = @($upAdapters | Where-Object {
-not (Get-NetIPConfiguration -InterfaceIndex $_.ifIndex).IPv4DefaultGateway
})
if ($withoutDefaultGateway.Count -eq 1) {
return [string]$withoutDefaultGateway[0].Name
}
if ($upAdapters.Count -eq 1) {
return [string]$upAdapters[0].Name
}
$aliases = ($upAdapters.Name | Sort-Object) -join ', '
throw "Could not select the private domain adapter unambiguously. Re-run with -NetworkInterfaceAlias. Available adapters: $aliases"
}
function Test-IPv4AddressesSharePrefix {
param(
[Parameter(Mandatory)][ipaddress]$FirstAddress,
[Parameter(Mandatory)][ipaddress]$SecondAddress,
[Parameter(Mandatory)][ValidateRange(1, 32)][int]$PrefixLength
)
if ($FirstAddress.AddressFamily -ne [Net.Sockets.AddressFamily]::InterNetwork -or
$SecondAddress.AddressFamily -ne [Net.Sockets.AddressFamily]::InterNetwork) {
return $false
}
$firstBytes = $FirstAddress.GetAddressBytes()
$secondBytes = $SecondAddress.GetAddressBytes()
$remainingBits = $PrefixLength
for ($index = 0; $index -lt 4; $index++) {
$bits = [Math]::Min(8, $remainingBits)
$mask = if ($bits -eq 0) {
0
}
elseif ($bits -eq 8) {
255
}
else {
256 - [int][Math]::Pow(2, 8 - $bits)
}
if (($firstBytes[$index] -band $mask) -ne ($secondBytes[$index] -band $mask)) {
return $false
}
$remainingBits -= $bits
}
return $true
}
function Assert-UsableClientIPv4Address {
param(
[Parameter(Mandatory)][ipaddress]$Address,
[Parameter(Mandatory)][ipaddress]$DomainControllerAddress,
[Parameter(Mandatory)][ValidateRange(1, 32)][int]$PrefixLength
)
if ($Address.AddressFamily -ne [Net.Sockets.AddressFamily]::InterNetwork) {
throw "The SGU client address '$Address' must be IPv4."
}
if ($Address.IPAddressToString -eq $DomainControllerAddress.IPAddressToString) {
throw 'The SGU client and domain controller cannot use the same IPv4 address.'
}
if ($Address.IPAddressToString -match '^(0\.|127\.|169\.254\.|22[4-9]\.|23\d\.)') {
throw "The SGU client address '$Address' is not usable on the private domain network."
}
if (-not (Test-IPv4AddressesSharePrefix -FirstAddress $Address `
-SecondAddress $DomainControllerAddress -PrefixLength $PrefixLength)) {
throw "The SGU client address '$Address/$PrefixLength' is not on the same network as domain controller $DomainControllerAddress."
}
}
function Set-ClientDomainAddress {
param(
[Parameter(Mandatory)][string]$InterfaceAlias,
[Parameter(Mandatory)][ipaddress]$DomainControllerAddress,
[ipaddress]$RequestedAddress,
[Parameter(Mandatory)][ValidateRange(1, 32)][int]$PrefixLength
)
$adapter = Get-NetAdapter -Name $InterfaceAlias -ErrorAction Stop
$matchingAddress = Get-NetIPAddress -InterfaceIndex $adapter.ifIndex -AddressFamily IPv4 `
-ErrorAction SilentlyContinue |
Where-Object {
$_.AddressState -eq 'Preferred' -and
$_.IPAddress -notmatch '^(127\.|169\.254\.)' -and
(Test-IPv4AddressesSharePrefix -FirstAddress ([ipaddress]$_.IPAddress) `
-SecondAddress $DomainControllerAddress -PrefixLength $PrefixLength)
} |
Select-Object -First 1
if (-not $RequestedAddress -and $matchingAddress) {
return [ipaddress]$matchingAddress.IPAddress
}
if (-not $RequestedAddress) {
$RequestedAddress = [ipaddress](Read-Host "Fixed IPv4 address for this SGU client on '$InterfaceAlias'")
}
Assert-UsableClientIPv4Address -Address $RequestedAddress `
-DomainControllerAddress $DomainControllerAddress -PrefixLength $PrefixLength
Set-NetIPInterface -InterfaceIndex $adapter.ifIndex -AddressFamily IPv4 -Dhcp Disabled
$existingAddresses = @(Get-NetIPAddress -InterfaceIndex $adapter.ifIndex -AddressFamily IPv4 `
-ErrorAction SilentlyContinue | Where-Object PrefixOrigin -ne 'WellKnown')
foreach ($existingAddress in $existingAddresses) {
if ($existingAddress.IPAddress -ne $RequestedAddress.IPAddressToString -or
[int]$existingAddress.PrefixLength -ne $PrefixLength) {
Remove-NetIPAddress -InputObject $existingAddress -Confirm:$false
}
}
if (-not (Get-NetIPAddress -InterfaceIndex $adapter.ifIndex -AddressFamily IPv4 `
-IPAddress $RequestedAddress.IPAddressToString -ErrorAction SilentlyContinue)) {
New-NetIPAddress -InterfaceIndex $adapter.ifIndex -AddressFamily IPv4 `
-IPAddress $RequestedAddress.IPAddressToString -PrefixLength $PrefixLength | Out-Null
}
$addressReadyDeadline = (Get-Date).AddSeconds(20)
do {
$configuredAddress = Get-NetIPAddress -InterfaceIndex $adapter.ifIndex `
-AddressFamily IPv4 -IPAddress $RequestedAddress.IPAddressToString `
-ErrorAction SilentlyContinue
if ($configuredAddress -and $configuredAddress.AddressState -eq 'Preferred') {
return $RequestedAddress
}
Start-Sleep -Milliseconds 500
} while ((Get-Date) -lt $addressReadyDeadline)
$observedState = if ($configuredAddress) { $configuredAddress.AddressState } else { 'Missing' }
throw "The SGU client address '$RequestedAddress' did not become ready on '$InterfaceAlias' within 20 seconds. Observed state: $observedState."
}
function Test-TcpPort {
param(
[Parameter(Mandatory)][ipaddress]$Address,
[Parameter(Mandatory)][int]$Port,
[int]$TimeoutMilliseconds = 5000
)
$client = [Net.Sockets.TcpClient]::new()
try {
$connect = $client.BeginConnect($Address, $Port, $null, $null)
if (-not $connect.AsyncWaitHandle.WaitOne($TimeoutMilliseconds)) {
return $false
}
$client.EndConnect($connect)
return $true
}
catch {
return $false
}
finally {
$client.Dispose()
}
}
function Wait-TcpPort {
param(
[Parameter(Mandatory)][ipaddress]$Address,
[Parameter(Mandatory)][int]$Port,
[int]$TimeoutSeconds = 20
)
$deadline = (Get-Date).AddSeconds($TimeoutSeconds)
do {
if (Test-TcpPort -Address $Address -Port $Port -TimeoutMilliseconds 2000) {
return $true
}
Start-Sleep -Milliseconds 750
} while ((Get-Date) -lt $deadline)
return $false
}
function Connect-SguAzureP2s {
param([Parameter(Mandatory)][string]$ConnectionName)
$connection = Get-VpnConnection -Name $ConnectionName -AllUserConnection `
-ErrorAction SilentlyContinue
if (-not $connection) {
throw "The all-user VPN connection '$ConnectionName' is not installed. Run Install-SguAzureP2sClient.ps1 in this VM first."
}
if ($connection.TunnelType -notcontains 'Ikev2' -and $connection.TunnelType -ne 'Ikev2') {
throw "The VPN connection '$ConnectionName' is not configured for IKEv2."
}
if ($connection.ConnectionStatus -ne 'Connected') {
& "$env:SystemRoot\System32\rasdial.exe" $ConnectionName
if ($LASTEXITCODE -ne 0) {
throw "Could not connect the Azure P2S profile '$ConnectionName'. Verify the machine certificate and that UDP 500/4500 is permitted by the local network."
}
}
$connection = Get-VpnConnection -Name $ConnectionName -AllUserConnection
if ($connection.ConnectionStatus -ne 'Connected') {
throw "The Azure P2S profile '$ConnectionName' did not reach Connected state."
}
return $connection
}
Assert-Administrator
$operatingSystem = Get-CimInstance Win32_OperatingSystem
if ([int]$operatingSystem.ProductType -ne 1) {
throw 'The client bootstrap supports Windows 10/11 workstations. Use the server bootstrap on Windows Server.'
}
$edition = (Get-WindowsEdition -Online).Edition
if ($edition -match '^Core' -or $edition -match 'Home') {
throw "Windows edition '$edition' cannot join an on-premises Active Directory domain or host RDP. Upgrade to Pro, Enterprise, or Education, then run this same bootstrap again."
}
if (-not $DomainControllerIPv4Address) {
$DomainControllerIPv4Address = [ipaddress](Read-Host 'Fixed IPv4 address of the SGU domain controller')
}
if (-not $ComputerOuDn) {
$baseDn = (($DomainName -split '\.') | ForEach-Object { "DC=$_" }) -join ','
$ComputerOuDn = "OU=Laboratorio,$baseDn"
}
$packageRoot = $PSScriptRoot
Assert-PackageManifest -PackageRoot $packageRoot
$scriptsRoot = Join-Path $packageRoot 'payload\scripts'
$providerPublishPath = Join-Path $packageRoot 'payload\credential-provider'
$runtimeInstaller = Get-ChildItem (Join-Path $packageRoot 'payload\prerequisites') `
-Filter '*x64*.exe' -File -ErrorAction SilentlyContinue |
Sort-Object Name -Descending |
Select-Object -First 1
foreach ($requiredPath in @(
(Join-Path $scriptsRoot 'Enroll-SguDomainClient.ps1'),
(Join-Path $scriptsRoot 'Install-SguRustDeskClient.ps1'),
(Join-Path $scriptsRoot 'Register-SguClientCertificate.ps1'),
(Join-Path $providerPublishPath 'SGU.CredentialProvider.comhost.dll'))) {
if (-not (Test-Path -LiteralPath $requiredPath -PathType Leaf)) {
throw "The client bootstrap package is incomplete: $requiredPath"
}
}
if (-not $runtimeInstaller) {
throw 'The offline Microsoft .NET 10 x64 runtime installer is missing from the client package.'
}
if ($ConnectivityMode -eq 'AzureP2S') {
$existingVpnConnection = Get-VpnConnection -Name $VpnConnectionName -AllUserConnection `
-ErrorAction SilentlyContinue
if (-not $existingVpnConnection) {
$installerPath = Join-Path $packageRoot 'Install-SguAzureP2sClient.ps1'
if (-not (Test-Path -LiteralPath $installerPath -PathType Leaf)) {
throw 'Install-SguAzureP2sClient.ps1 is missing from the client bootstrap package.'
}
foreach ($vpnInput in @(
@{ Name = 'VpnProfilePackagePath'; Value = $VpnProfilePackagePath },
@{ Name = 'VpnClientCertificatePfxPath'; Value = $VpnClientCertificatePfxPath },
@{ Name = 'VpnClientRootCertificatePath'; Value = $VpnClientRootCertificatePath })) {
if ([string]::IsNullOrWhiteSpace([string]$vpnInput.Value)) {
throw "$($vpnInput.Name) is required the first time an Azure P2S client is enrolled."
}
}
$vpnInstallParameters = @{
VpnProfilePackagePath = $VpnProfilePackagePath
ClientCertificatePfxPath = $VpnClientCertificatePfxPath
ClientRootCertificatePath = $VpnClientRootCertificatePath
ConnectionName = $VpnConnectionName
AzureNetworkPrefixes = $AzureNetworkPrefixes
DomainControllerIPv4Address = $DomainControllerIPv4Address
DomainName = $DomainName
}
if ($VpnClientCertificatePfxPassword) {
$vpnInstallParameters.ClientCertificatePfxPassword = $VpnClientCertificatePfxPassword
}
& $installerPath @vpnInstallParameters | Out-Null
}
$vpnConnection = Connect-SguAzureP2s -ConnectionName $VpnConnectionName
$nrptDisplayName = "SGU Azure P2S DNS - $DomainName"
$nrptRule = Get-DnsClientNrptRule -ErrorAction SilentlyContinue |
Where-Object DisplayName -eq $nrptDisplayName |
Select-Object -First 1
if (-not $nrptRule -or
@($nrptRule.NameServers) -notcontains $DomainControllerIPv4Address.IPAddressToString) {
throw "The SGU NRPT rule for $DomainName is missing or does not point to $DomainControllerIPv4Address. Re-run Install-SguAzureP2sClient.ps1."
}
$NetworkInterfaceAlias = $vpnConnection.Name
}
else {
$NetworkInterfaceAlias = Resolve-ClientInterfaceAlias -RequestedAlias $NetworkInterfaceAlias
$ClientIPv4Address = Set-ClientDomainAddress -InterfaceAlias $NetworkInterfaceAlias `
-DomainControllerAddress $DomainControllerIPv4Address `
-RequestedAddress $ClientIPv4Address -PrefixLength $ClientPrefixLength
Set-DnsClientServerAddress -InterfaceAlias $NetworkInterfaceAlias `
-ServerAddresses $DomainControllerIPv4Address.IPAddressToString
}
if (-not (Wait-TcpPort -Address $DomainControllerIPv4Address -Port 5985 -TimeoutSeconds 20)) {
throw "The domain controller at $DomainControllerIPv4Address did not accept WinRM on TCP 5985 after 20 seconds. Run the server bootstrap first and verify the selected IP."
}
if (-not $DomainCredential) {
$DomainCredential = Get-Credential `
-UserName "$DomainNetbios\Administrator" `
-Message "Credential permitted to enroll this computer in $DomainName"
}
New-Item -ItemType Directory -Path $temporaryRoot -Force | Out-Null
$clientCertificatePath = Join-Path $temporaryRoot 'client.cer'
$serverCertificatePath = Join-Path $temporaryRoot 'server.cer'
$remoteRegistrationScript = $null
$remoteClientCertificate = $null
$session = $null
$winRmWasRunning = (Get-Service WinRM).Status -eq 'Running'
$priorTrustedHosts = $null
try {
if (-not $winRmWasRunning) {
Set-Service WinRM -StartupType Manual
Start-Service WinRM
}
$priorTrustedHosts = [string](Get-Item WSMan:\localhost\Client\TrustedHosts).Value
$trustedHostValues = @($priorTrustedHosts -split ',' | ForEach-Object Trim | Where-Object { $_ })
if ($trustedHostValues -notcontains $DomainControllerIPv4Address.IPAddressToString) {
$trustedHostValues += $DomainControllerIPv4Address.IPAddressToString
Set-Item WSMan:\localhost\Client\TrustedHosts -Value ($trustedHostValues -join ',') -Force
}
$session = New-PSSession `
-ComputerName $DomainControllerIPv4Address.IPAddressToString `
-Credential $DomainCredential `
-Authentication Negotiate
$serverIdentity = Invoke-Command -Session $session -ScriptBlock {
$computer = Get-CimInstance Win32_ComputerSystem
$brokerService = Get-Service SGUAuthBroker -ErrorAction SilentlyContinue
$rustDeskStatusPath = Join-Path $env:ProgramData 'SGU\RustDesk\server.json'
$rustDeskStatus = if (Test-Path -LiteralPath $rustDeskStatusPath -PathType Leaf) {
Get-Content -LiteralPath $rustDeskStatusPath -Raw | ConvertFrom-Json
}
else {
$null
}
$hbbsTask = Get-ScheduledTask -TaskName 'SGU-RustDesk-hbbs' -ErrorAction SilentlyContinue
$hbbrTask = Get-ScheduledTask -TaskName 'SGU-RustDesk-hbbr' -ErrorAction SilentlyContinue
[pscustomobject]@{
ComputerName = $env:COMPUTERNAME
Domain = $computer.Domain
BrokerService = if ($brokerService) { $brokerService.Status.ToString() } else { 'Missing' }
RustDeskServerAddress = if ($rustDeskStatus) { [string]$rustDeskStatus.ServerAddress } else { $null }
RustDeskPublicKey = if ($rustDeskStatus) { [string]$rustDeskStatus.PublicKey } else { $null }
RustDeskHbbsTask = if ($hbbsTask) { $hbbsTask.State.ToString() } else { 'Missing' }
RustDeskHbbrTask = if ($hbbrTask) { $hbbrTask.State.ToString() } else { 'Missing' }
}
}
if (-not $serverIdentity.Domain -or
-not $serverIdentity.Domain.Equals($DomainName, [StringComparison]::OrdinalIgnoreCase)) {
throw "The server at $DomainControllerIPv4Address belongs to $($serverIdentity.Domain), not $DomainName."
}
if ($serverIdentity.BrokerService -ne 'Running') {
throw "The SGU Authentication Broker is not running on $($serverIdentity.ComputerName)."
}
if ([string]::IsNullOrWhiteSpace($serverIdentity.RustDeskServerAddress) -or
[string]::IsNullOrWhiteSpace($serverIdentity.RustDeskPublicKey) -or
$serverIdentity.RustDeskHbbsTask -ne 'Running' -or
$serverIdentity.RustDeskHbbrTask -ne 'Running') {
throw "The RustDesk server is not ready on $($serverIdentity.ComputerName). Run the current server bootstrap first."
}
$certificateSubject = "CN=SGU Credential Provider Client $env:COMPUTERNAME"
$clientCertificate = Get-ChildItem Cert:\LocalMachine\My |
Where-Object {
$_.Subject -eq $certificateSubject -and
$_.HasPrivateKey -and
$_.NotAfter -gt (Get-Date).AddDays(30)
} |
Sort-Object NotAfter -Descending |
Select-Object -First 1
if (-not $clientCertificate) {
$clientCertificate = New-SelfSignedCertificate `
-Subject $certificateSubject `
-CertStoreLocation Cert:\LocalMachine\My `
-KeyAlgorithm RSA `
-KeyLength 3072 `
-HashAlgorithm SHA256 `
-KeyExportPolicy NonExportable `
-NotAfter (Get-Date).AddYears(2) `
-TextExtension @('2.5.29.37={text}1.3.6.1.5.5.7.3.2')
}
Export-Certificate -Cert $clientCertificate -FilePath $clientCertificatePath -Force | Out-Null
if (-not (Get-ChildItem Cert:\LocalMachine\Root | Where-Object Thumbprint -eq $clientCertificate.Thumbprint)) {
Import-Certificate -FilePath $clientCertificatePath `
-CertStoreLocation Cert:\LocalMachine\Root | Out-Null
}
$remoteTemporaryRoot = Invoke-Command -Session $session -ScriptBlock {
$path = Join-Path $env:ProgramData ("SGU\Enrollment\Incoming-" + [Guid]::NewGuid().ToString('N'))
New-Item -ItemType Directory -Path $path -Force | Out-Null
$path
}
$remoteRegistrationScript = Join-Path $remoteTemporaryRoot 'Register-SguClientCertificate.ps1'
$remoteClientCertificate = Join-Path $remoteTemporaryRoot 'client.cer'
Copy-Item -LiteralPath (Join-Path $scriptsRoot 'Register-SguClientCertificate.ps1') `
-Destination $remoteRegistrationScript -ToSession $session
Copy-Item -LiteralPath $clientCertificatePath `
-Destination $remoteClientCertificate -ToSession $session
Invoke-Command -Session $session -ScriptBlock {
param($RegistrationScript, $CertificatePath)
& $RegistrationScript -CertificatePath $CertificatePath | Out-Null
} -ArgumentList $remoteRegistrationScript,$remoteClientCertificate
$serverCertificateBase64 = Invoke-Command -Session $session -ScriptBlock {
param($ExpectedSubject)
$certificate = Get-ChildItem Cert:\LocalMachine\My |
Where-Object {
$_.Subject -eq "CN=$ExpectedSubject" -and
$_.HasPrivateKey -and
$_.NotAfter -gt (Get-Date)
} |
Sort-Object NotAfter -Descending |
Select-Object -First 1
if (-not $certificate) {
throw "The broker certificate for $ExpectedSubject is missing."
}
[Convert]::ToBase64String($certificate.RawData)
} -ArgumentList $brokerDnsName
[IO.File]::WriteAllBytes($serverCertificatePath, [Convert]::FromBase64String($serverCertificateBase64))
$serverCertificate = [Security.Cryptography.X509Certificates.X509Certificate2]::new($serverCertificatePath)
Import-Certificate -FilePath $serverCertificatePath `
-CertStoreLocation Cert:\LocalMachine\Root | Out-Null
Resolve-DnsName -Type SRV "_ldap._tcp.dc._msdcs.$DomainName" -ErrorAction Stop | Out-Null
$brokerAddress = Resolve-DnsName -Name $brokerDnsName -Type A -ErrorAction Stop |
Where-Object IPAddress -eq $DomainControllerIPv4Address.IPAddressToString
if (-not $brokerAddress) {
throw "$brokerDnsName does not resolve to $DomainControllerIPv4Address."
}
$enrollmentParameters = @{
PublishPath = $providerPublishPath
BrokerEndpoint = $brokerEndpoint
ClientCertificateThumbprint = $clientCertificate.Thumbprint
ServerCertificateThumbprint = $serverCertificate.Thumbprint
DomainCredential = $DomainCredential
DomainName = $DomainName
DomainNetbios = $DomainNetbios
ComputerOuDn = $ComputerOuDn
NetworkInterfaceAlias = $NetworkInterfaceAlias
DomainDnsServerAddresses = @($DomainControllerIPv4Address.IPAddressToString)
ConnectivityMode = $ConnectivityMode
RemoteDesktopPrincipal = "$DomainNetbios\SG-Laboratorio-Usuarios-RDP"
DotNetRuntimeInstallerPath = $runtimeInstaller.FullName
RustDeskServerAddress = $serverIdentity.RustDeskServerAddress
RustDeskServerPublicKey = $serverIdentity.RustDeskPublicKey
SkipRestart = $true
}
if ($NewComputerName) {
$enrollmentParameters.NewComputerName = $NewComputerName
}
$result = & (Join-Path $scriptsRoot 'Enroll-SguDomainClient.ps1') @enrollmentParameters
$rustDeskEnrollment = $result.RustDesk
if (-not $rustDeskEnrollment -or -not $rustDeskEnrollment.RustDeskId -or
-not $rustDeskEnrollment.AccessPassword) {
throw 'The client RustDesk enrollment did not provide an ID and protected access credential.'
}
$rustDeskPasswordPointer = [Runtime.InteropServices.Marshal]::SecureStringToBSTR(
$rustDeskEnrollment.AccessPassword)
try {
$rustDeskPassword = [Runtime.InteropServices.Marshal]::PtrToStringBSTR($rustDeskPasswordPointer)
Invoke-Command -Session $session -ScriptBlock {
param($ComputerName, $RustDeskId, $AccessPassword)
$registrationScript = Join-Path $env:ProgramData 'SGU\RustDesk\Register-SguRustDeskDevice.ps1'
if (-not (Test-Path -LiteralPath $registrationScript -PathType Leaf)) {
throw 'The RustDesk device-registration script is missing on the domain controller.'
}
& $registrationScript -ComputerName $ComputerName -RustDeskId $RustDeskId `
-AccessPassword $AccessPassword | Out-Null
} -ArgumentList $env:COMPUTERNAME,$rustDeskEnrollment.RustDeskId,$rustDeskPassword
}
finally {
[Runtime.InteropServices.Marshal]::ZeroFreeBSTR($rustDeskPasswordPointer)
$rustDeskPassword = $null
}
$rustDeskEnrollment.PSObject.Properties.Remove('AccessPassword')
}
finally {
if ($session) {
if ($remoteRegistrationScript -or $remoteClientCertificate) {
Invoke-Command -Session $session -ScriptBlock {
param($Paths)
foreach ($path in $Paths) {
if ($path) {
Remove-Item -LiteralPath $path -Force -ErrorAction SilentlyContinue
}
}
if ($Paths.Count -gt 0 -and $Paths[0]) {
Remove-Item -LiteralPath (Split-Path $Paths[0] -Parent) `
-Force -ErrorAction SilentlyContinue
}
} -ArgumentList (,@($remoteRegistrationScript,$remoteClientCertificate)) `
-ErrorAction SilentlyContinue
}
Remove-PSSession $session
}
if ($null -ne $priorTrustedHosts) {
Set-Item WSMan:\localhost\Client\TrustedHosts -Value $priorTrustedHosts -Force
}
if (-not $winRmWasRunning) {
Stop-Service WinRM -Force -NoWait -WarningAction SilentlyContinue `
-ErrorAction SilentlyContinue
}
Remove-Item -LiteralPath $temporaryRoot -Recurse -Force -ErrorAction SilentlyContinue
$DomainCredential = $null
$VpnClientCertificatePfxPassword = $null
}
if ($SkipRestart) {
[pscustomobject]@{
ComputerName = if ($NewComputerName) { $NewComputerName } else { $env:COMPUTERNAME }
DomainName = $DomainName
ProviderInstalled = $true
ClientCertificateRegistered = $true
BrokerEndpoint = $brokerEndpoint
ConnectivityMode = $ConnectivityMode
VpnConnectionName = if ($ConnectivityMode -eq 'AzureP2S') { $VpnConnectionName } else { $null }
RestartRequired = $true
RustDesk = if ($result) { $result.RustDesk } else { $null }
EnrollmentResult = $result
}
return
}
Restart-Computer -Force