519 lines
24 KiB
PowerShell
519 lines
24 KiB
PowerShell
#Requires -Version 5.1
|
|
[CmdletBinding(SupportsShouldProcess)]
|
|
param(
|
|
[Parameter(Mandatory)][guid]$SubscriptionId,
|
|
[Parameter(Mandatory)][string]$ResourceGroupName,
|
|
[Parameter(Mandatory)]
|
|
[ValidatePattern('^[a-z0-9]{3,24}$')]
|
|
[string]$StorageAccountName,
|
|
[ValidatePattern('^[a-z0-9](?:[a-z0-9-]{1,61}[a-z0-9])$')]
|
|
[string]$FsLogixProfilesShareName = 'profiles',
|
|
[ValidatePattern('^[a-z0-9](?:[a-z0-9-]{1,61}[a-z0-9])$')]
|
|
[string]$RedirectedFoldersShareName = 'redirected',
|
|
[string]$DomainController = $env:COMPUTERNAME,
|
|
[string]$AzureFilesOuName = 'AzureFilesConfig',
|
|
[string]$StudentOuName = 'Alumnos',
|
|
[string]$ProfessorOuName = 'Docentes',
|
|
[string]$AdministrativeOuName = 'Administrativos',
|
|
[string]$LaboratoryOuName = 'Laboratorio',
|
|
[string]$StudentGroupName = 'SGU-Alumnos',
|
|
[string]$ProfessorGroupName = 'SGU-Docentes',
|
|
[string]$AdministrativeGroupName = 'SGU-Administrativos',
|
|
[string]$StudentGpoName = 'SGU - AL redirected folders',
|
|
[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,
|
|
[switch]$UseDeviceAuthentication,
|
|
[switch]$DeleteExistingStaffLocalProfiles
|
|
)
|
|
|
|
$ErrorActionPreference = 'Stop'
|
|
|
|
function Assert-Administrator {
|
|
$identity = [Security.Principal.WindowsIdentity]::GetCurrent()
|
|
$principal = [Security.Principal.WindowsPrincipal]::new($identity)
|
|
if (-not $principal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) {
|
|
throw 'Run this script from an elevated Windows PowerShell 5.1 session on the SGU domain controller.'
|
|
}
|
|
}
|
|
|
|
function Import-SguAzFilesHybrid {
|
|
param([string]$ModulePath)
|
|
|
|
if ($ModulePath) {
|
|
if (-not (Test-Path -LiteralPath $ModulePath)) {
|
|
throw "AzFilesHybridModulePath does not exist: $ModulePath"
|
|
}
|
|
$resolvedModule = if (Test-Path -LiteralPath $ModulePath -PathType Container) {
|
|
Get-ChildItem -LiteralPath $ModulePath -Recurse -File |
|
|
Where-Object Name -in @('AzFilesHybrid.psd1', 'AzFilesHybrid.psm1') |
|
|
Sort-Object @{ Expression = { $_.Extension -eq '.psd1' }; Descending = $true }, FullName |
|
|
Select-Object -First 1
|
|
}
|
|
else {
|
|
Get-Item -LiteralPath $ModulePath
|
|
}
|
|
if (-not $resolvedModule) {
|
|
throw "AzFilesHybrid.psd1 or AzFilesHybrid.psm1 was not found beneath $ModulePath."
|
|
}
|
|
Import-Module -Name $resolvedModule.FullName -Force -ErrorAction Stop
|
|
}
|
|
else {
|
|
Import-Module -Name AzFilesHybrid -Force -ErrorAction Stop
|
|
}
|
|
|
|
$joinCommand = Get-Command Join-AzStorageAccount -ErrorAction SilentlyContinue
|
|
if (-not $joinCommand) {
|
|
$joinCommand = Get-Command Join-AzStorageAccountForAuth -ErrorAction SilentlyContinue
|
|
}
|
|
if (-not $joinCommand) {
|
|
throw 'AzFilesHybrid did not expose Join-AzStorageAccount. Install the current Microsoft AzFilesHybrid module and retry.'
|
|
}
|
|
return $joinCommand
|
|
}
|
|
|
|
function Get-SguStorageSamAccountName {
|
|
param([Parameter(Mandatory)][string]$StorageName)
|
|
|
|
if ($StorageName.Length -le 15) {
|
|
return $StorageName
|
|
}
|
|
$sha256 = [Security.Cryptography.SHA256]::Create()
|
|
try {
|
|
$hash = $sha256.ComputeHash([Text.Encoding]::UTF8.GetBytes($StorageName))
|
|
$suffix = ([BitConverter]::ToString($hash) -replace '-', '').Substring(0, 10).ToLowerInvariant()
|
|
return "sgufs$suffix"
|
|
}
|
|
finally {
|
|
$sha256.Dispose()
|
|
}
|
|
}
|
|
|
|
function Ensure-SguOrganizationalUnit {
|
|
param(
|
|
[Parameter(Mandatory)][string]$Name,
|
|
[Parameter(Mandatory)][string]$Path,
|
|
[Parameter(Mandatory)][string]$Server
|
|
)
|
|
|
|
$escapedName = $Name.Replace('\', '\5c').Replace('*', '\2a').Replace('(', '\28').Replace(')', '\29')
|
|
$ou = Get-ADOrganizationalUnit -LDAPFilter "(ou=$escapedName)" -SearchBase $Path `
|
|
-SearchScope OneLevel -Server $Server -ErrorAction Stop | Select-Object -First 1
|
|
if (-not $ou -and $PSCmdlet.ShouldProcess("OU=$Name,$Path", 'Create Azure Files identity OU')) {
|
|
New-ADOrganizationalUnit -Name $Name -Path $Path -ProtectedFromAccidentalDeletion $true `
|
|
-Server $Server | Out-Null
|
|
$ou = Get-ADOrganizationalUnit -Identity "OU=$Name,$Path" -Server $Server
|
|
}
|
|
if (-not $ou) {
|
|
throw "The organizational unit OU=$Name,$Path does not exist."
|
|
}
|
|
return $ou
|
|
}
|
|
|
|
function Ensure-SguGpoLink {
|
|
param(
|
|
[Parameter(Mandatory)][string]$Name,
|
|
[Parameter(Mandatory)][string]$TargetOuDn,
|
|
[Parameter(Mandatory)][string]$DomainName,
|
|
[Parameter(Mandatory)][string]$Server
|
|
)
|
|
|
|
$gpo = Get-GPO -Name $Name -Domain $DomainName -Server $Server -ErrorAction SilentlyContinue
|
|
if (-not $gpo -and $PSCmdlet.ShouldProcess($Name, 'Create user-roaming GPO')) {
|
|
$gpo = New-GPO -Name $Name -Domain $DomainName -Server $Server
|
|
}
|
|
if (-not $gpo) {
|
|
throw "The GPO '$Name' does not exist and was not created."
|
|
}
|
|
|
|
$link = @(Get-GPInheritance -Target $TargetOuDn -Domain $DomainName -Server $Server).GpoLinks |
|
|
Where-Object DisplayName -eq $Name | Select-Object -First 1
|
|
$linkEnabled = $link -and ($link.Enabled -eq $true -or [string]$link.Enabled -eq 'Yes')
|
|
if (-not $link -and $PSCmdlet.ShouldProcess($TargetOuDn, "Link and enable '$Name'")) {
|
|
New-GPLink -Name $Name -Target $TargetOuDn -Domain $DomainName -Server $Server `
|
|
-LinkEnabled Yes | Out-Null
|
|
}
|
|
elseif ($link -and -not $linkEnabled -and
|
|
$PSCmdlet.ShouldProcess($TargetOuDn, "Enable the '$Name' link")) {
|
|
Set-GPLink -Name $Name -Target $TargetOuDn -Domain $DomainName -Server $Server `
|
|
-LinkEnabled Yes | Out-Null
|
|
}
|
|
return $gpo
|
|
}
|
|
|
|
function Set-SguGpoRegistryValue {
|
|
param(
|
|
[Parameter(Mandatory)][string]$GpoName,
|
|
[Parameter(Mandatory)][string]$DomainName,
|
|
[Parameter(Mandatory)][string]$Server,
|
|
[Parameter(Mandatory)][string]$Key,
|
|
[Parameter(Mandatory)][string]$ValueName,
|
|
[Parameter(Mandatory)][ValidateSet('DWord', 'String', 'ExpandString')][string]$Type,
|
|
[Parameter(Mandatory)]$Value
|
|
)
|
|
|
|
if ($PSCmdlet.ShouldProcess("$GpoName :: $Key\\$ValueName", "Set $Type policy value")) {
|
|
Set-GPRegistryValue -Name $GpoName -Domain $DomainName -Server $Server `
|
|
-Key $Key -ValueName $ValueName -Type $Type -Value $Value | Out-Null
|
|
}
|
|
}
|
|
|
|
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')) {
|
|
if ($used -notcontains $name) {
|
|
return $name
|
|
}
|
|
}
|
|
throw 'No temporary drive letter is available for configuring Azure Files ACLs.'
|
|
}
|
|
|
|
function Set-SguAzureFileRootAcl {
|
|
param(
|
|
[Parameter(Mandatory)][string]$UncPath,
|
|
[Parameter(Mandatory)][PSCredential]$Credential,
|
|
[Parameter(Mandatory)][Security.Principal.SecurityIdentifier]$DomainAdminsSid,
|
|
[Parameter(Mandatory)][Security.Principal.SecurityIdentifier[]]$ContributorSids,
|
|
[Security.AccessControl.FileSystemRights]$ContributorRights =
|
|
[Security.AccessControl.FileSystemRights]::Modify
|
|
)
|
|
|
|
$driveName = Get-SguUnusedDriveName
|
|
try {
|
|
New-PSDrive -Name $driveName -PSProvider FileSystem -Root $UncPath `
|
|
-Credential $Credential -Scope Script -ErrorAction Stop | Out-Null
|
|
$rootPath = "${driveName}:\"
|
|
$acl = [Security.AccessControl.DirectorySecurity]::new()
|
|
$acl.SetAccessRuleProtection($true, $false)
|
|
$acl.SetOwner($DomainAdminsSid)
|
|
$allow = [Security.AccessControl.AccessControlType]::Allow
|
|
$containerAndObject = [Security.AccessControl.InheritanceFlags]'ContainerInherit,ObjectInherit'
|
|
$noneInheritance = [Security.AccessControl.InheritanceFlags]::None
|
|
$nonePropagation = [Security.AccessControl.PropagationFlags]::None
|
|
$inheritOnly = [Security.AccessControl.PropagationFlags]::InheritOnly
|
|
$systemSid = [Security.Principal.SecurityIdentifier]::new('S-1-5-18')
|
|
$creatorOwnerSid = [Security.Principal.SecurityIdentifier]::new('S-1-3-0')
|
|
foreach ($administratorSid in @($systemSid, $DomainAdminsSid)) {
|
|
$acl.AddAccessRule([Security.AccessControl.FileSystemAccessRule]::new(
|
|
$administratorSid,
|
|
[Security.AccessControl.FileSystemRights]::FullControl,
|
|
$containerAndObject,
|
|
$nonePropagation,
|
|
$allow))
|
|
}
|
|
$acl.AddAccessRule([Security.AccessControl.FileSystemAccessRule]::new(
|
|
$creatorOwnerSid,
|
|
[Security.AccessControl.FileSystemRights]::Modify,
|
|
$containerAndObject,
|
|
$inheritOnly,
|
|
$allow))
|
|
foreach ($contributorSid in $ContributorSids) {
|
|
$acl.AddAccessRule([Security.AccessControl.FileSystemAccessRule]::new(
|
|
$contributorSid,
|
|
$ContributorRights,
|
|
$noneInheritance,
|
|
$nonePropagation,
|
|
$allow))
|
|
}
|
|
Set-Acl -LiteralPath $rootPath -AclObject $acl -ErrorAction Stop
|
|
}
|
|
finally {
|
|
Remove-PSDrive -Name $driveName -Scope Script -Force -ErrorAction SilentlyContinue
|
|
}
|
|
}
|
|
|
|
Assert-Administrator
|
|
if ($FsLogixProfilesShareName.Contains('--') -or $RedirectedFoldersShareName.Contains('--')) {
|
|
throw 'Azure Files share names cannot contain consecutive hyphens.'
|
|
}
|
|
if ($FsLogixProfilesShareName -eq $RedirectedFoldersShareName) {
|
|
throw 'The profile-container and redirected-folder shares must have different names.'
|
|
}
|
|
|
|
Import-Module ActiveDirectory -ErrorAction Stop
|
|
Import-Module GroupPolicy -ErrorAction Stop
|
|
foreach ($azureModule in @('Az.Accounts', 'Az.Storage')) {
|
|
try {
|
|
Import-Module $azureModule -ErrorAction Stop
|
|
}
|
|
catch {
|
|
throw "The current $azureModule module is required on the domain controller. Install Azure PowerShell and retry. $($_.Exception.Message)"
|
|
}
|
|
}
|
|
$joinStorageCommand = Import-SguAzFilesHybrid -ModulePath $AzFilesHybridModulePath
|
|
|
|
$domain = Get-ADDomain -Server $DomainController
|
|
$baseDn = $domain.DistinguishedName
|
|
$domainName = $domain.DNSRoot
|
|
$usersOuDn = "OU=Usuarios-SGU,$baseDn"
|
|
$studentOuDn = "OU=$StudentOuName,$usersOuDn"
|
|
$professorOuDn = "OU=$ProfessorOuName,$usersOuDn"
|
|
$administrativeOuDn = "OU=$AdministrativeOuName,$usersOuDn"
|
|
$laboratoryOuDn = "OU=$LaboratoryOuName,$baseDn"
|
|
foreach ($requiredOu in @($studentOuDn, $professorOuDn, $administrativeOuDn, $laboratoryOuDn)) {
|
|
Get-ADOrganizationalUnit -Identity $requiredOu -Server $DomainController -ErrorAction Stop | Out-Null
|
|
}
|
|
|
|
$studentGroup = Get-ADGroup -Identity "CN=$StudentGroupName,$studentOuDn" -Server $DomainController
|
|
$professorGroup = Get-ADGroup -Identity "CN=$ProfessorGroupName,$professorOuDn" -Server $DomainController
|
|
$administrativeGroup = Get-ADGroup -Identity "CN=$AdministrativeGroupName,$administrativeOuDn" `
|
|
-Server $DomainController
|
|
$domainAdminsSid = [Security.Principal.SecurityIdentifier]::new("$($domain.DomainSID.Value)-512")
|
|
$azureFilesOu = Ensure-SguOrganizationalUnit -Name $AzureFilesOuName -Path $baseDn -Server $DomainController
|
|
|
|
$azureContext = Get-AzContext -ErrorAction SilentlyContinue
|
|
if (-not $azureContext -or $azureContext.Subscription.Id -ne $SubscriptionId.Guid) {
|
|
$connectParameters = @{}
|
|
if ($UseDeviceAuthentication) {
|
|
$connectParameters.UseDeviceAuthentication = $true
|
|
}
|
|
Connect-AzAccount @connectParameters | Out-Null
|
|
}
|
|
Set-AzContext -SubscriptionId $SubscriptionId.Guid | Out-Null
|
|
$storageAccount = Get-AzStorageAccount -ResourceGroupName $ResourceGroupName `
|
|
-Name $StorageAccountName -ErrorAction Stop
|
|
$fileEndpointHost = ([uri]$storageAccount.PrimaryEndpoints.File).Host
|
|
if (-not $fileEndpointHost) {
|
|
throw "Azure did not return a file endpoint for $StorageAccountName."
|
|
}
|
|
|
|
$directoryService = [string]$storageAccount.AzureFilesIdentityBasedAuth.DirectoryServiceOptions
|
|
if ($directoryService -and $directoryService -ne 'None' -and $directoryService -ne 'AD') {
|
|
throw "Storage account $StorageAccountName already uses the incompatible Azure Files identity source '$directoryService'."
|
|
}
|
|
if ($directoryService -ne 'AD') {
|
|
if ($PSCmdlet.ShouldProcess($StorageAccountName, "Join Azure Files to $domainName with AES-256 Kerberos")) {
|
|
$requestedSamAccountName = Get-SguStorageSamAccountName -StorageName $StorageAccountName
|
|
$joinParameters = @{
|
|
ResourceGroupName = $ResourceGroupName
|
|
StorageAccountName = $StorageAccountName
|
|
SamAccountName = $requestedSamAccountName
|
|
DomainAccountType = 'ComputerAccount'
|
|
OrganizationalUnitDistinguishedName = $azureFilesOu.DistinguishedName
|
|
}
|
|
& $joinStorageCommand @joinParameters
|
|
$storageAccount = Get-AzStorageAccount -ResourceGroupName $ResourceGroupName `
|
|
-Name $StorageAccountName -ErrorAction Stop
|
|
}
|
|
}
|
|
|
|
$directoryService = [string]$storageAccount.AzureFilesIdentityBasedAuth.DirectoryServiceOptions
|
|
if ($directoryService -ne 'AD') {
|
|
throw "Azure Files identity authentication is '$directoryService', not AD. The domain join did not complete."
|
|
}
|
|
$activeDirectoryProperties = $storageAccount.AzureFilesIdentityBasedAuth.ActiveDirectoryProperties
|
|
if ([string]$activeDirectoryProperties.DomainName -ne $domainName) {
|
|
throw "Storage account $StorageAccountName is joined to $($activeDirectoryProperties.DomainName), not $domainName."
|
|
}
|
|
$storageSamAccountName = [string]$activeDirectoryProperties.SamAccountName
|
|
if (-not $storageSamAccountName) {
|
|
$storageSamAccountName = $StorageAccountName
|
|
}
|
|
$storageComputer = Get-ADComputer -Identity "${storageSamAccountName}$" `
|
|
-Server $DomainController -ErrorAction Stop
|
|
if ($PSCmdlet.ShouldProcess($storageComputer.DistinguishedName, 'Require AES-256 Kerberos and prevent an unattended storage identity password expiry')) {
|
|
Set-ADComputer -Identity $storageComputer -Server $DomainController `
|
|
-KerberosEncryptionType AES256 -PasswordNeverExpires $true
|
|
}
|
|
if ($PSCmdlet.ShouldProcess($StorageAccountName, 'Grant authenticated AD identities the Azure Files SMB contributor default share permission')) {
|
|
$storageAccount = Set-AzStorageAccount -ResourceGroupName $ResourceGroupName `
|
|
-Name $StorageAccountName `
|
|
-DefaultSharePermission StorageFileDataSmbShareContributor
|
|
}
|
|
|
|
$endpointAddresses = @(Resolve-DnsName -Name $fileEndpointHost -Type A -ErrorAction Stop |
|
|
Where-Object IPAddress | Select-Object -ExpandProperty IPAddress)
|
|
if ($endpointAddresses.Count -eq 0) {
|
|
throw "$fileEndpointHost did not resolve to an IPv4 address."
|
|
}
|
|
$privateAddresses = @($endpointAddresses | Where-Object {
|
|
$_ -match '^(10\.|192\.168\.|172\.(1[6-9]|2\d|3[01])\.)'
|
|
})
|
|
if ($EndpointAccess -eq 'Private' -and $privateAddresses.Count -eq 0) {
|
|
throw "$fileEndpointHost did not resolve to a private endpoint. Verify the privatelink.file.core.windows.net VNet link and the DC Azure DNS forwarder."
|
|
}
|
|
if (-not (Test-NetConnection -ComputerName $fileEndpointHost -Port 445 -InformationLevel Quiet)) {
|
|
throw "The domain controller cannot reach $fileEndpointHost on TCP 445 using the selected $EndpointAccess endpoint."
|
|
}
|
|
|
|
$storageKey = @(Get-AzStorageAccountKey -ResourceGroupName $ResourceGroupName `
|
|
-Name $StorageAccountName -ErrorAction Stop | Where-Object KeyName -eq 'key1' |
|
|
Select-Object -First 1).Value
|
|
if (-not $storageKey) {
|
|
throw "Azure did not return key1 for $StorageAccountName; it is required only to set the initial root ACLs."
|
|
}
|
|
$storageCredential = [PSCredential]::new(
|
|
"Azure\$StorageAccountName",
|
|
(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
|
|
[Security.AccessControl.FileSystemRights]::ReadExtendedAttributes -bor
|
|
[Security.AccessControl.FileSystemRights]::ReadPermissions -bor
|
|
[Security.AccessControl.FileSystemRights]::Traverse -bor
|
|
[Security.AccessControl.FileSystemRights]::Synchronize
|
|
try {
|
|
if ($PSCmdlet.ShouldProcess($profilesSharePath, 'Apply isolated FSLogix root ACLs')) {
|
|
Set-SguAzureFileRootAcl -UncPath $profilesSharePath -Credential $storageCredential `
|
|
-DomainAdminsSid $domainAdminsSid `
|
|
-ContributorSids @($professorGroup.SID, $administrativeGroup.SID) `
|
|
-ContributorRights $perUserRootRights
|
|
}
|
|
if ($PSCmdlet.ShouldProcess($redirectedFoldersSharePath, 'Apply isolated student-folder root ACLs')) {
|
|
Set-SguAzureFileRootAcl -UncPath $redirectedFoldersSharePath -Credential $storageCredential `
|
|
-DomainAdminsSid $domainAdminsSid -ContributorSids @($studentGroup.SID) `
|
|
-ContributorRights $perUserRootRights
|
|
}
|
|
}
|
|
finally {
|
|
$storageKey = $null
|
|
$storageCredential = $null
|
|
}
|
|
|
|
$studentGpo = Ensure-SguGpoLink -Name $StudentGpoName -TargetOuDn $studentOuDn `
|
|
-DomainName $domainName -Server $DomainController
|
|
$userShellFoldersKey = 'HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders'
|
|
Set-SguGpoRegistryValue -GpoName $studentGpo.DisplayName -DomainName $domainName `
|
|
-Server $DomainController -Key $userShellFoldersKey -ValueName 'Desktop' `
|
|
-Type ExpandString -Value "$redirectedFoldersSharePath\%USERNAME%\Desktop"
|
|
Set-SguGpoRegistryValue -GpoName $studentGpo.DisplayName -DomainName $domainName `
|
|
-Server $DomainController -Key $userShellFoldersKey -ValueName 'Personal' `
|
|
-Type ExpandString -Value "$redirectedFoldersSharePath\%USERNAME%\Documents"
|
|
Set-SguGpoRegistryValue -GpoName $studentGpo.DisplayName -DomainName $domainName `
|
|
-Server $DomainController -Key 'HKCU\Software\Policies\Microsoft\Windows\NetCache' `
|
|
-ValueName 'DisableFRAdminPin' -Type DWord -Value 1
|
|
|
|
$staffGpo = Ensure-SguGpoLink -Name $StaffGpoName -TargetOuDn $laboratoryOuDn `
|
|
-DomainName $domainName -Server $DomainController
|
|
$fsLogixRoot = 'HKLM\SOFTWARE\FSLogix\Profiles'
|
|
Set-SguGpoRegistryValue -GpoName $staffGpo.DisplayName -DomainName $domainName `
|
|
-Server $DomainController -Key $fsLogixRoot -ValueName 'Enabled' -Type DWord -Value 0
|
|
$fsLogixValues = [ordered]@{
|
|
Enabled = @{ Type = 'DWord'; Value = 1 }
|
|
DeleteLocalProfileWhenVHDShouldApply = @{
|
|
Type = 'DWord'
|
|
Value = if ($DeleteExistingStaffLocalProfiles) { 1 } else { 0 }
|
|
}
|
|
FlipFlopProfileDirectoryName = @{ Type = 'DWord'; Value = 1 }
|
|
IsDynamic = @{ Type = 'DWord'; Value = 1 }
|
|
LockedRetryCount = @{ Type = 'DWord'; Value = 3 }
|
|
LockedRetryInterval = @{ Type = 'DWord'; Value = 15 }
|
|
ProfileType = @{ Type = 'DWord'; Value = 0 }
|
|
ReAttachIntervalSeconds = @{ Type = 'DWord'; Value = 15 }
|
|
ReAttachRetryCount = @{ Type = 'DWord'; Value = 3 }
|
|
SizeInMBs = @{ Type = 'DWord'; Value = $FsLogixProfileSizeMiB }
|
|
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
|
|
}
|
|
}
|
|
|
|
[pscustomobject]@{
|
|
StorageAccountName = $StorageAccountName
|
|
FileEndpoint = $fileEndpointHost
|
|
EndpointAccess = $EndpointAccess
|
|
StaffProfileStorageMode = $resolvedStaffProfileStorageMode
|
|
EndpointAddresses = $endpointAddresses
|
|
PrivateEndpointAddresses = $privateAddresses
|
|
DirectoryService = $directoryService
|
|
KerberosEncryption = 'AES256'
|
|
StorageIdentity = $storageComputer.DistinguishedName
|
|
StorageIdentityPasswordNeverExpires = $true
|
|
ProfilesSharePath = $profilesSharePath
|
|
RedirectedFoldersSharePath = $redirectedFoldersSharePath
|
|
StudentPolicy = $studentGpo.DisplayName
|
|
StaffPolicy = $staffGpo.DisplayName
|
|
StudentBehavior = 'Local non-authoritative profile; Documents and Desktop redirected without Offline Files pinning.'
|
|
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
|
|
}
|