$repositoryRoot = (Resolve-Path (Join-Path $PSScriptRoot '..')).Path $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' foreach ($scriptPath in @( $deploymentPath, $configurationPath, $installerPath, $gpoDeploymentPath, $clientBootstrapPath, $packagePath)) { $tokens = $null $parseErrors = $null [Management.Automation.Language.Parser]::ParseFile( $scriptPath, [ref]$tokens, [ref]$parseErrors) | Out-Null if ($parseErrors.Count -gt 0) { throw "$scriptPath contains PowerShell parser errors: $($parseErrors -join '; ')" } } $configurationTokens = $null $configurationParseErrors = $null $configurationAst = [Management.Automation.Language.Parser]::ParseFile( $configurationPath, [ref]$configurationTokens, [ref]$configurationParseErrors) $samFunction = $configurationAst.Find({ param($node) $node -is [Management.Automation.Language.FunctionDefinitionAst] -and $node.Name -eq 'Get-SguStorageSamAccountName' }, $true) Invoke-Expression $samFunction.Extent.Text Describe 'SGU Azure user-roaming infrastructure' { It 'deploys dedicated private Azure Files resources by default' { $source = Get-Content -LiteralPath $bicepPath -Raw $source | Should Match 'param deployUserRoaming bool = true' $source | Should Match "purpose: 'SGU-user-roaming'" $source | Should Match "publicNetworkAccess: 'Disabled'" $source | Should Match "privateLinkServiceId: userRoamingStorageAccount.id" $source | Should Match "'file'" $source | Should Match "privatelink\.file\.\$\{storageEndpointSuffix\}" $source | Should Match 'dhcpOptions:' $source | Should Match 'dnsServers:' $source | Should Match 'domainControllerPrivateIp' $source | Should Match 'fsLogixProfilesShare' $source | Should Match 'redirectedFoldersShare' } It 'does not repurpose the temporary bootstrap staging account' { (Get-Content -LiteralPath $bicepPath -Raw) | Should Not Match 'sgustage|SGU-bootstrap-staging' } It 'requires a private P2S route when roaming is enabled' { $source = Get-Content -LiteralPath $deploymentPath -Raw $source | Should Match '\$DeployUserRoaming -and -not \$DeployVpnGateway' $source | Should Match 'private Azure Files endpoint' } It 'returns every post-domain setup value from the Azure deployment' { $source = Get-Content -LiteralPath $deploymentPath -Raw foreach ($name in @( 'UserRoamingEnabled', 'UserRoamingStorageAccountName', 'FsLogixProfilesSharePath', 'RedirectedFoldersSharePath', 'UserRoamingSetupArguments')) { $source | Should Match $name } } } Describe 'SGU role-specific roaming policies' { It 'supports an explicitly selected public SMB endpoint while keeping private as the default' { (((Get-Command $configurationPath).Parameters['EndpointAccess'].Attributes | Where-Object { $_ -is [Management.Automation.ValidateSetAttribute] }).ValidValues -join ',') | Should Be 'Private,Public' $source = Get-Content -LiteralPath $configurationPath -Raw $source | Should Match ([regex]::Escape("[string]`$EndpointAccess = 'Private'")) $source | Should Match ([regex]::Escape("`$EndpointAccess -eq 'Private'")) $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'" $source | Should Match 'KerberosEncryptionType AES256' $source | Should Match 'PasswordNeverExpires \$true' $source | Should Match 'StorageFileDataSmbShareContributor' } It 'isolates per-user directories with CREATOR OWNER ACLs' { $source = Get-Content -LiteralPath $configurationPath -Raw $source | Should Match "SecurityIdentifier\]::new\('S-1-3-0'\)" $source | Should Match 'PropagationFlags\]::InheritOnly' $source | Should Match 'SetAccessRuleProtection\(\$true, \$false\)' $source | Should Match '\$perUserRootRights' $source | Should Match 'CreateDirectories' $source | Should Match 'ContributorRights \$perUserRootRights' } It 'redirects only the student Desktop and Documents folders' { $source = Get-Content -LiteralPath $configurationPath -Raw $source | Should Match "ValueName 'Desktop'" $source | Should Match "ValueName 'Personal'" $source | Should Match '%USERNAME%\\Desktop' $source | Should Match '%USERNAME%\\Documents' $source | Should Match "ValueName 'DisableFRAdminPin'" } It 'enables FSLogix only through the AD and DO group SIDs' { $source = Get-Content -LiteralPath $configurationPath -Raw $source | Should Match "\$fsLogixRoot = 'HKLM\\SOFTWARE\\FSLogix\\Profiles'" $source | Should Match '\\ObjectSpecific\\\$\(\$staffGroup\.SID\.Value\)' $source | Should Match '\$professorGroup, \$administrativeGroup' $source | Should Match "ValueName 'Enabled' -Type DWord -Value 0" $source | Should Match 'VHDLocations = @\{ Type = ''String''; Value = \$profilesSharePath \}' } It 'does not delete existing staff profiles unless explicitly requested' { $source = Get-Content -LiteralPath $configurationPath -Raw $source | Should Match '\[switch\]\$DeleteExistingStaffLocalProfiles' $source | Should Match 'if \(\$DeleteExistingStaffLocalProfiles\) \{ 1 \} else \{ 0 \}' } It 'derives valid deterministic AD names for long storage account names' { $name = Get-SguStorageSamAccountName -StorageName 'abcdefghijklmnopqrstuvwx' $name.Length | Should Be 15 $name | Should Match '^sgufs[0-9a-f]{10}$' (Get-SguStorageSamAccountName -StorageName 'abcdefghijklmnopqrstuvwx') | Should Be $name (Get-SguStorageSamAccountName -StorageName 'sguroam1234567') | Should Be 'sguroam1234567' } } Describe 'SGU FSLogix image enrollment' { It 'verifies the Microsoft signature and installs unattended' { $source = Get-Content -LiteralPath $installerPath -Raw $source | Should Match 'Get-AuthenticodeSignature' $source | Should Match 'CN=Microsoft Corporation' foreach ($argument in @('/install', '/quiet', '/norestart')) { $source | Should Match ([regex]::Escape($argument)) } $source | Should Match "Get-Service -Name frxsvc" } It 'exposes the optional installer through the unified Azure launcher' { ((Get-Command $clientBootstrapPath).Parameters.Keys -contains 'FsLogixInstallerPath') | Should Be $true $launcher = Get-Content -LiteralPath $azureLauncherPath -Raw $launcher | Should Match 'SGU_FSLOGIX_INSTALLER=%~5' $launcher | Should Match '-FsLogixInstallerPath' } It 'packages both roaming setup scripts' { $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' } }