diff --git a/README.md b/README.md index 38eb0d7..3697cdf 100644 --- a/README.md +++ b/README.md @@ -111,7 +111,13 @@ ports remain private even though the VM owns a public IP. Never disable the built-in Microsoft password Credential Provider. It is the supported recovery path if a third-party provider fails to load. -For a clean machine, the supported entry points are the release packages: +For a clean machine, choose the release package that matches the workstation: + +- `sgu-windows10-legacy-client-bootstrap-VERSION.zip` for Windows 10; +- `sgu-windows11-client-bootstrap-VERSION.zip` for Windows 11, including the + modern Azure P2S/pre-logon flow. + +Both use the same direct-lab entry point: ```bat Start-SguServerBootstrap.cmd 192.168.50.10 @@ -123,6 +129,10 @@ omite y ese adaptador todavía usa una dirección `169.254.x.x`, el enrolador la solicita de forma interactiva. En equipos con dos NIC selecciona el adaptador sin puerta de enlace y conserva el `Default Switch` para Internet. +El manifiesto identifica el perfil `Windows10Legacy` o `Windows11Modern` y el +bootstrap valida el build antes de hacer cambios. Las correcciones comunes se +mantienen en ambos; Windows 11 conserva además sus puntos de entrada modernos. + Linux clients are enrolled through their native PAM/SSSD stack instead of the Windows Credential Provider: diff --git a/docs/azure-vpn-deployment.md b/docs/azure-vpn-deployment.md index 160ebb9..b7dcd6c 100644 --- a/docs/azure-vpn-deployment.md +++ b/docs/azure-vpn-deployment.md @@ -122,7 +122,7 @@ $w11 = .\scripts\New-SguAzureP2sCertificates.ps1 -ClientName 'Windows11' Copie a la VM Windows 11 de Hyper-V: -- `sgu-client-bootstrap-VERSION.zip` extraído; +- `sgu-windows11-client-bootstrap-VERSION.zip` extraído; - `$vpn.PackagePath`; - `$w11.ClientCertificatePath`; - `sgu-azure-p2s-root.cer`. diff --git a/docs/bootstrap-recovery.md b/docs/bootstrap-recovery.md index 070b2dd..f8f3749 100644 --- a/docs/bootstrap-recovery.md +++ b/docs/bootstrap-recovery.md @@ -96,7 +96,9 @@ Se admiten Pro, Enterprise y Education. Windows Home no puede unirse a Active Directory local ni actuar como host RDP; el bootstrap lo detecta antes de cambiar el equipo y explica que se debe actualizar la edición. -1. Descargar y extraer `sgu-client-bootstrap-VERSION.zip`. +1. Descargar y extraer el paquete correspondiente: + `sgu-windows10-legacy-client-bootstrap-VERSION.zip` o + `sgu-windows11-client-bootstrap-VERSION.zip`. 2. Ejecutar con la IP fija actual del controlador de dominio: ```bat @@ -111,6 +113,12 @@ del cliente, el bootstrap la solicita cuando el adaptador sólo tiene APIPA ruta predeterminada. Si falla, la ventana elevada permanece abierta y el mismo error queda en `C:\ProgramData\SGU\Bootstrap\Client\latest-error.log`. +Cada manifiesto fija su perfil y evita cruzar paquetes: Windows 10 utiliza +`Windows10Legacy` (build menor a 22000) y Windows 11 `Windows11Modern` (build +22000 o posterior). El ZIP moderno conserva tanto el enrolamiento directo como +Azure P2S/pre-logon; el ZIP legado contiene el flujo directo. El código común y +las garantías de seguridad son idénticos. + Después de UAC, se solicita interactivamente la credencial autorizada para unir equipos. La contraseña existe sólo en memoria. El bootstrap: diff --git a/docs/client-enrollment.md b/docs/client-enrollment.md index 64f852d..dbf730f 100644 --- a/docs/client-enrollment.md +++ b/docs/client-enrollment.md @@ -1,7 +1,11 @@ # Enrolamiento obligatorio de clientes SGU -Para una instalación limpia de Windows se prefiere el único punto de entrada -empaquetado: +Para una instalación limpia se selecciona primero el ZIP correspondiente: + +- `sgu-windows10-legacy-client-bootstrap-VERSION.zip` para Windows 10; +- `sgu-windows11-client-bootstrap-VERSION.zip` para Windows 11. + +Ambos conservan el punto de entrada directo: ```bat Start-SguClientEnrollment.cmd 192.168.50.10 192.168.50.11 @@ -19,6 +23,13 @@ la única NIC activa sin puerta de enlace para no reemplazar el adaptador de Internet. Ante cualquier error conserva la ventana y escribe el diagnóstico en `C:\ProgramData\SGU\Bootstrap\Client\latest-error.log`. +El manifiesto contiene el perfil `Windows10Legacy` o `Windows11Modern` y el +bootstrap rechaza un ZIP que no corresponda al build instalado. El paquete de +Windows 11 conserva además `Start-SguAzureClientEnrollment.cmd` y el instalador +P2S de equipo; el legado de Windows 10 se limita al transporte directo del +laboratorio. Credential Provider, mTLS, cuenta `alumno`, RustDesk, monitorización +y autorreparación siguen saliendo de la misma base de código. + El flujo administrado instala y valida el Credential Provider **antes** de ejecutar `Add-Computer`. La pertenencia al dominio es el último cambio; si falta el runtime, un certificado, el registro COM, la directiva predeterminada o la diff --git a/scripts/Invoke-SguClientBootstrap.ps1 b/scripts/Invoke-SguClientBootstrap.ps1 index 5392939..2b3eb98 100644 --- a/scripts/Invoke-SguClientBootstrap.ps1 +++ b/scripts/Invoke-SguClientBootstrap.ps1 @@ -11,6 +11,8 @@ param( [string]$DomainNetbios = 'LCI', [string]$ComputerOuDn, [string]$NewComputerName, + [ValidateSet('Auto', 'Windows10Legacy', 'Windows11Modern')] + [string]$CompatibilityProfile = 'Auto', [ValidateSet('Direct', 'AzureP2S')] [string]$ConnectivityMode = 'Direct', [string]$VpnConnectionName = 'SGU Azure P2S', @@ -85,6 +87,7 @@ function Assert-PackageManifest { throw "Bootstrap package integrity check failed: $($entry.Path)" } } + return $manifest } function Resolve-ClientInterfaceAlias { @@ -309,7 +312,38 @@ if (-not $ComputerOuDn) { } $packageRoot = $PSScriptRoot -Assert-PackageManifest -PackageRoot $packageRoot +$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.' +} $scriptsRoot = Join-Path $packageRoot 'payload\scripts' $providerPublishPath = Join-Path $packageRoot 'payload\credential-provider' $runtimeInstaller = Get-ChildItem (Join-Path $packageRoot 'payload\prerequisites') ` @@ -609,6 +643,7 @@ if ($SkipRestart) { ClientCertificateRegistered = $true BrokerEndpoint = $brokerEndpoint ConnectivityMode = $ConnectivityMode + CompatibilityProfile = $CompatibilityProfile VpnConnectionName = if ($ConnectivityMode -eq 'AzureP2S') { $VpnConnectionName } else { $null } RestartRequired = $true RustDesk = if ($result) { $result.RustDesk } else { $null } diff --git a/scripts/New-SguBootstrapPackages.ps1 b/scripts/New-SguBootstrapPackages.ps1 index 02160c5..6b6e9c0 100644 --- a/scripts/New-SguBootstrapPackages.ps1 +++ b/scripts/New-SguBootstrapPackages.ps1 @@ -33,7 +33,10 @@ function Write-PackageManifest { param( [Parameter(Mandatory)][string]$PackageRoot, [Parameter(Mandatory)][string]$PackageVersion, - [Parameter(Mandatory)][string]$PackageKind + [Parameter(Mandatory)][string]$PackageKind, + [ValidateSet('Windows10Legacy', 'Windows11Modern')] + [string]$CompatibilityProfile, + [string]$TargetOperatingSystem ) $resolvedPackageRoot = (Resolve-Path -LiteralPath $PackageRoot).Path.TrimEnd('\') @@ -48,13 +51,19 @@ function Write-PackageManifest { } }) $manifest = [ordered]@{ - SchemaVersion = 1 + SchemaVersion = 2 Product = 'SGU Credential Provider' PackageKind = $PackageKind Version = $PackageVersion CreatedAt = (Get-Date).ToUniversalTime().ToString('o') Files = $files } + if ($CompatibilityProfile) { + $manifest['CompatibilityProfile'] = $CompatibilityProfile + } + if ($TargetOperatingSystem) { + $manifest['TargetOperatingSystem'] = $TargetOperatingSystem + } [IO.File]::WriteAllText( (Join-Path $resolvedPackageRoot 'package-manifest.json'), ($manifest | ConvertTo-Json -Depth 6), @@ -78,21 +87,28 @@ if (-not $runtimeInstaller) { } New-Item -ItemType Directory -Path $resolvedOutputRoot -Force | Out-Null -$clientRoot = Join-Path $resolvedOutputRoot "sgu-client-bootstrap-$Version" +$windows11ClientRoot = Join-Path $resolvedOutputRoot "sgu-windows11-client-bootstrap-$Version" +$windows10ClientRoot = Join-Path $resolvedOutputRoot "sgu-windows10-legacy-client-bootstrap-$Version" +$clientRoot = $windows11ClientRoot $serverRoot = Join-Path $resolvedOutputRoot "sgu-server-bootstrap-$Version" $linuxClientRoot = Join-Path $resolvedOutputRoot "sgu-linux-client-bootstrap-$Version" $azureRoot = Join-Path $resolvedOutputRoot "sgu-azure-infrastructure-$Version" -$clientZip = "$clientRoot.zip" +$windows11ClientZip = "$windows11ClientRoot.zip" +$windows10ClientZip = "$windows10ClientRoot.zip" $serverZip = "$serverRoot.zip" $linuxClientZip = "$linuxClientRoot.zip" $azureZip = "$azureRoot.zip" -foreach ($target in @($clientRoot,$serverRoot,$linuxClientRoot,$azureRoot,$clientZip,$serverZip,$linuxClientZip,$azureZip)) { +foreach ($target in @( + $windows11ClientRoot,$windows10ClientRoot,$serverRoot,$linuxClientRoot,$azureRoot, + $windows11ClientZip,$windows10ClientZip,$serverZip,$linuxClientZip,$azureZip)) { if (Test-Path -LiteralPath $target) { throw "Release target already exists: $target" } } -New-Item -ItemType Directory -Path $clientRoot,$serverRoot,$linuxClientRoot,$azureRoot -Force | Out-Null +New-Item -ItemType Directory ` + -Path $windows11ClientRoot,$windows10ClientRoot,$serverRoot,$linuxClientRoot,$azureRoot ` + -Force | Out-Null $welcomeFontNames = @( 'IndivisaTextSans-Regular.otf', 'IndivisaTextSans-Bold.otf', @@ -105,10 +121,6 @@ Copy-RequiredFile -Source (Join-Path $PSScriptRoot 'Invoke-SguClientBootstrap.ps -Destination (Join-Path $clientRoot 'Invoke-SguClientBootstrap.ps1') Copy-RequiredFile -Source (Join-Path $PSScriptRoot 'Start-SguClientEnrollment.cmd') ` -Destination (Join-Path $clientRoot 'Start-SguClientEnrollment.cmd') -Copy-RequiredFile -Source (Join-Path $PSScriptRoot 'Start-SguAzureClientEnrollment.cmd') ` - -Destination (Join-Path $clientRoot 'Start-SguAzureClientEnrollment.cmd') -Copy-RequiredFile -Source (Join-Path $PSScriptRoot 'Install-SguAzureP2sClient.ps1') ` - -Destination (Join-Path $clientRoot 'Install-SguAzureP2sClient.ps1') $clientScripts = @( 'Enable-LabRemoteAccess.ps1', 'Enable-SguClientMonitoring.ps1', @@ -141,8 +153,26 @@ foreach ($fontName in $welcomeFontNames) { } Copy-RequiredFile -Source $runtimeInstaller.FullName ` -Destination (Join-Path $clientRoot "payload\prerequisites\$($runtimeInstaller.Name)") -Write-PackageManifest -PackageRoot $clientRoot -PackageVersion $Version -PackageKind Client -Compress-Archive -Path (Join-Path $clientRoot '*') -DestinationPath $clientZip ` + +# Both Windows packages share the provider and enrollment implementation. The +# Windows 10 artifact freezes the direct-network compatibility surface, while +# the Windows 11 artifact adds the modern Azure P2S/pre-logon entry point. +Copy-Item -Path (Join-Path $windows11ClientRoot '*') ` + -Destination $windows10ClientRoot -Recurse -Force +Copy-RequiredFile -Source (Join-Path $PSScriptRoot 'Start-SguAzureClientEnrollment.cmd') ` + -Destination (Join-Path $windows11ClientRoot 'Start-SguAzureClientEnrollment.cmd') +Copy-RequiredFile -Source (Join-Path $PSScriptRoot 'Install-SguAzureP2sClient.ps1') ` + -Destination (Join-Path $windows11ClientRoot 'Install-SguAzureP2sClient.ps1') + +Write-PackageManifest -PackageRoot $windows10ClientRoot -PackageVersion $Version ` + -PackageKind WindowsClient -CompatibilityProfile Windows10Legacy ` + -TargetOperatingSystem 'Windows 10 Pro, Enterprise, or Education (build below 22000)' +Write-PackageManifest -PackageRoot $windows11ClientRoot -PackageVersion $Version ` + -PackageKind WindowsClient -CompatibilityProfile Windows11Modern ` + -TargetOperatingSystem 'Windows 11 Pro, Enterprise, or Education (build 22000 or later)' +Compress-Archive -Path (Join-Path $windows10ClientRoot '*') -DestinationPath $windows10ClientZip ` + -CompressionLevel Optimal +Compress-Archive -Path (Join-Path $windows11ClientRoot '*') -DestinationPath $windows11ClientZip ` -CompressionLevel Optimal # Linux clients use their native PAM/SSSD sign-in stack rather than the Windows @@ -242,7 +272,8 @@ Compress-Archive -Path (Join-Path $azureRoot '*') -DestinationPath $azureZip ` -CompressionLevel Optimal $checksums = @( - ("{0} {1}" -f (Get-FileHash -LiteralPath $clientZip -Algorithm SHA256).Hash, (Split-Path $clientZip -Leaf)) + ("{0} {1}" -f (Get-FileHash -LiteralPath $windows10ClientZip -Algorithm SHA256).Hash, (Split-Path $windows10ClientZip -Leaf)) + ("{0} {1}" -f (Get-FileHash -LiteralPath $windows11ClientZip -Algorithm SHA256).Hash, (Split-Path $windows11ClientZip -Leaf)) ("{0} {1}" -f (Get-FileHash -LiteralPath $serverZip -Algorithm SHA256).Hash, (Split-Path $serverZip -Leaf)) ("{0} {1}" -f (Get-FileHash -LiteralPath $linuxClientZip -Algorithm SHA256).Hash, (Split-Path $linuxClientZip -Leaf)) ("{0} {1}" -f (Get-FileHash -LiteralPath $azureZip -Algorithm SHA256).Hash, (Split-Path $azureZip -Leaf)) @@ -252,8 +283,10 @@ $checksumsPath = Join-Path $resolvedOutputRoot "SHA256SUMS-$Version.txt" [pscustomobject]@{ Version = $Version - ClientPackage = $clientZip - ClientSha256 = (Get-FileHash -LiteralPath $clientZip -Algorithm SHA256).Hash + Windows10LegacyClientPackage = $windows10ClientZip + Windows10LegacyClientSha256 = (Get-FileHash -LiteralPath $windows10ClientZip -Algorithm SHA256).Hash + Windows11ClientPackage = $windows11ClientZip + Windows11ClientSha256 = (Get-FileHash -LiteralPath $windows11ClientZip -Algorithm SHA256).Hash LinuxClientPackage = $linuxClientZip LinuxClientSha256 = (Get-FileHash -LiteralPath $linuxClientZip -Algorithm SHA256).Hash ServerPackage = $serverZip diff --git a/scripts/Publish-GiteaRelease.ps1 b/scripts/Publish-GiteaRelease.ps1 index 39f9920..195d42a 100644 --- a/scripts/Publish-GiteaRelease.ps1 +++ b/scripts/Publish-GiteaRelease.ps1 @@ -15,7 +15,8 @@ param( $ErrorActionPreference = 'Stop' $tagName = "v$Version" $assetPaths = @( - (Join-Path $ReleaseDirectory "sgu-client-bootstrap-$Version.zip"), + (Join-Path $ReleaseDirectory "sgu-windows10-legacy-client-bootstrap-$Version.zip"), + (Join-Path $ReleaseDirectory "sgu-windows11-client-bootstrap-$Version.zip"), (Join-Path $ReleaseDirectory "sgu-server-bootstrap-$Version.zip"), (Join-Path $ReleaseDirectory "sgu-linux-client-bootstrap-$Version.zip"), (Join-Path $ReleaseDirectory "sgu-azure-infrastructure-$Version.zip"), @@ -111,7 +112,9 @@ Bootstrap reproducible para el laboratorio SGU. - **Advertencia:** el bootstrap de servidor crea un bosque nuevo. No restaura los SID, contraseñas ni relaciones de confianza del bosque anterior; para conservarlos se requiere una recuperación de bosque desde una copia de estado del sistema. - `sgu-server-bootstrap-$Version.zip`: crea el bosque AD/DNS, OUs, grupo RDP, GPO, recurso `Packages`, broker mTLS y administración remota; se reanuda solo después del reinicio. -- `sgu-client-bootstrap-$Version.zip`: registra un certificado mTLS único, instala y valida el Credential Provider antes de unir el equipo al dominio, habilita RDP/WinRM y se repara al arranque. +- `sgu-windows10-legacy-client-bootstrap-$Version.zip`: perfil directo para Windows 10 de laboratorio, con las correcciones de NIC privada, límites de cuentas locales y compatibilidad de sus APIs heredadas. +- `sgu-windows11-client-bootstrap-$Version.zip`: perfil completo para Windows 11; conserva el enrolamiento directo y añade Azure P2S con certificado de máquina y entrada previa al inicio de sesión. +- Ambos clientes comparten los mismos binarios, seguridad mTLS, Credential Provider, cuenta estándar, RustDesk, supervisión y autorreparación; el manifiesto impide ejecutar accidentalmente el paquete de la otra versión de Windows. - En clientes Hyper-V con dos NIC, el bootstrap selecciona la red privada sin puerta de enlace, solicita o acepta la IP fija del cliente, espera a que la dirección y WinRM estén disponibles y conserva en pantalla y archivo cualquier error de enrolamiento. - `sgu-linux-client-bootstrap-$Version.zip`: une clientes Debian/Ubuntu o RHEL/Fedora/Rocky/AlmaLinux con realmd, Kerberos y SSSD. Solicita interactivamente la contraseña de unión y no instala el Credential Provider de Windows. - `sgu-azure-infrastructure-$Version.zip`: despliega mediante Bicep una VM Windows Server 2025, red privada, IP pública protegida por NSG y Azure VPN Gateway P2S; también genera certificados por equipo y descarga el perfil de cliente. diff --git a/scripts/Start-SguAzureClientEnrollment.cmd b/scripts/Start-SguAzureClientEnrollment.cmd index d823f0a..ef666d0 100644 --- a/scripts/Start-SguAzureClientEnrollment.cmd +++ b/scripts/Start-SguAzureClientEnrollment.cmd @@ -4,5 +4,12 @@ set "SGU_BOOTSTRAP_IP=%~1" set "SGU_VPN_PACKAGE=%~2" set "SGU_VPN_PFX=%~3" set "SGU_VPN_ROOT=%~4" -powershell.exe -NoLogo -NoProfile -ExecutionPolicy Bypass -Command "$script = Join-Path '%~dp0' 'Invoke-SguClientBootstrap.ps1'; $arguments = @('-NoLogo','-NoProfile','-ExecutionPolicy','Bypass','-File',('"' + $script + '"'),'-ConnectivityMode','AzureP2S'); if ($env:SGU_BOOTSTRAP_IP) { $arguments += @('-DomainControllerIPv4Address',('"' + $env:SGU_BOOTSTRAP_IP + '"')) }; if ($env:SGU_VPN_PACKAGE) { $arguments += @('-VpnProfilePackagePath',('"' + [IO.Path]::GetFullPath($env:SGU_VPN_PACKAGE) + '"')) }; if ($env:SGU_VPN_PFX) { $arguments += @('-VpnClientCertificatePfxPath',('"' + [IO.Path]::GetFullPath($env:SGU_VPN_PFX) + '"')) }; if ($env:SGU_VPN_ROOT) { $arguments += @('-VpnClientRootCertificatePath',('"' + [IO.Path]::GetFullPath($env:SGU_VPN_ROOT) + '"')) }; $process = Start-Process -FilePath powershell.exe -Verb RunAs -ArgumentList $arguments -Wait -PassThru; exit $process.ExitCode" -exit /b %errorlevel% +powershell.exe -NoLogo -NoProfile -ExecutionPolicy Bypass -Command "$script = Join-Path '%~dp0' 'Invoke-SguClientBootstrap.ps1'; $arguments = @('-NoLogo','-NoProfile','-ExecutionPolicy','Bypass','-File',('"' + $script + '"'),'-PauseOnError','-ConnectivityMode','AzureP2S'); if ($env:SGU_BOOTSTRAP_IP) { $arguments += @('-DomainControllerIPv4Address',('"' + $env:SGU_BOOTSTRAP_IP + '"')) }; if ($env:SGU_VPN_PACKAGE) { $arguments += @('-VpnProfilePackagePath',('"' + [IO.Path]::GetFullPath($env:SGU_VPN_PACKAGE) + '"')) }; if ($env:SGU_VPN_PFX) { $arguments += @('-VpnClientCertificatePfxPath',('"' + [IO.Path]::GetFullPath($env:SGU_VPN_PFX) + '"')) }; if ($env:SGU_VPN_ROOT) { $arguments += @('-VpnClientRootCertificatePath',('"' + [IO.Path]::GetFullPath($env:SGU_VPN_ROOT) + '"')) }; $process = Start-Process -FilePath powershell.exe -Verb RunAs -ArgumentList $arguments -Wait -PassThru; exit $process.ExitCode" +set "SGU_EXIT_CODE=%errorlevel%" +if not "%SGU_EXIT_CODE%"=="0" ( + echo. + echo SGU Windows 11 Azure enrollment did not complete. Review: + echo C:\ProgramData\SGU\Bootstrap\Client\latest-error.log + pause +) +exit /b %SGU_EXIT_CODE% diff --git a/tests/BootstrapNetwork.Tests.ps1 b/tests/BootstrapNetwork.Tests.ps1 index 61e7420..6b4cb02 100644 --- a/tests/BootstrapNetwork.Tests.ps1 +++ b/tests/BootstrapNetwork.Tests.ps1 @@ -72,6 +72,8 @@ Describe 'SGU public-cloud network safety' { 'ConnectivityMode') | Should Be $true ((Get-Command $clientBootstrapPath).Parameters.Keys -contains 'VpnProfilePackagePath') | Should Be $true + ((Get-Command $clientBootstrapPath).Parameters.Keys -contains + 'CompatibilityProfile') | Should Be $true } It 'accepts an explicit static IPv4 address for a private Windows adapter' { @@ -106,6 +108,16 @@ Describe 'SGU public-cloud network safety' { $source | Should Match 'Wait-TcpPort -Address \$DomainControllerIPv4Address -Port 5985' } + It 'keeps legacy and modern Windows package profiles isolated by build' { + $source = Get-Content -LiteralPath $clientBootstrapPath -Raw + $source.Contains("if (`$CompatibilityProfile -eq 'Windows10Legacy' -and `$windowsBuild -ge 22000)") | + Should Be $true + $source.Contains("if (`$CompatibilityProfile -eq 'Windows11Modern' -and `$windowsBuild -lt 22000)") | + Should Be $true + $source.Contains("if (`$CompatibilityProfile -eq 'Windows10Legacy' -and `$ConnectivityMode -eq 'AzureP2S')") | + Should Be $true + } + It 'uses an all-user machine-certificate VPN profile' { $source = Get-Content -LiteralPath $azureClientPath -Raw $source | Should Match '-AuthenticationMethod MachineCertificate' diff --git a/tests/ClientEnrollmentScripts.Tests.ps1 b/tests/ClientEnrollmentScripts.Tests.ps1 index 5a34b1f..0e3d916 100644 --- a/tests/ClientEnrollmentScripts.Tests.ps1 +++ b/tests/ClientEnrollmentScripts.Tests.ps1 @@ -1,6 +1,9 @@ $repositoryRoot = (Resolve-Path (Join-Path $PSScriptRoot '..')).Path $localUserScriptPath = Join-Path $repositoryRoot 'scripts\Set-SguStandardLocalUser.ps1' $enrollmentTestScriptPath = Join-Path $repositoryRoot 'scripts\Test-SguClientEnrollment.ps1' +$packageScriptPath = Join-Path $repositoryRoot 'scripts\New-SguBootstrapPackages.ps1' +$releaseScriptPath = Join-Path $repositoryRoot 'scripts\Publish-GiteaRelease.ps1' +$azureLauncherPath = Join-Path $repositoryRoot 'scripts\Start-SguAzureClientEnrollment.cmd' $tokens = $null $parseErrors = $null @@ -38,4 +41,25 @@ Describe 'SGU Windows client enrollment scripts' { $localUserSource | Should Not Match '\$verifiedUser\.PasswordNeverExpires' $enrollmentTestSource | Should Not Match '\$standardLocalUser\.PasswordNeverExpires' } + + It 'publishes separate legacy Windows 10 and modern Windows 11 artifacts' { + $packageSource = Get-Content -LiteralPath $packageScriptPath -Raw + $releaseSource = Get-Content -LiteralPath $releaseScriptPath -Raw + $packageSource | Should Match 'sgu-windows10-legacy-client-bootstrap-\$Version' + $packageSource | Should Match 'sgu-windows11-client-bootstrap-\$Version' + $packageSource | Should Match '-CompatibilityProfile Windows10Legacy' + $packageSource | Should Match '-CompatibilityProfile Windows11Modern' + $releaseSource | Should Match 'sgu-windows10-legacy-client-bootstrap-\$Version\.zip' + $releaseSource | Should Match 'sgu-windows11-client-bootstrap-\$Version\.zip' + } + + It 'keeps Azure P2S in the modern Windows 11 artifact' { + $packageSource = Get-Content -LiteralPath $packageScriptPath -Raw + $azureLauncher = Get-Content -LiteralPath $azureLauncherPath -Raw + $packageSource.Contains("Join-Path `$windows11ClientRoot 'Start-SguAzureClientEnrollment.cmd'") | + Should Be $true + $packageSource.Contains("Join-Path `$windows10ClientRoot 'Start-SguAzureClientEnrollment.cmd'") | + Should Be $false + $azureLauncher | Should Match '-PauseOnError' + } }