Compare commits

...
1 Commits
Author SHA1 Message Date
alexrg da310e4213 Deploy FSLogix automatically through domain policy 2026-09-18 10:22:28 -06:00
5 changed files with 209 additions and 1 deletions
+36
View File
@@ -0,0 +1,36 @@
# SGU Credential Provider 0.6.2
Continúa la serie publicada después de `0.6.1`.
## Cambios
- Se agrega despliegue automático e idempotente de FSLogix 26.08 mediante una
GPO de inicio de equipo ligada a `OU=Laboratorio`.
- El publicador verifica la firma Authenticode de Microsoft y el SHA-256 del
instalador antes de copiarlo al SYSVOL.
- Los equipos que estaban apagados reciben e instalan FSLogix en su siguiente
arranque; no se emplean tareas programadas.
- El script queda incluido en el paquete de servidor y la guía de roaming
documenta su publicación y comprobación.
## Validación del despliegue
- La GPO `SGU - FSLogix client deployment` está enlazada y sus versiones de AD
y SYSVOL coinciden.
- El instalador publicado conserva firma válida de Microsoft y SHA-256
`0BAF7FE8195571060F0361B351E58FE6CD422AE521A96BAB3E66D900544E8A30`.
- `AF-02` y `AF-03` aplicaron la GPO y ejecutan `frxsvc` con FSLogix
`3.26.826.17182`.
- Azure Storage permanece en `defaultAction=Deny` y sólo autoriza
`200.13.89.0/24`.
## Descargas
- `sgu-windows-client-bootstrap-0.6.2.zip`
- `sgu-server-bootstrap-0.6.2.zip`
- `sgu-linux-client-bootstrap-0.6.2.zip`
- `sgu-azure-infrastructure-0.6.2.zip`
- `SHA256SUMS-0.6.2.txt`
Verifique los hashes SHA-256 antes de ejecutar los paquetes. No se incluyen
credenciales, claves de almacenamiento ni artefactos temporales del despliegue.
+18 -1
View File
@@ -115,7 +115,7 @@ dedicada para evitar que una cuenta de equipo sin agente pierda Kerberos a los
30 días. Si la política exige rotación, use periódicamente el procedimiento 30 días. Si la política exige rotación, use periódicamente el procedimiento
oficial `Update-AzStorageAccountADObjectPassword` y no cambie sólo un lado. oficial `Update-AzStorageAccountADObjectPassword` y no cambie sólo un lado.
## 3. Instalar FSLogix en las imágenes Windows ## 3. Instalar FSLogix en los clientes Windows
FSLogix no forma parte de Windows y su licencia debe validarse para las ediciones FSLogix no forma parte de Windows y su licencia debe validarse para las ediciones
y suscripciones usadas. Descargue `FSLogixAppsSetup.exe` directamente de y suscripciones usadas. Descargue `FSLogixAppsSetup.exe` directamente de
@@ -132,6 +132,22 @@ imagen ya contiene FSLogix, el instalador puede omitirse. Microsoft recomienda
mantener la versión más reciente; FSLogix no se actualiza mediante Windows mantener la versión más reciente; FSLogix no se actualiza mediante Windows
Update. Update.
Para cubrir también equipos ya unidos al dominio —incluidos los que estaban
apagados durante el despliegue— publique el instalador como script de inicio de
equipo:
```powershell
.\payload\scripts\Publish-SguFsLogixClientDeployment.ps1
```
El script descarga el paquete oficial FSLogix 26.08, valida la firma
Authenticode de Microsoft y el SHA-256 esperado, y crea la GPO
`SGU - FSLogix client deployment` ligada a `OU=Laboratorio`. El ejecutable queda
en el SYSVOL de esa GPO y se instala de forma idempotente durante el arranque;
no usa tareas programadas ni requiere que el equipo estuviera encendido al
publicarla. Si `frxsvc` y `frx.exe` ya existen, el script de inicio termina sin
reinstalar.
## 4. Validar un piloto ## 4. Validar un piloto
Después de `gpupdate /force` y reiniciar un cliente: Después de `gpupdate /force` y reiniciar un cliente:
@@ -141,6 +157,7 @@ Resolve-DnsName "$($azure.UserRoamingStorageAccountName).file.core.windows.net"
Test-NetConnection "$($azure.UserRoamingStorageAccountName).file.core.windows.net" -Port 445 Test-NetConnection "$($azure.UserRoamingStorageAccountName).file.core.windows.net" -Port 445
Get-Service frxsvc Get-Service frxsvc
Get-ItemProperty 'HKLM:\SOFTWARE\FSLogix\Profiles' Get-ItemProperty 'HKLM:\SOFTWARE\FSLogix\Profiles'
Get-ItemProperty 'HKLM:\SOFTWARE\SGU\FSLogixDeployment'
& 'C:\Program Files\FSLogix\Apps\frx.exe' list-redirects & 'C:\Program Files\FSLogix\Apps\frx.exe' list-redirects
``` ```
+1
View File
@@ -201,6 +201,7 @@ $serverScripts = @(
'Get-SguUsageReport.ps1', 'Get-SguUsageReport.ps1',
'Get-SguBrokerLog.ps1', 'Get-SguBrokerLog.ps1',
'Install-SguDomainMonitoring.ps1', 'Install-SguDomainMonitoring.ps1',
'Publish-SguFsLogixClientDeployment.ps1',
'Install-SguRustDeskClient.ps1', 'Install-SguRustDeskClient.ps1',
'Install-SguRustDeskLinuxEnrollment.ps1', 'Install-SguRustDeskLinuxEnrollment.ps1',
'Install-SguRustDeskServer.ps1', 'Install-SguRustDeskServer.ps1',
@@ -0,0 +1,140 @@
#Requires -Version 5.1
[CmdletBinding(SupportsShouldProcess)]
param(
[string]$GpoName = 'SGU - FSLogix client deployment',
[string]$LaboratoryOuDn,
[uri]$InstallerZipUri = 'https://download.microsoft.com/download/ae6d2014-e692-45fa-a88b-ee552567cdc1/FSLogix_26.08.zip',
[string]$ExpectedInstallerSha256 = '0BAF7FE8195571060F0361B351E58FE6CD422AE521A96BAB3E66D900544E8A30',
[string]$CacheRoot = "$env:ProgramData\SGU\FSLogixDeployment\26.08"
)
$ErrorActionPreference = 'Stop'
$identity = [Security.Principal.WindowsIdentity]::GetCurrent()
$principal = [Security.Principal.WindowsPrincipal]::new($identity)
if (-not $principal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) {
throw 'Run the FSLogix GPO deployment from an elevated Windows PowerShell session.'
}
Import-Module ActiveDirectory -ErrorAction Stop
Import-Module GroupPolicy -ErrorAction Stop
$domain = Get-ADDomain
$domainName = $domain.DNSRoot
$domainDn = $domain.DistinguishedName
if (-not $LaboratoryOuDn) { $LaboratoryOuDn = "OU=Laboratorio,$domainDn" }
Get-ADOrganizationalUnit -Identity $LaboratoryOuDn -ErrorAction Stop | Out-Null
$zipPath = Join-Path $CacheRoot 'FSLogix_26.08.zip'
$extractRoot = Join-Path $CacheRoot 'expanded'
$installerPath = Join-Path $extractRoot 'x64\Release\FSLogixAppsSetup.exe'
if (-not (Test-Path -LiteralPath $installerPath -PathType Leaf)) {
if (-not $PSCmdlet.ShouldProcess($InstallerZipUri.AbsoluteUri, "Download official FSLogix package to $zipPath")) { return }
New-Item -ItemType Directory -Path $CacheRoot,$extractRoot -Force | Out-Null
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
Invoke-WebRequest -Uri $InstallerZipUri -OutFile $zipPath -UseBasicParsing
Expand-Archive -LiteralPath $zipPath -DestinationPath $extractRoot -Force
}
if (-not (Test-Path -LiteralPath $installerPath -PathType Leaf)) {
throw "The FSLogix x64 installer is missing after extraction: $installerPath"
}
$signature = Get-AuthenticodeSignature -LiteralPath $installerPath
if ($signature.Status -ne [Management.Automation.SignatureStatus]::Valid -or
-not $signature.SignerCertificate -or
$signature.SignerCertificate.Subject -notmatch '(^|,\s*)CN=Microsoft Corporation(,|$)') {
throw 'FSLogixAppsSetup.exe does not have a valid Microsoft Corporation signature.'
}
$installerHash = (Get-FileHash -LiteralPath $installerPath -Algorithm SHA256).Hash
if ($installerHash -ne $ExpectedInstallerSha256) {
throw "FSLogixAppsSetup.exe SHA-256 mismatch. Expected $ExpectedInstallerSha256; received $installerHash."
}
$gpo = Get-GPO -Name $GpoName -Domain $domainName -ErrorAction SilentlyContinue
if (-not $gpo) { $gpo = New-GPO -Name $GpoName -Domain $domainName }
$links = @(Get-GPInheritance -Target $LaboratoryOuDn -Domain $domainName).GpoLinks
if (-not ($links | Where-Object DisplayName -eq $GpoName)) {
New-GPLink -Name $GpoName -Target $LaboratoryOuDn -Domain $domainName -LinkEnabled Yes | Out-Null
}
# Set one ordinary machine value through the supported cmdlet. Besides providing
# a deployment marker, this initializes the computer half of the GPO and its
# Registry client-side extension before the Scripts extension is added below.
Set-GPRegistryValue -Name $GpoName -Domain $domainName `
-Key 'HKLM\SOFTWARE\SGU\FSLogixDeployment' -ValueName 'Version' `
-Type String -Value '26.08-3.26.826.17182' | Out-Null
$gpo = Get-GPO -Name $GpoName -Domain $domainName
$guid = $gpo.Id.ToString('B').ToUpperInvariant()
$gpoRoot = "\\$domainName\SYSVOL\$domainName\Policies\$guid"
$scriptsRoot = Join-Path $gpoRoot 'Machine\Scripts'
$startupRoot = Join-Path $scriptsRoot 'Startup'
New-Item -ItemType Directory -Path $startupRoot -Force | Out-Null
Copy-Item -LiteralPath $installerPath -Destination (Join-Path $startupRoot 'FSLogixAppsSetup.exe') -Force
$startupPowerShell = @'
$ErrorActionPreference = 'Stop'
$appsRoot = Join-Path $env:ProgramFiles 'FSLogix\Apps'
$frx = Join-Path $appsRoot 'frx.exe'
if ((Get-Service frxsvc -ErrorAction SilentlyContinue) -and (Test-Path -LiteralPath $frx)) { exit 0 }
$source = Join-Path $PSScriptRoot 'FSLogixAppsSetup.exe'
$signature = Get-AuthenticodeSignature -LiteralPath $source
if ($signature.Status -ne 'Valid' -or -not $signature.SignerCertificate -or
$signature.SignerCertificate.Subject -notmatch '(^|,\s*)CN=Microsoft Corporation(,|$)') { exit 10 }
$targetRoot = Join-Path $env:ProgramData 'SGU\FSLogix'
New-Item -ItemType Directory -Path $targetRoot -Force | Out-Null
$target = Join-Path $targetRoot 'FSLogixAppsSetup.exe'
Copy-Item -LiteralPath $source -Destination $target -Force
$log = Join-Path $targetRoot 'install.log'
$process = Start-Process -FilePath $target -ArgumentList @('/install','/quiet','/norestart','/log',"`"$log`"") -Wait -PassThru
if ($process.ExitCode -notin @(0,1641,3010)) { exit $process.ExitCode }
if (-not (Get-Service frxsvc -ErrorAction SilentlyContinue) -or -not (Test-Path -LiteralPath $frx)) { exit 11 }
exit 0
'@
[IO.File]::WriteAllText(
(Join-Path $startupRoot 'Install-SguFsLogix-Startup.ps1'),
$startupPowerShell,
[Text.UTF8Encoding]::new($true))
$startupCommand = '@echo off' + [Environment]::NewLine +
'powershell.exe -NoProfile -NonInteractive -ExecutionPolicy Bypass -File "%~dp0Install-SguFsLogix-Startup.ps1"' + [Environment]::NewLine +
'exit /b %ERRORLEVEL%' + [Environment]::NewLine
[IO.File]::WriteAllText(
(Join-Path $startupRoot 'Install-SguFsLogix.cmd'),
$startupCommand,
[Text.Encoding]::ASCII)
$scriptsIni = "[Startup]`r`n0CmdLine=Install-SguFsLogix.cmd`r`n0Parameters=`r`n"
[IO.File]::WriteAllText(
(Join-Path $scriptsRoot 'scripts.ini'),
$scriptsIni,
[Text.Encoding]::Unicode)
$policyDn = "CN=$guid,CN=Policies,CN=System,$domainDn"
$policy = Get-ADObject -Identity $policyDn -Properties versionNumber,gPCMachineExtensionNames
$scriptExtension = '[{42B5FAAE-6536-11D2-AE5A-0000F87571E3}{40B6664F-4972-11D1-A7CA-0000F87571E3}]'
$extensions = @([regex]::Matches([string]$policy.gPCMachineExtensionNames, '\[\{[0-9A-Fa-f-]{36}\}\{[0-9A-Fa-f-]{36}\}\]') |
ForEach-Object Value)
if ($extensions -notcontains $scriptExtension) { $extensions += $scriptExtension }
$extensionValue = ($extensions | Sort-Object { $_.Substring(1, 38) }) -join ''
$currentVersion = [int64]$policy.versionNumber
$machineVersion = $currentVersion -band 0xFFFF
if ($machineVersion -ge 65535) { throw 'The computer GPO version cannot be incremented further.' }
$newVersion = ($currentVersion -band 0xFFFF0000) -bor ($machineVersion + 1)
$gptIniPath = Join-Path $gpoRoot 'gpt.ini'
$gptLines = @(Get-Content -LiteralPath $gptIniPath)
$versionFound = $false
$gptLines = @($gptLines | ForEach-Object {
if ($_ -match '^Version=') { $versionFound = $true; "Version=$newVersion" } else { $_ }
})
if (-not $versionFound) { $gptLines += "Version=$newVersion" }
[IO.File]::WriteAllLines($gptIniPath, $gptLines, [Text.Encoding]::ASCII)
Set-ADObject -Identity $policy -Replace @{
gPCMachineExtensionNames = $extensionValue
versionNumber = [int]$newVersion
}
$verifiedGpo = Get-GPO -Name $GpoName -Domain $domainName
[pscustomobject]@{
GpoName = $verifiedGpo.DisplayName
GpoId = $verifiedGpo.Id
LaboratoryOuDn = $LaboratoryOuDn
MachineVersion = $verifiedGpo.Computer.DSVersion
InstallerVersion = '3.26.826.17182'
InstallerSha256 = $installerHash
StartupScript = Join-Path $startupRoot 'Install-SguFsLogix.cmd'
}
+14
View File
@@ -3,6 +3,7 @@ $bicepPath = Join-Path $repositoryRoot 'infra\azure\main.bicep'
$deploymentPath = Join-Path $repositoryRoot 'scripts\Deploy-SguAzureInfrastructure.ps1' $deploymentPath = Join-Path $repositoryRoot 'scripts\Deploy-SguAzureInfrastructure.ps1'
$configurationPath = Join-Path $repositoryRoot 'scripts\Enable-SguAzureUserRoaming.ps1' $configurationPath = Join-Path $repositoryRoot 'scripts\Enable-SguAzureUserRoaming.ps1'
$installerPath = Join-Path $repositoryRoot 'scripts\Install-SguFsLogix.ps1' $installerPath = Join-Path $repositoryRoot 'scripts\Install-SguFsLogix.ps1'
$gpoDeploymentPath = Join-Path $repositoryRoot 'scripts\Publish-SguFsLogixClientDeployment.ps1'
$clientBootstrapPath = Join-Path $repositoryRoot 'scripts\Invoke-SguClientBootstrap.ps1' $clientBootstrapPath = Join-Path $repositoryRoot 'scripts\Invoke-SguClientBootstrap.ps1'
$azureLauncherPath = Join-Path $repositoryRoot 'scripts\Start-SguAzureClientEnrollment.cmd' $azureLauncherPath = Join-Path $repositoryRoot 'scripts\Start-SguAzureClientEnrollment.cmd'
$packagePath = Join-Path $repositoryRoot 'scripts\New-SguBootstrapPackages.ps1' $packagePath = Join-Path $repositoryRoot 'scripts\New-SguBootstrapPackages.ps1'
@@ -11,6 +12,7 @@ foreach ($scriptPath in @(
$deploymentPath, $deploymentPath,
$configurationPath, $configurationPath,
$installerPath, $installerPath,
$gpoDeploymentPath,
$clientBootstrapPath, $clientBootstrapPath,
$packagePath)) { $packagePath)) {
$tokens = $null $tokens = $null
@@ -162,5 +164,17 @@ Describe 'SGU FSLogix image enrollment' {
$source = Get-Content -LiteralPath $packagePath -Raw $source = Get-Content -LiteralPath $packagePath -Raw
$source | Should Match "'Install-SguFsLogix\.ps1'" $source | Should Match "'Install-SguFsLogix\.ps1'"
$source | Should Match "'Enable-SguAzureUserRoaming\.ps1'" $source | Should Match "'Enable-SguAzureUserRoaming\.ps1'"
$source | Should Match "'Publish-SguFsLogixClientDeployment\.ps1'"
}
It 'publishes a signed idempotent FSLogix computer startup deployment' {
$source = Get-Content -LiteralPath $gpoDeploymentPath -Raw
$source | Should Match 'Get-AuthenticodeSignature'
$source | Should Match 'ExpectedInstallerSha256'
$source | Should Match "'Machine\\Scripts'"
$source | Should Match "'Startup'"
$source | Should Match 'scripts\.ini'
$source | Should Match '42B5FAAE-6536-11D2-AE5A-0000F87571E3'
$source | Should Match 'Install-SguFsLogix-Startup\.ps1'
} }
} }