Montag, 22. Juni 2026

How to build perfect Home Directory on Netapp

This post collect some thoughts how to run the perfect  quoted "personal" shares for Windows Fatclients / Citrix / Horizon / FSLogix

What Model will you choose ?

How many Users do you have ?

10-100 : Keep it simple.
100-5000 : Low enough to scale in single instances.
5000+ : You need to scale to several instances.

Do you have several environments (Citrix Farms, huge departments with dedicated IT teams, several AD Domains) ?

No : Perfect, one to fit all is easier.  
Yes : Consider volume or qtree per SILO.

Now the KnowHow

classify your user into "use cases"

Instead of managing a quote for every user create qtrees for "Use Cases" and put a separate quota on it, instead of setting quotas you (better a 1st level agent) just copy the profile to another qtree. The homefolder search path feature will lead the user to the qtree where his profile is.


Unfortunatly most Citrix / Windows / FSLogic policy voodoo don't like the %USERNAME% variable as name for a share, but if you use a static share name Windows will say 2 users accessing the same UNC Path of a file will cause a lock, the trick to avoid this is to share recursivly the folder where the Folder %USERNAME% by adding /.. to the path ending with %w - %w/.. .

Use DFS-N to stay flexible and prevent pit fights about responsibilities

You don't want to coordinate with several departments changing paths in all their tools and policies, DFS-N will avoid you ALOT trouble and add some "benefit". 

The Problem with the search-pattern based share is that SOMEONE has to create the profile folder first before the share will even work for the user - but if creator != user the creator has to prepare the ACLs on the folder too. 

Just misuse DFS-N failover for the onboarding process. You build up a "low quota" search path - where every new user will start, just create a nonbrowseable welcome$ share on this path and define it as secondary target for the DFS-N Path. A fresh user trying to access the \\fileserver\home\ share will fallback to \fileserver\welcome$ and create his folder there, next time he access the DFS-N link the search paths will return the qtree with the lowest quota and succeed.

But be aware that if someone still use the classic Users and Computers Active Directory MMC the Path will created in his "Use Case" qtree and will has him as owner.  

Keep control over the structure (special folders / trashcans / language chaos)

TBD : Reg Keys for "Well-known Folders" and Recycle Bin - desktop.ini voodoo

MONITOR everything - really EVERYTHING

TBD : REST-API Links  

Setting self managing ACLs

This little function will set the right ACLs on all search pattern paths you define earlier.

function Set-ProfileFolderRoot {
    param(
        [Parameter(Mandatory = $True, Position = 1)]
        [ValidateScript( { $DirInfo = [System.IO.DirectoryInfo]"$_"; $DirInfo.Exists -and ($DirInfo.Mode -match '^d') -and ($DirInfo.FullName -ne $DirInfo.Root) })]
        [String]$ShareRootPath,
        [Parameter(Position = 2)][String[]]$ProfileRoot,
        [Parameter()][String]$AdminNTGroup = "DOMAIN\StorageAdmins"
    )


    If (-not $PSBoundParameters['ProfileRoot']) {
        $RootACL = [System.Security.AccessControl.DirectorySecurity]::new()
        $RootACL.AddAccessRule([System.Security.AccessControl.FileSystemAccessRule]::new([System.Security.Principal.SecurityIdentifier]"S-1-5-32-544",2032127,3,0,0))
        $RootACL.AddAccessRule([System.Security.AccessControl.FileSystemAccessRule]::new([System.Security.Principal.NTAccount]$AdminNTGroup,2032127,3,0,0))
        $RootACL.AddAccessRule( [System.Security.Principal.SecurityIdentifier]"S-1-1-0",131241,0,0,0)
        $RootACL.SetAccessRuleProtection(1, 0)
        [System.IO.Directory]::SetAccessControl($ShareRootPath, $RootACL)
        $ProfileRoot = ([System.IO.DirectoryInfo]$ShareRootPath).GetDirectories().Name
    }
    else {
        $ProfileRoot.Where( { -not $([System.IO.DirectoryInfo]"$($ShareRootPath.TrimEnd('\'))\$_").Exists }) | ForEach-Object {
            [System.IO.Directory]::CreateDirectory("$($ShareRootPath.TrimEnd('\'))\$_")
        }
    }

    $ProfileRoot.ForEach( {
            $ProfileACL = [System.Security.AccessControl.DirectorySecurity]::new()
            $ProfileACL.AddAccessRule([System.Security.AccessControl.FileSystemAccessRule]::new([System.Security.Principal.SecurityIdentifier]"S-1-5-32-544",2032127,3,0,0))
            $ProfileACL.AddAccessRule([System.Security.AccessControl.FileSystemAccessRule]::new([System.Security.Principal.NTAccount]$AdminNTGroup,2032127,3,0,0))
            $ProfileACL.AddAccessRule([System.Security.AccessControl.FileSystemAccessRule]::new([System.Security.Principal.SecurityIdentifier]"S-1-3-0",197055,3,0,0))
            $ProfileACL.AddAccessRule([System.Security.AccessControl.FileSystemAccessRule]::new([System.Security.Principal.SecurityIdentifier]"S-1-1-0",131245,0,0,0))
            $ProfileACL.SetAccessRuleProtection(1, 0)
            [System.IO.Directory]::SetAccessControl("$($ShareRootPath.TrimEnd('\'))\$_", $ProfileACL)
        } )

}

Keine Kommentare:

Kommentar veröffentlichen