#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 { $_.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 } return $RequestedAddress } 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 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 (Test-TcpPort -Address $DomainControllerIPv4Address -Port 5985)) { throw "The domain controller at $DomainControllerIPv4Address is not accepting WinRM on TCP 5985. 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 -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