Unify Windows client bootstrap and discover network paths
This commit is contained in:
@@ -7,8 +7,8 @@ param(
|
||||
[ValidateRange(1, 32)]
|
||||
[int]$ClientPrefixLength = 24,
|
||||
[PSCredential]$DomainCredential,
|
||||
[string]$DomainName = 'lci.lasalle.mx',
|
||||
[string]$DomainNetbios = 'LCI',
|
||||
[string]$DomainName,
|
||||
[string]$DomainNetbios,
|
||||
[string]$ComputerOuDn,
|
||||
[string]$NewComputerName,
|
||||
[ValidateSet('Auto', 'Windows10Legacy', 'Windows11Modern')]
|
||||
@@ -27,8 +27,6 @@ param(
|
||||
|
||||
$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'
|
||||
@@ -91,33 +89,69 @@ function Assert-PackageManifest {
|
||||
}
|
||||
|
||||
function Resolve-ClientInterfaceAlias {
|
||||
param([string]$RequestedAlias)
|
||||
param(
|
||||
[string]$RequestedAlias,
|
||||
[Parameter(Mandatory)][ipaddress]$DomainControllerAddress
|
||||
)
|
||||
|
||||
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
|
||||
# IP interfaces include tunnel/PPP adapters that Get-NetAdapter can omit.
|
||||
$interfaces = @(Get-NetIPInterface -AddressFamily IPv4 | Where-Object {
|
||||
$_.ConnectionState -eq 'Connected' -and
|
||||
(-not $RequestedAlias -or $_.InterfaceAlias -eq $RequestedAlias)
|
||||
})
|
||||
if ($withoutDefaultGateway.Count -eq 1) {
|
||||
return [string]$withoutDefaultGateway[0].Name
|
||||
$preferred = @(Find-NetRoute -RemoteIPAddress $DomainControllerAddress.IPAddressToString `
|
||||
-ErrorAction SilentlyContinue | Where-Object { $_.PSObject.Properties['IPAddress'] })
|
||||
$attempts = @()
|
||||
$candidates = @(foreach ($interface in $interfaces) {
|
||||
$addresses = @(Get-NetIPAddress -InterfaceIndex $interface.InterfaceIndex -AddressFamily IPv4 `
|
||||
-ErrorAction SilentlyContinue | Where-Object {
|
||||
$_.AddressState -eq 'Preferred' -and -not $_.SkipAsSource -and
|
||||
$_.IPAddress -notmatch '^(0\.|127\.|169\.254\.)'
|
||||
})
|
||||
if ($addresses.Count -eq 0) {
|
||||
$attempts += "$($interface.InterfaceAlias): no usable IPv4 address (check DHCP/static configuration)"
|
||||
continue
|
||||
}
|
||||
$route = Get-NetRoute -InterfaceIndex $interface.InterfaceIndex -AddressFamily IPv4 `
|
||||
-PolicyStore ActiveStore -ErrorAction SilentlyContinue | Where-Object {
|
||||
$parts = $_.DestinationPrefix -split '/'
|
||||
Test-IPv4AddressesSharePrefix -FirstAddress $DomainControllerAddress `
|
||||
-SecondAddress ([ipaddress]$parts[0]) -PrefixLength ([int]$parts[1])
|
||||
} | Sort-Object @{ Expression = { [int]($_.DestinationPrefix -split '/')[1] }; Descending = $true },
|
||||
RouteMetric | Select-Object -First 1
|
||||
if ($route) {
|
||||
foreach ($address in $addresses) {
|
||||
[pscustomobject]@{
|
||||
InterfaceAlias = $interface.InterfaceAlias
|
||||
InterfaceIndex = [int]$interface.InterfaceIndex
|
||||
IPAddress = $address.IPAddress
|
||||
NextHop = $route.NextHop
|
||||
Preferred = @($preferred | Where-Object IPAddress -eq $address.IPAddress).Count -gt 0
|
||||
PrefixLength = [int]($route.DestinationPrefix -split '/')[1]
|
||||
Metric = [int]$route.RouteMetric + [int]$interface.InterfaceMetric
|
||||
}
|
||||
}
|
||||
}
|
||||
else { $attempts += "$($interface.InterfaceAlias): no route to $DomainControllerAddress" }
|
||||
})
|
||||
if ($interfaces.Count -eq 0) { $attempts += 'No matching connected IPv4 interface' }
|
||||
foreach ($candidate in ($candidates | Sort-Object @{ Expression = { $_.Preferred }; Descending = $true },
|
||||
@{ Expression = { $_.PrefixLength }; Descending = $true }, Metric, InterfaceIndex, IPAddress)) {
|
||||
Write-Host "Checking $($candidate.InterfaceAlias) ($($candidate.IPAddress)) -> $DomainControllerAddress..."
|
||||
if (Test-TcpPort -Address $DomainControllerAddress -Port 5985 -TimeoutMilliseconds 2000 `
|
||||
-SourceAddress ([ipaddress]$candidate.IPAddress) -InterfaceIndex $candidate.InterfaceIndex) {
|
||||
return $candidate
|
||||
}
|
||||
$attempts += "$($candidate.InterfaceAlias) [$($candidate.IPAddress), next hop $($candidate.NextHop)]: TCP 5985 unavailable"
|
||||
}
|
||||
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"
|
||||
throw "Cannot reach SGU server $DomainControllerAddress. $($attempts -join '; '). Check the LAN/VPN connection, DHCP or an administrator-assigned IP, routes and the server WinRM firewall. No client IP was changed."
|
||||
}
|
||||
|
||||
function Test-IPv4AddressesSharePrefix {
|
||||
param(
|
||||
[Parameter(Mandatory)][ipaddress]$FirstAddress,
|
||||
[Parameter(Mandatory)][ipaddress]$SecondAddress,
|
||||
[Parameter(Mandatory)][ValidateRange(1, 32)][int]$PrefixLength
|
||||
[Parameter(Mandatory)][ValidateRange(0, 32)][int]$PrefixLength
|
||||
)
|
||||
|
||||
if ($FirstAddress.AddressFamily -ne [Net.Sockets.AddressFamily]::InterNetwork -or
|
||||
@@ -147,6 +181,27 @@ function Test-IPv4AddressesSharePrefix {
|
||||
return $true
|
||||
}
|
||||
|
||||
function Wait-ClientInterface {
|
||||
param(
|
||||
[string]$RequestedAlias,
|
||||
[Parameter(Mandatory)][ipaddress]$DomainControllerAddress,
|
||||
[int]$TimeoutSeconds = 20
|
||||
)
|
||||
$deadline = (Get-Date).AddSeconds($TimeoutSeconds)
|
||||
do {
|
||||
try {
|
||||
return Resolve-ClientInterfaceAlias -RequestedAlias $RequestedAlias `
|
||||
-DomainControllerAddress $DomainControllerAddress
|
||||
}
|
||||
catch {
|
||||
$lastFailure = $_
|
||||
if ((Get-Date) -ge $deadline) { throw $lastFailure }
|
||||
Write-Host 'Waiting for DHCP, VPN routes or server connectivity to become ready...'
|
||||
Start-Sleep -Seconds 2
|
||||
}
|
||||
} while ($true)
|
||||
}
|
||||
|
||||
function Assert-UsableClientIPv4Address {
|
||||
param(
|
||||
[Parameter(Mandatory)][ipaddress]$Address,
|
||||
@@ -192,7 +247,7 @@ function Set-ClientDomainAddress {
|
||||
return [ipaddress]$matchingAddress.IPAddress
|
||||
}
|
||||
if (-not $RequestedAddress) {
|
||||
$RequestedAddress = [ipaddress](Read-Host "Fixed IPv4 address for this SGU client on '$InterfaceAlias'")
|
||||
throw 'Static addressing requires an explicit -ClientIPv4Address. Automatic enrollment preserves DHCP and existing addresses.'
|
||||
}
|
||||
Assert-UsableClientIPv4Address -Address $RequestedAddress `
|
||||
-DomainControllerAddress $DomainControllerAddress -PrefixLength $PrefixLength
|
||||
@@ -231,11 +286,22 @@ function Test-TcpPort {
|
||||
param(
|
||||
[Parameter(Mandatory)][ipaddress]$Address,
|
||||
[Parameter(Mandatory)][int]$Port,
|
||||
[int]$TimeoutMilliseconds = 5000
|
||||
[int]$TimeoutMilliseconds = 5000,
|
||||
[ipaddress]$SourceAddress,
|
||||
[int]$InterfaceIndex
|
||||
)
|
||||
|
||||
$client = [Net.Sockets.TcpClient]::new()
|
||||
$client = [Net.Sockets.TcpClient]::new([Net.Sockets.AddressFamily]::InterNetwork)
|
||||
$connect = $null
|
||||
try {
|
||||
if ($InterfaceIndex) {
|
||||
# IP_UNICAST_IF (31) expects the interface index in network byte order.
|
||||
$client.Client.SetSocketOption([Net.Sockets.SocketOptionLevel]::IP,
|
||||
[Net.Sockets.SocketOptionName]31, [Net.IPAddress]::HostToNetworkOrder($InterfaceIndex))
|
||||
}
|
||||
if ($SourceAddress) {
|
||||
$client.Client.Bind([Net.IPEndPoint]::new($SourceAddress, 0))
|
||||
}
|
||||
$connect = $client.BeginConnect($Address, $Port, $null, $null)
|
||||
if (-not $connect.AsyncWaitHandle.WaitOne($TimeoutMilliseconds)) {
|
||||
return $false
|
||||
@@ -248,6 +314,61 @@ function Test-TcpPort {
|
||||
}
|
||||
finally {
|
||||
$client.Dispose()
|
||||
if ($connect) { $connect.AsyncWaitHandle.Close() }
|
||||
}
|
||||
}
|
||||
|
||||
function Set-ClientServerRoute {
|
||||
param(
|
||||
[Parameter(Mandatory)]$SelectedInterface,
|
||||
[Parameter(Mandatory)][ipaddress]$DomainControllerAddress
|
||||
)
|
||||
$current = @(Find-NetRoute -RemoteIPAddress $DomainControllerAddress.IPAddressToString |
|
||||
Where-Object { $_.PSObject.Properties['IPAddress'] })
|
||||
if (@($current | Where-Object InterfaceIndex -eq $SelectedInterface.InterfaceIndex).Count -gt 0) { return }
|
||||
|
||||
# Only pin this server when Windows currently chooses a different interface.
|
||||
# Do not replace default routes or change interface metrics used by Internet traffic.
|
||||
$route = New-NetRoute -DestinationPrefix "$DomainControllerAddress/32" `
|
||||
-InterfaceIndex $SelectedInterface.InterfaceIndex -NextHop $SelectedInterface.NextHop `
|
||||
-RouteMetric 1
|
||||
$current = @(Find-NetRoute -RemoteIPAddress $DomainControllerAddress.IPAddressToString |
|
||||
Where-Object { $_.PSObject.Properties['IPAddress'] })
|
||||
if (@($current | Where-Object InterfaceIndex -eq $SelectedInterface.InterfaceIndex).Count -eq 0) {
|
||||
$route | Remove-NetRoute -Confirm:$false
|
||||
throw "Windows still routes $DomainControllerAddress through another interface. Resolve conflicting host routes or VPN policies and retry."
|
||||
}
|
||||
}
|
||||
|
||||
function Set-ClientDomainDns {
|
||||
param(
|
||||
[Parameter(Mandatory)][string]$DnsDomain,
|
||||
[Parameter(Mandatory)][ipaddress]$ServerAddress
|
||||
)
|
||||
$displayName = "SGU domain DNS - $DnsDomain"
|
||||
$existing = @(Get-DnsClientNrptRule -ErrorAction Stop | Where-Object DisplayName -eq $displayName)
|
||||
if ($existing.Count -eq 1 -and @($existing[0].NameServers) -contains $ServerAddress.IPAddressToString -and
|
||||
@($existing[0].Namespace) -contains ".$DnsDomain" -and @($existing[0].Namespace) -contains $DnsDomain) { return }
|
||||
$existing | Remove-DnsClientNrptRule -Force
|
||||
Add-DnsClientNrptRule -Namespace @($DnsDomain, ".$DnsDomain") `
|
||||
-NameServers $ServerAddress.IPAddressToString -DisplayName $displayName | Out-Null
|
||||
Clear-DnsClientCache
|
||||
}
|
||||
|
||||
function Assert-ClientOperatingSystem {
|
||||
param(
|
||||
[Parameter(Mandatory)]$OperatingSystem,
|
||||
[Parameter(Mandatory)][string]$Edition,
|
||||
[Parameter(Mandatory)][string]$Architecture
|
||||
)
|
||||
if ([int]$OperatingSystem.ProductType -ne 1) {
|
||||
throw 'The client bootstrap supports Windows 10/11 workstations. Use the server bootstrap on Windows Server.'
|
||||
}
|
||||
if ([int]$OperatingSystem.BuildNumber -lt 14393 -or $Architecture -ne 'AMD64') {
|
||||
throw 'This package requires Windows 10 1607 or later, or Windows 11, running x64 Windows PowerShell.'
|
||||
}
|
||||
if ($Edition -match '^Core' -or $Edition -match 'Home') {
|
||||
throw "Windows edition '$Edition' cannot join an Active Directory domain. Upgrade to Pro, Enterprise, or Education, then run this same bootstrap again."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -294,56 +415,28 @@ function Connect-SguAzureP2s {
|
||||
|
||||
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."
|
||||
}
|
||||
Assert-ClientOperatingSystem -OperatingSystem $operatingSystem -Edition $edition `
|
||||
-Architecture $env:PROCESSOR_ARCHITECTURE
|
||||
$windowsBuild = [int]$operatingSystem.BuildNumber
|
||||
$windowsName = if ($windowsBuild -ge 22000) { 'Windows 11' } else { 'Windows 10' }
|
||||
Write-Host "$windowsName (build $windowsBuild): unified SGU enrollment."
|
||||
|
||||
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"
|
||||
if ($DomainControllerIPv4Address.AddressFamily -ne [Net.Sockets.AddressFamily]::InterNetwork -or
|
||||
$DomainControllerIPv4Address.IPAddressToString -match '^(0\.|127\.|169\.254\.|22[4-9]\.|23\d\.|24\d\.|25[0-5]\.)') {
|
||||
throw 'Enter a reachable unicast IPv4 address for the domain controller.'
|
||||
}
|
||||
|
||||
$packageRoot = $PSScriptRoot
|
||||
$packageManifest = Assert-PackageManifest -PackageRoot $packageRoot
|
||||
$manifestProfile = if ($packageManifest.PSObject.Properties['CompatibilityProfile']) {
|
||||
[string]$packageManifest.CompatibilityProfile
|
||||
}
|
||||
else {
|
||||
'Auto'
|
||||
}
|
||||
if ($CompatibilityProfile -ne 'Auto' -and $manifestProfile -ne 'Auto' -and
|
||||
$CompatibilityProfile -ne $manifestProfile) {
|
||||
throw "The requested compatibility profile '$CompatibilityProfile' does not match package profile '$manifestProfile'."
|
||||
}
|
||||
if ($CompatibilityProfile -eq 'Auto') {
|
||||
$CompatibilityProfile = $manifestProfile
|
||||
}
|
||||
$windowsBuild = [int]$operatingSystem.BuildNumber
|
||||
if ($CompatibilityProfile -eq 'Auto') {
|
||||
$CompatibilityProfile = if ($windowsBuild -lt 22000) {
|
||||
'Windows10Legacy'
|
||||
}
|
||||
else {
|
||||
'Windows11Modern'
|
||||
}
|
||||
}
|
||||
if ($CompatibilityProfile -eq 'Windows10Legacy' -and $windowsBuild -ge 22000) {
|
||||
throw "The Windows 10 legacy package cannot enroll Windows build $windowsBuild. Use the Windows 11 modern client package."
|
||||
}
|
||||
if ($CompatibilityProfile -eq 'Windows11Modern' -and $windowsBuild -lt 22000) {
|
||||
throw "The Windows 11 modern package cannot enroll Windows build $windowsBuild. Use the Windows 10 legacy client package."
|
||||
}
|
||||
if ($CompatibilityProfile -eq 'Windows10Legacy' -and $ConnectivityMode -eq 'AzureP2S') {
|
||||
throw 'Azure P2S pre-logon enrollment belongs to the Windows 11 modern package. Use Direct connectivity for the Windows 10 legacy package.'
|
||||
# Retain the old parameter for existing automation; neither name restricts networking.
|
||||
if ($CompatibilityProfile -ne 'Auto') {
|
||||
Write-Warning 'CompatibilityProfile is deprecated. This package uses the same implementation on Windows 10 and 11.'
|
||||
}
|
||||
$CompatibilityProfile = 'Auto'
|
||||
$scriptsRoot = Join-Path $packageRoot 'payload\scripts'
|
||||
$providerPublishPath = Join-Path $packageRoot 'payload\credential-provider'
|
||||
$runtimeInstaller = Get-ChildItem (Join-Path $packageRoot 'payload\prerequisites') `
|
||||
@@ -362,6 +455,15 @@ foreach ($requiredPath in @(
|
||||
if (-not $runtimeInstaller) {
|
||||
throw 'The offline Microsoft .NET 10 x64 runtime installer is missing from the client package.'
|
||||
}
|
||||
if (-not $PSCmdlet.ShouldProcess($env:COMPUTERNAME, "Enroll with SGU server $DomainControllerIPv4Address")) { return }
|
||||
if ($ClientIPv4Address) {
|
||||
if (-not $NetworkInterfaceAlias -or $ConnectivityMode -eq 'AzureP2S') {
|
||||
throw 'Explicit static IP setup requires -NetworkInterfaceAlias with Direct connectivity. Omit -ClientIPv4Address to preserve the current LAN/VPN configuration.'
|
||||
}
|
||||
$ClientIPv4Address = Set-ClientDomainAddress -InterfaceAlias $NetworkInterfaceAlias `
|
||||
-DomainControllerAddress $DomainControllerIPv4Address `
|
||||
-RequestedAddress $ClientIPv4Address -PrefixLength $ClientPrefixLength
|
||||
}
|
||||
|
||||
if ($ConnectivityMode -eq 'AzureP2S') {
|
||||
$existingVpnConnection = Get-VpnConnection -Name $VpnConnectionName -AllUserConnection `
|
||||
@@ -394,34 +496,26 @@ if ($ConnectivityMode -eq 'AzureP2S') {
|
||||
& $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
|
||||
}
|
||||
$selectedInterface = Wait-ClientInterface -RequestedAlias $NetworkInterfaceAlias `
|
||||
-DomainControllerAddress $DomainControllerIPv4Address
|
||||
$NetworkInterfaceAlias = $selectedInterface.InterfaceAlias
|
||||
$ClientIPv4Address = [ipaddress]$selectedInterface.IPAddress
|
||||
Set-ClientServerRoute -SelectedInterface $selectedInterface -DomainControllerAddress $DomainControllerIPv4Address
|
||||
Write-Host "Using $NetworkInterfaceAlias ($ClientIPv4Address)."
|
||||
|
||||
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) {
|
||||
$suggestedUser = if ($DomainNetbios) { "$DomainNetbios\Administrator" }
|
||||
elseif ($DomainName) { "Administrator@$DomainName" } else { 'Administrator' }
|
||||
$DomainCredential = Get-Credential `
|
||||
-UserName "$DomainNetbios\Administrator" `
|
||||
-Message "Credential permitted to enroll this computer in $DomainName"
|
||||
-UserName $suggestedUser `
|
||||
-Message "Domain account permitted to enroll this computer (DOMAIN\user or user@domain)"
|
||||
}
|
||||
if (-not $DomainCredential) { throw 'Enrollment cancelled: no domain credential was provided.' }
|
||||
|
||||
New-Item -ItemType Directory -Path $temporaryRoot -Force | Out-Null
|
||||
$clientCertificatePath = Join-Path $temporaryRoot 'client.cer'
|
||||
@@ -451,6 +545,12 @@ try {
|
||||
|
||||
$serverIdentity = Invoke-Command -Session $session -ScriptBlock {
|
||||
$computer = Get-CimInstance Win32_ComputerSystem
|
||||
if ([int]$computer.DomainRole -lt 4) { throw 'The supplied server is not an Active Directory domain controller.' }
|
||||
Import-Module ActiveDirectory -ErrorAction Stop
|
||||
$domain = Get-ADDomain -ErrorAction Stop
|
||||
$labOu = Get-ADOrganizationalUnit -LDAPFilter '(ou=Laboratorio)' `
|
||||
-SearchBase $domain.DistinguishedName -SearchScope OneLevel -ErrorAction Stop |
|
||||
Select-Object -First 1
|
||||
$brokerService = Get-Service SGUAuthBroker -ErrorAction SilentlyContinue
|
||||
$rustDeskStatusPath = Join-Path $env:ProgramData 'SGU\RustDesk\server.json'
|
||||
$rustDeskStatus = if (Test-Path -LiteralPath $rustDeskStatusPath -PathType Leaf) {
|
||||
@@ -464,6 +564,8 @@ try {
|
||||
[pscustomobject]@{
|
||||
ComputerName = $env:COMPUTERNAME
|
||||
Domain = $computer.Domain
|
||||
DomainNetbios = $domain.NetBIOSName
|
||||
ComputerContainer = if ($labOu) { $labOu.DistinguishedName } else { $domain.ComputersContainer }
|
||||
BrokerService = if ($brokerService) { $brokerService.Status.ToString() } else { 'Missing' }
|
||||
RustDeskServerAddress = if ($rustDeskStatus) { [string]$rustDeskStatus.ServerAddress } else { $null }
|
||||
RustDeskPublicKey = if ($rustDeskStatus) { [string]$rustDeskStatus.PublicKey } else { $null }
|
||||
@@ -471,10 +573,19 @@ try {
|
||||
RustDeskHbbrTask = if ($hbbrTask) { $hbbrTask.State.ToString() } else { 'Missing' }
|
||||
}
|
||||
}
|
||||
if (-not $serverIdentity.Domain -or
|
||||
-not $serverIdentity.Domain.Equals($DomainName, [StringComparison]::OrdinalIgnoreCase)) {
|
||||
if (-not $serverIdentity.Domain -or ($DomainName -and
|
||||
-not $serverIdentity.Domain.Equals($DomainName, [StringComparison]::OrdinalIgnoreCase))) {
|
||||
throw "The server at $DomainControllerIPv4Address belongs to $($serverIdentity.Domain), not $DomainName."
|
||||
}
|
||||
if ($DomainNetbios -and $DomainNetbios -ne $serverIdentity.DomainNetbios) {
|
||||
throw "The supplied NetBIOS domain '$DomainNetbios' does not match '$($serverIdentity.DomainNetbios)'."
|
||||
}
|
||||
$DomainName = [string]$serverIdentity.Domain
|
||||
$DomainNetbios = [string]$serverIdentity.DomainNetbios
|
||||
if (-not $ComputerOuDn) { $ComputerOuDn = [string]$serverIdentity.ComputerContainer }
|
||||
$brokerDnsName = "$brokerRecordName.$DomainName"
|
||||
$brokerEndpoint = "https://${brokerDnsName}:8443/v1/authenticate"
|
||||
Write-Host "Discovered domain: $DomainName ($DomainNetbios). Computer container: $ComputerOuDn"
|
||||
if ($serverIdentity.BrokerService -ne 'Running') {
|
||||
throw "The SGU Authentication Broker is not running on $($serverIdentity.ComputerName)."
|
||||
}
|
||||
@@ -484,6 +595,12 @@ try {
|
||||
$serverIdentity.RustDeskHbbrTask -ne 'Running') {
|
||||
throw "The RustDesk server is not ready on $($serverIdentity.ComputerName). Run the current server bootstrap first."
|
||||
}
|
||||
Set-ClientDomainDns -DnsDomain $DomainName -ServerAddress $DomainControllerIPv4Address
|
||||
foreach ($port in @(53, 88, 135, 389, 445, 8443)) {
|
||||
if (-not (Test-TcpPort -Address $DomainControllerIPv4Address -Port $port -TimeoutMilliseconds 2000)) {
|
||||
throw "Server $DomainControllerIPv4Address is reachable, but required TCP port $port is unavailable. Check AD/SGU services and the LAN/VPN firewall. Domain join has not started."
|
||||
}
|
||||
}
|
||||
|
||||
$certificateSubject = "CN=SGU Credential Provider Client $env:COMPUTERNAME"
|
||||
$clientCertificate = Get-ChildItem Cert:\LocalMachine\My |
|
||||
@@ -566,6 +683,7 @@ try {
|
||||
ComputerOuDn = $ComputerOuDn
|
||||
NetworkInterfaceAlias = $NetworkInterfaceAlias
|
||||
DomainDnsServerAddresses = @($DomainControllerIPv4Address.IPAddressToString)
|
||||
DomainDnsConfigured = $true
|
||||
ConnectivityMode = $ConnectivityMode
|
||||
RemoteDesktopPrincipal = "$DomainNetbios\SG-Laboratorio-Usuarios-RDP"
|
||||
DotNetRuntimeInstallerPath = $runtimeInstaller.FullName
|
||||
|
||||
Reference in New Issue
Block a user