Compare commits

...
2 Commits
9 changed files with 388 additions and 9 deletions
+3 -1
View File
@@ -111,7 +111,9 @@ For a public Azure VM, use
optional Azure P2S gateway or direct enrollment restricted to explicit public
source CIDRs. P2S deployments also provision private Azure Files storage for
role-based roaming: `AL` redirects only Documents/Desktop, while `AD` and `DO`
can mount an FSLogix VHDX profile after the post-domain setup step.
use an FSLogix VHDX profile after the post-domain setup step. Existing public
Azure Files deployments automatically use FSLogix Cloud Cache locally instead
of performing all profile I/O across WAN SMB.
Never disable the built-in Microsoft password Credential Provider. It is the
supported recovery path if a third-party provider fails to load.
+3 -1
View File
@@ -84,7 +84,9 @@ Azure P2S deployments can add a dedicated, private Azure Files account. Student
accounts (`AL`) keep a non-authoritative local profile and redirect only
Documents/Desktop. Administrative and professor accounts (`AD`/`DO`) receive an
FSLogix VHDX profile container through object-specific settings keyed to their
AD security-group SIDs. The storage account uses AD DS Kerberos with AES-256;
AD security-group SIDs. Private endpoints mount the VHDX directly; public WAN
endpoints use a local FSLogix Cloud Cache synchronized to Azure Files so profile
I/O does not block on every SMB round trip. The storage account uses AD DS Kerberos with AES-256;
share-root ACLs use `CREATOR OWNER` so users cannot browse one another's data.
The bootstrap staging account is deliberately separate. See
[user-roaming.md](user-roaming.md) for rollout and recovery constraints.
+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.
+42
View File
@@ -0,0 +1,42 @@
# SGU Credential Provider 0.6.3
Corrige el primer inicio de sesión de perfiles completos `AD`/`DO` cuando Azure
Files se consume mediante su endpoint público.
## Cambios
- `Enable-SguAzureUserRoaming.ps1` selecciona automáticamente FSLogix Cloud
Cache para endpoints públicos y conserva el montaje directo para endpoints
privados.
- Cloud Cache realiza el I/O del perfil sobre el VHDX local y sincroniza con
Azure Files, evitando que la latencia WAN de SMB bloquee cada operación.
- La GPO elimina de forma efectiva `VHDLocations` al activar `CCDLocations`,
incluyendo equipos que habían aplicado la política directa anterior.
- Se impide el retorno silencioso a un perfil local o temporal cuando FSLogix
no puede registrar el contenedor.
- La documentación agrega capacidad de disco, validación del registro efectivo
y rutas de diagnóstico para ambos modos.
## Validación del despliegue
- Se eliminó únicamente el perfil piloto fallido de `AD017045` en `AF-03`; la
cuenta de Active Directory se conservó.
- La GPO `SGU - AD-DO FSLogix profiles` quedó en Cloud Cache para los SID de
`SGU-Administrativos` y `SGU-Docentes`; `VHDLocations` ya no aparece en el
registro efectivo de `AF-03`.
- El nuevo inicio de `AD017045` creó la caché y el proxy locales, agregó la
redirección del perfil al VHDX y completó `LoadProfile` en 4.649 segundos, sin
error 258, perfil temporal ni retorno local.
- Pasaron 70 pruebas Pester, 41 pruebas de `SGU.AuthBroker.Core`, 16 de
`SGU.AuthBroker` y 6 del Credential Provider. La solución compiló sin errores.
## Descargas
- `sgu-windows-client-bootstrap-0.6.3.zip`
- `sgu-server-bootstrap-0.6.3.zip`
- `sgu-linux-client-bootstrap-0.6.3.zip`
- `sgu-azure-infrastructure-0.6.3.zip`
- `SHA256SUMS-0.6.3.txt`
Verifique los hashes SHA-256 antes de ejecutar los paquetes. No se incluyen
credenciales, claves de almacenamiento ni artefactos temporales del despliegue.
+53 -5
View File
@@ -6,7 +6,7 @@ cada tipo de cuenta SGU:
| Rol | Datos conservados | Implementación |
| --- | --- | --- |
| `AL` | `Desktop` y `Documents` | Redirección de carpetas a Azure Files; el resto del perfil local no es autoritativo y no se copia al iniciar o cerrar sesión. |
| `AD` / `DO` | Perfil completo | Un contenedor VHDX único de FSLogix montado desde Azure Files. |
| `AD` / `DO` | Perfil completo | FSLogix: montaje directo del VHDX con endpoint privado o Cloud Cache local sincronizada con Azure Files cuando el endpoint es público. |
Linux conserva su solución nativa de directorios personales; no intenta montar
un perfil de Windows.
@@ -36,6 +36,22 @@ esa ejecución; después se establece `defaultAction=Deny` y se deja únicamente
red institucional autorizada. En el despliegue LCI actual esa regla final es
`200.13.89.0/24`.
El modo `-StaffProfileStorageMode Auto` es el predeterminado. Selecciona
`Direct` con un endpoint privado y `CloudCache` con uno público. Cloud Cache
mantiene el VHDX de trabajo en `C:\ProgramData\FSLogix\Cache` y sincroniza sus
cambios con Azure Files, evitando que cada operación del perfil dependa de la
latencia WAN de SMB. `CCDLocations` y `VHDLocations` son mutuamente excluyentes:
el configurador elimina de forma efectiva el valor obsoleto al cambiar de modo,
incluso en clientes que ya habían aplicado la GPO anterior. Puede forzarse un
modo con `-StaffProfileStorageMode Direct` o `CloudCache`, pero `Direct` sobre
un endpoint público sólo debe usarse para diagnóstico.
Cloud Cache necesita espacio local suficiente para el perfil (30 GB de máximo
por omisión), conserva la caché tras cerrar sesión y exige al menos un proveedor
remoto sano al registrar o desmontar el perfil. La GPO también impide el inicio
con perfil temporal o local cuando FSLogix falla, para que una incidencia no
produzca dos perfiles divergentes silenciosamente.
Azure Files se integra con el AD DS SGU, no con cuentas o claves guardadas en
cada cliente. El script configura AES-256 para Kerberos, una ACL de mínimo
privilegio con `CREATOR OWNER`, administradores y los grupos del rol, y permiso
@@ -97,6 +113,14 @@ $parameters = @{
.\payload\scripts\Enable-SguAzureUserRoaming.ps1 @parameters
```
Para reutilizar una cuenta con endpoint público:
```powershell
$parameters.EndpointAccess = 'Public'
$parameters.StaffProfileStorageMode = 'Auto' # resuelve a CloudCache
.\payload\scripts\Enable-SguAzureUserRoaming.ps1 @parameters
```
La operación es idempotente. Crea `OU=AzureFilesConfig`, une la cuenta de
almacenamiento al bosque y publica dos GPO:
@@ -115,7 +139,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
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
y suscripciones usadas. Descargue `FSLogixAppsSetup.exe` directamente de
@@ -132,6 +156,22 @@ imagen ya contiene FSLogix, el instalador puede omitirse. Microsoft recomienda
mantener la versión más reciente; FSLogix no se actualiza mediante Windows
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
Después de `gpupdate /force` y reiniciar un cliente:
@@ -140,14 +180,21 @@ Después de `gpupdate /force` y reiniciar un cliente:
Resolve-DnsName "$($azure.UserRoamingStorageAccountName).file.core.windows.net"
Test-NetConnection "$($azure.UserRoamingStorageAccountName).file.core.windows.net" -Port 445
Get-Service frxsvc
Get-ItemProperty 'HKLM:\SOFTWARE\FSLogix\Profiles'
Get-ItemProperty 'HKLM:\SOFTWARE\FSLogix\Profiles\ObjectSpecific\<SID-del-grupo>'
Get-ItemProperty 'HKLM:\SOFTWARE\SGU\FSLogixDeployment'
& 'C:\Program Files\FSLogix\Apps\frx.exe' list-redirects
```
La resolución debe devolver la IP privada del endpoint. Inicie primero con un
En modo privado, la resolución debe devolver la IP privada del endpoint; en
modo público debe devolver una dirección pública y TCP 445 debe estar permitido
desde la red institucional. En Cloud Cache compruebe además que existe
`C:\ProgramData\FSLogix\Cache` y que el registro efectivo contiene
`CCDLocations`, pero no `VHDLocations`. Inicie primero con un
`AL` nuevo y confirme las ubicaciones de Escritorio/Documentos; luego pruebe un
`AD` o `DO` sin perfil local previo y confirme la creación de un VHDX en
`profiles`. No active el borrado de perfiles locales ni despliegue a todos los
`profiles`. Revise `%ProgramData%\FSLogix\Logs\Profile` para confirmar que el
perfil se registró sin `ERROR: 258`, perfil temporal ni retorno a `C:\Users`.
No active el borrado de perfiles locales ni despliegue a todos los
usuarios hasta probar inicio, cierre, desconexión de VPN, cuota y restauración.
Referencias de Microsoft:
@@ -156,4 +203,5 @@ Referencias de Microsoft:
- [Permisos SMB para FSLogix](https://learn.microsoft.com/fslogix/how-to-configure-storage-permissions)
- [Contenedores de perfil FSLogix](https://learn.microsoft.com/fslogix/how-to-configure-profile-containers)
- [Configuración ObjectSpecific de FSLogix](https://learn.microsoft.com/fslogix/how-to-configure-object-specific-settings)
- [Cloud Cache de FSLogix](https://learn.microsoft.com/fslogix/concepts-fslogix-cloud-cache)
- [Redirección de carpetas](https://learn.microsoft.com/windows-server/storage/folder-redirection/deploy-folder-redirection)
+81 -2
View File
@@ -23,6 +23,8 @@ param(
[string]$StaffGpoName = 'SGU - AD-DO FSLogix profiles',
[ValidateSet('Private', 'Public')]
[string]$EndpointAccess = 'Private',
[ValidateSet('Auto', 'Direct', 'CloudCache')]
[string]$StaffProfileStorageMode = 'Auto',
[ValidateRange(1024, 1048576)]
[int]$FsLogixProfileSizeMiB = 30000,
[string]$AzFilesHybridModulePath,
@@ -161,6 +163,36 @@ function Set-SguGpoRegistryValue {
}
}
function Remove-SguGpoRegistryValue {
param(
[Parameter(Mandatory)][string]$GpoName,
[Parameter(Mandatory)][string]$DomainName,
[Parameter(Mandatory)][string]$Server,
[Parameter(Mandatory)][string]$Key,
[Parameter(Mandatory)][string]$ValueName
)
if ($PSCmdlet.ShouldProcess("$GpoName :: $Key\$ValueName", 'Remove obsolete policy value')) {
Remove-GPRegistryValue -Name $GpoName -Domain $DomainName -Server $Server `
-Key $Key -ValueName $ValueName -ErrorAction SilentlyContinue
}
}
function Set-SguGpoRegistryValueDeletion {
param(
[Parameter(Mandatory)][string]$GpoName,
[Parameter(Mandatory)][string]$DomainName,
[Parameter(Mandatory)][string]$Server,
[Parameter(Mandatory)][string]$Key,
[Parameter(Mandatory)][string]$ValueName
)
Remove-SguGpoRegistryValue -GpoName $GpoName -DomainName $DomainName -Server $Server `
-Key $Key -ValueName $ValueName
Set-SguGpoRegistryValue -GpoName $GpoName -DomainName $DomainName -Server $Server `
-Key $Key -ValueName "**del.$ValueName" -Type String -Value ''
}
function Get-SguUnusedDriveName {
$used = @(Get-PSDrive -PSProvider FileSystem | Select-Object -ExpandProperty Name)
foreach ($name in @('Z', 'Y', 'X', 'W', 'V')) {
@@ -350,6 +382,12 @@ $storageCredential = [PSCredential]::new(
(ConvertTo-SecureString -String $storageKey -AsPlainText -Force))
$profilesSharePath = "\\$fileEndpointHost\$FsLogixProfilesShareName"
$redirectedFoldersSharePath = "\\$fileEndpointHost\$RedirectedFoldersShareName"
$resolvedStaffProfileStorageMode = if ($StaffProfileStorageMode -eq 'Auto') {
if ($EndpointAccess -eq 'Public') { 'CloudCache' } else { 'Direct' }
}
else {
$StaffProfileStorageMode
}
$perUserRootRights = [Security.AccessControl.FileSystemRights]::CreateDirectories -bor
[Security.AccessControl.FileSystemRights]::ListDirectory -bor
[Security.AccessControl.FileSystemRights]::ReadAttributes -bor
@@ -407,12 +445,47 @@ $fsLogixValues = [ordered]@{
ReAttachIntervalSeconds = @{ Type = 'DWord'; Value = 15 }
ReAttachRetryCount = @{ Type = 'DWord'; Value = 3 }
SizeInMBs = @{ Type = 'DWord'; Value = $FsLogixProfileSizeMiB }
VHDLocations = @{ Type = 'String'; Value = $profilesSharePath }
VolumeType = @{ Type = 'String'; Value = 'VHDX' }
}
$cloudCacheOnlyValues = @(
'CCDLocations',
'ClearCacheOnLogoff',
'HealthyProvidersRequiredForRegister',
'HealthyProvidersRequiredForUnregister',
'PreventLoginWithFailure',
'PreventLoginWithTempProfile',
'VHDCompactDisk'
)
if ($resolvedStaffProfileStorageMode -eq 'CloudCache') {
$fsLogixValues.CCDLocations = @{
Type = 'String'
Value = "type=smb,name=`"SGU Azure Files`",connectionString=$profilesSharePath"
}
$fsLogixValues.ClearCacheOnLogoff = @{ Type = 'DWord'; Value = 0 }
$fsLogixValues.HealthyProvidersRequiredForRegister = @{ Type = 'DWord'; Value = 1 }
$fsLogixValues.HealthyProvidersRequiredForUnregister = @{ Type = 'DWord'; Value = 1 }
$fsLogixValues.PreventLoginWithFailure = @{ Type = 'DWord'; Value = 1 }
$fsLogixValues.PreventLoginWithTempProfile = @{ Type = 'DWord'; Value = 1 }
$fsLogixValues.VHDCompactDisk = @{ Type = 'DWord'; Value = 0 }
}
else {
$fsLogixValues.VHDLocations = @{ Type = 'String'; Value = $profilesSharePath }
}
foreach ($staffGroup in @($professorGroup, $administrativeGroup)) {
$objectSpecificKey = "$fsLogixRoot\ObjectSpecific\$($staffGroup.SID.Value)"
if ($resolvedStaffProfileStorageMode -eq 'CloudCache') {
Set-SguGpoRegistryValueDeletion -GpoName $staffGpo.DisplayName -DomainName $domainName `
-Server $DomainController -Key $objectSpecificKey -ValueName 'VHDLocations'
}
else {
foreach ($obsoleteValue in $cloudCacheOnlyValues) {
Set-SguGpoRegistryValueDeletion -GpoName $staffGpo.DisplayName -DomainName $domainName `
-Server $DomainController -Key $objectSpecificKey -ValueName $obsoleteValue
}
}
foreach ($setting in $fsLogixValues.GetEnumerator()) {
Remove-SguGpoRegistryValue -GpoName $staffGpo.DisplayName -DomainName $domainName `
-Server $DomainController -Key $objectSpecificKey -ValueName "**del.$($setting.Key)"
Set-SguGpoRegistryValue -GpoName $staffGpo.DisplayName -DomainName $domainName `
-Server $DomainController -Key $objectSpecificKey -ValueName $setting.Key `
-Type $setting.Value.Type -Value $setting.Value.Value
@@ -423,6 +496,7 @@ foreach ($staffGroup in @($professorGroup, $administrativeGroup)) {
StorageAccountName = $StorageAccountName
FileEndpoint = $fileEndpointHost
EndpointAccess = $EndpointAccess
StaffProfileStorageMode = $resolvedStaffProfileStorageMode
EndpointAddresses = $endpointAddresses
PrivateEndpointAddresses = $privateAddresses
DirectoryService = $directoryService
@@ -434,6 +508,11 @@ foreach ($staffGroup in @($professorGroup, $administrativeGroup)) {
StudentPolicy = $studentGpo.DisplayName
StaffPolicy = $staffGpo.DisplayName
StudentBehavior = 'Local non-authoritative profile; Documents and Desktop redirected without Offline Files pinning.'
StaffBehavior = 'FSLogix VHDX profile container for SGU-Docentes and SGU-Administrativos only.'
StaffBehavior = if ($resolvedStaffProfileStorageMode -eq 'CloudCache') {
'FSLogix Cloud Cache profile container for SGU-Docentes and SGU-Administrativos only; local I/O is synchronized to Azure Files.'
}
else {
'Direct FSLogix VHDX profile container for SGU-Docentes and SGU-Administrativos only.'
}
ExistingStaffLocalProfilesDeleted = [bool]$DeleteExistingStaffLocalProfiles
}
+1
View File
@@ -201,6 +201,7 @@ $serverScripts = @(
'Get-SguUsageReport.ps1',
'Get-SguBrokerLog.ps1',
'Install-SguDomainMonitoring.ps1',
'Publish-SguFsLogixClientDeployment.ps1',
'Install-SguRustDeskClient.ps1',
'Install-SguRustDeskLinuxEnrollment.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'
}
+29
View File
@@ -3,6 +3,7 @@ $bicepPath = Join-Path $repositoryRoot 'infra\azure\main.bicep'
$deploymentPath = Join-Path $repositoryRoot 'scripts\Deploy-SguAzureInfrastructure.ps1'
$configurationPath = Join-Path $repositoryRoot 'scripts\Enable-SguAzureUserRoaming.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'
$azureLauncherPath = Join-Path $repositoryRoot 'scripts\Start-SguAzureClientEnrollment.cmd'
$packagePath = Join-Path $repositoryRoot 'scripts\New-SguBootstrapPackages.ps1'
@@ -11,6 +12,7 @@ foreach ($scriptPath in @(
$deploymentPath,
$configurationPath,
$installerPath,
$gpoDeploymentPath,
$clientBootstrapPath,
$packagePath)) {
$tokens = $null
@@ -87,6 +89,21 @@ Describe 'SGU role-specific roaming policies' {
$source | Should Match 'selected \$EndpointAccess endpoint'
}
It 'uses local Cloud Cache automatically for public WAN profile storage' {
(((Get-Command $configurationPath).Parameters['StaffProfileStorageMode'].Attributes |
Where-Object { $_ -is [Management.Automation.ValidateSetAttribute] }).ValidValues -join ',') |
Should Be 'Auto,Direct,CloudCache'
$source = Get-Content -LiteralPath $configurationPath -Raw
$source | Should Match "EndpointAccess -eq 'Public'\) \{ 'CloudCache' \}"
$source | Should Match 'CCDLocations'
$source | Should Match 'type=smb,name='
$source | Should Match "ValueName 'VHDLocations'"
$source | Should Match '\*\*del\.\$ValueName'
$source | Should Match 'HealthyProvidersRequiredForRegister'
$source | Should Match 'PreventLoginWithFailure'
$source | Should Match 'VHDCompactDisk'
}
It 'uses an AD computer identity with AES-256 Kerberos' {
$source = Get-Content -LiteralPath $configurationPath -Raw
$source | Should Match "DomainAccountType = 'ComputerAccount'"
@@ -162,5 +179,17 @@ Describe 'SGU FSLogix image enrollment' {
$source = Get-Content -LiteralPath $packagePath -Raw
$source | Should Match "'Install-SguFsLogix\.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'
}
}