#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' }