Compare commits

...
2 Commits
Author SHA1 Message Date
alexrg 01e0ee5ac4 Bound FSLogix Cloud Cache sign-out time 2026-09-18 11:24:33 -06:00
alexrg c47913e81d Use FSLogix Cloud Cache for public profile storage 2026-09-18 11:09:44 -06:00
6 changed files with 193 additions and 8 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.
+44
View File
@@ -0,0 +1,44 @@
# 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.
- El cierre de sesión deja de esperar indefinidamente por un proveedor remoto:
se limita a 60 segundos y conserva la caché local si vence el plazo.
- 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.
+37 -4
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,24 @@ 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. Si el proveedor no completa la
sincronización, el cierre espera como máximo 60 segundos y después conserva la
caché local para reintentar; no la descarta. 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 +115,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:
@@ -156,15 +182,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:
@@ -173,4 +205,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)
+88 -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,54 @@ $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',
'CcdUnregisterTimeout',
'ClearCacheOnForcedUnregister',
'ClearCacheOnLogoff',
'HealthyProvidersRequiredForRegister',
'HealthyProvidersRequiredForUnregister',
'PreventLoginWithFailure',
'PreventLoginWithTempProfile'
)
if ($resolvedStaffProfileStorageMode -eq 'CloudCache') {
$fsLogixValues.CCDLocations = @{
Type = 'String'
Value = "type=smb,name=`"SGU Azure Files`",connectionString=$profilesSharePath"
}
$fsLogixValues.CcdUnregisterTimeout = @{ Type = 'DWord'; Value = 60 }
$fsLogixValues.ClearCacheOnForcedUnregister = @{ Type = 'DWord'; Value = 0 }
$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 }
}
else {
$fsLogixValues.VHDLocations = @{ Type = 'String'; Value = $profilesSharePath }
}
Set-SguGpoRegistryValue -GpoName $staffGpo.DisplayName -DomainName $domainName `
-Server $DomainController -Key 'HKLM\SOFTWARE\FSLogix\Apps' `
-ValueName 'VHDCompactDisk' -Type DWord -Value 0
foreach ($staffGroup in @($professorGroup, $administrativeGroup)) {
$objectSpecificKey = "$fsLogixRoot\ObjectSpecific\$($staffGroup.SID.Value)"
Set-SguGpoRegistryValueDeletion -GpoName $staffGpo.DisplayName -DomainName $domainName `
-Server $DomainController -Key $objectSpecificKey -ValueName 'VHDCompactDisk'
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 +503,7 @@ foreach ($staffGroup in @($professorGroup, $administrativeGroup)) {
StorageAccountName = $StorageAccountName
FileEndpoint = $fileEndpointHost
EndpointAccess = $EndpointAccess
StaffProfileStorageMode = $resolvedStaffProfileStorageMode
EndpointAddresses = $endpointAddresses
PrivateEndpointAddresses = $privateAddresses
DirectoryService = $directoryService
@@ -434,6 +515,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
}
+18
View File
@@ -89,6 +89,24 @@ 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 'CcdUnregisterTimeout.*60'
$source | Should Match 'ClearCacheOnForcedUnregister'
$source | Should Match 'PreventLoginWithFailure'
$source | Should Match "'HKLM\\SOFTWARE\\FSLogix\\Apps'"
$source | Should Match "ValueName 'VHDCompactDisk'"
}
It 'uses an AD computer identity with AES-256 Kerberos' {
$source = Get-Content -LiteralPath $configurationPath -Raw
$source | Should Match "DomainAccountType = 'ComputerAccount'"