Automatically clean up FSLogix Profile Containers – The Cloud Admin (2024)

For me, FSLogix profile containers are an indispensable tool for managing roaming profiles on Azure Virtual Desktop. Of course, it is not only useful for AVD, but it is a great combination, especially when also harnessing the power of Azure Files shares.

I am lazy, I have made “set once and forget” my life’s mission and true purpose and I want others to also have that bliss in their lives. That means as a project consultant, I do not want to just set up FSLogix and let the sysadmin deal with the accumulation of stale profile containers.

Jeroen Tielen created a cool script that does some of the things I wanted it to do, but not all of it.

That is why I wrote the script below to deal with the stale containers instead. It cross-checks the existing containers with users from Active Directory and then deletes eligible containers, based on the input you give it.

It has support for

  • Deleting profiles based on (a combination of) the below criteria:
    • Removed – Delete profiles owned by removed/non-existing users.
    • Disabled state – Delete profiles owned by disabled user accounts.
    • Inactivity – Delete profiles owned by user accounts that have not logged on for x days.
  • Logging to console and/or log file.
  • Use of the FlipFlop FSLogix parameter.
  • WhatIf/simulation mode to see what would happen by using the -WhatIf switch.
  • Folder exclusion by using the -ExcludeFolders switch.
  • Unattended run by using the -Confirm switch.

I use it myself to delete all removed users’ profile containers and leave the inactive and disabled users alone. You know how HR works, before you know it, they have changed their minds about booting someone out. The parameter set I use for that task are as follows:

.\script.ps1 -ContainerPath "\\mystorageaccount.file.core.windows.net\share" -DeleteRemoved -LogPath "C:\Logs" -Confirm

The latest version of the script and its licence are also available on my GitHub.

<#.SYNOPSIS Finds all FSlogix folders in specified directory and cross checks if the user is disabled, exists and/or is inactive. Based on specified parameters, will then remove the stale containers from the directory..DESCRIPTION Will automatically clean up stale FSLogix container folders based on the specified criteria..PARAMETER * Parameters are available as specified in the param block below..NOTES Version: 1.1 Author: Tom Schoen Creation Date: 01-11-2022 Purpose/Change: Add logging functionality .EXAMPLE Remove all containers for disabled, removed/non-existent and inactive users but exclude folders "folder1" and "folder2" from location "F:\" and output logs to "C:\temp". .\script.ps1 -ContainerPath "F:\" -DeleteDisabled -DeleteRemoved -DeleteInactive -ExcludeFolders @("folder1","folder2") -LogPath "C:\temp".EXAMPLE Remove all containers for disabled users from Azure Files share "\\mystorageaccount.file.core.windows.net\share" and don't ask for confirmation. .\script.ps1 -ContainerPath "\\mystorageaccount.file.core.windows.net\share" -DeleteDisabled -Confirm.EXAMPLE Dry run for removal of all containers for users that have not logged in for 30 days from Azure Files share "\\mystorageaccount.file.core.windows.net\share". .\script.ps1 -ContainerPath "\\mystorageaccount.file.core.windows.net\share" -DeleteInactive -InactiveDays 30 -WhatIf#>[CmdletBinding()]param( [Parameter(Mandatory, HelpMessage = "The full (UNC) path to the FSLogix container directory.")] [string] $ContainerPath, [Parameter(HelpMessage = "If set to a full (UNC) path, the script will output the log file to this directory.")] $LogPath = $False, [Parameter(HelpMessage = "Name that appears in the name of the log file.")] [string] $LogName = "FSLogixCleanUp", [Parameter(HelpMessage = "If set, enables dry-run mode.")] [switch] $WhatIf, [Parameter(HelpMessage = "Array of strings with folder names to exclude in recursion.")] [string[]] $ExcludeFolders, [Parameter(HelpMessage = "Number of days a user must have not logged into Active Directory to be considered inactive.")] [int] $InactiveDays = 90, [Parameter(HelpMessage = "If set, containers belonging to disabled users will be deleted.")] [switch] $DeleteDisabled, [Parameter(HelpMessage = "If set, containers belonging to removed/non-existing users will be deleted.")] [switch] $DeleteRemoved, [Parameter(HelpMessage = "If set, containers belonging to inactive users will be deleted.")] [switch] $DeleteInactive, [Parameter(HelpMessage = "If set, don't ask for confirmation before execution.")] [switch] $Confirm, [Parameter(HelpMessage = "if set, don't use the FlipFlop name convention (%username%_%sid%) but use the default (%sid%_%username%)")] [switch] $NoFlipFlop)function Write-Log { param ( [Parameter(Mandatory)] [string] $LogMessage, [Parameter(Mandatory)] [string] $LogLevel, [Parameter(Mandatory)] $LogPath, [Parameter(Mandatory)] [string] $LogName, [string] $LogHeader, [switch] $NoDate ) if ($NoDate) { $Message = "[$LogLevel] $LogMessage" } else { $Message = "$(Get-Date -Format "u") [$LogLevel] $LogMessage" } if ($LogPath) { $File = (Join-Path -Path $LogPath -ChildPath "$LogName.log") if (-not (Test-Path $File) -and $LogHeader) { Add-Content -Path $File -Value "" } Add-content -Path $File -Value $Message } Write-Output $Message}[decimal]$SpaceDisabled = 0[decimal]$SpaceRemoved = 0[decimal]$SpaceInactive = 0[int]$ContainerCount = 0$LogName = "$LogName`_$(Get-Date -Format "yyyyMMdd_HHmmss")"Write-Log -LogPath $LogPath -LogName $LogName -LogMessage "========================================" -LogLevel "Info"Write-Log -LogPath $LogPath -LogName $LogName -LogMessage "Starting execution" -LogLevel "Info"Write-Log -LogPath $LogPath -LogName $LogName -LogMessage "Container Path: $ContainerPath" -LogLevel "Info"Write-Log -LogPath $LogPath -LogName $LogName -LogMessage "Excluded folders: $ExcludeFolders" -LogLevel "Info"Write-Log -LogPath $LogPath -LogName $LogName -LogMessage "Deletion options" -LogLevel "Info"Write-Log -LogPath $LogPath -LogName $LogName -LogMessage "Removed Users: $DeleteRemoved" -LogLevel "Info"Write-Log -LogPath $LogPath -LogName $LogName -LogMessage "Disabled Users: $DeleteDisabled" -LogLevel "Info"Write-Log -LogPath $LogPath -LogName $LogName -LogMessage "Inactive Users: $DeleteInactive ($InactiveDays days)" -LogLevel "Info"Write-Log -LogPath $LogPath -LogName $LogName -LogMessage "========================================" -LogLevel "Info"if (-not (Test-Path -Path $ContainerPath)) { Write-Log -LogPath $LogPath -LogName $LogName -LogMessage "Container Path not accessible or does not exist." -LogLevel "Error" Exit}if ($True -eq $WhatIf) { Write-Log -LogPath $LogPath -LogName $LogName -LogMessage "Executing with WhatIf switch set. No changes will be made." -LogLevel "Info"}elseif ($False -eq $Confirm) { Write-Log -LogPath $LogPath -LogName $LogName -LogMessage "Executing without WhatIf switch set. Specified containers will be deleted." -LogLevel "Info" $ConfirmTitle = 'Confirm execution' $ConfirmQuestion = 'Do you want to continue?' $ConfirmChoices = '&Yes', '&No' $ConfirmDecision = $Host.UI.PromptForChoice($ConfirmTitle, $ConfirmQuestion, $ConfirmChoices, 1) if ($ConfirmDecision -eq 1) { Write-Log -LogPath $LogPath -LogName $LogName -LogMessage "Execution stopped by user." -LogLevel "Info" Exit }}$ContainerDirs = Get-ChildItem -Path $ContainerPath -Directory -Exclude $ExcludeFoldersforeach ($ContainerDir in $ContainerDirs) { $ContainerCount++ if ($True -eq $NoFlipFlop) { $UserName = $ContainerDir.Name.Substring($ContainerDir.Name.IndexOf('_') + 1) } else { $UserName = $ContainerDir.Name.Substring(0, $ContainerDir.Name.IndexOf('_S-1-5')) } $ContainerDir = Join-Path $ContainerPath $ContainerDir try { $ADUser = Get-ADUser -Identity $UserName -Properties sAMAccountName, Enabled, lastLogon, lastLogonDate } catch [Microsoft.ActiveDirectory.Management.ADIdentityNotFoundException] { $ADUser = $False } $ContainerSize = (Get-ChildItem -Path $ContainerDir | Measure-Object Length -Sum).Sum / 1Gb Write-Log -LogPath $LogPath -LogName $LogName -LogMessage ("Processing $UserName ({0:N2} GB)." -f $ContainerSize) -LogLevel "Info" if ($False -eq $ADUser -and $True -eq $DeleteRemoved) { Write-Log -LogPath $LogPath -LogName $LogName -LogMessage "Account for $UserName does not exist." -LogLevel "Info" if ($False -eq $WhatIf) { Write-Log -LogPath $LogPath -LogName $LogName -LogMessage "Deleting container for $UserName based on removed/non-existent state of account." -LogLevel "Info" try { Remove-Item -Path $ContainerDir -Recurse -Force -ErrorAction Stop } catch { Write-Log -LogPath $LogPath -LogName $LogName -LogMessage "Could not delete container for $UserName`: $($_)" -LogLevel "Warning" Continue } $SpaceRemoved = $SpaceRemoved + $ContainerSize } else { Write-Log -LogPath $LogPath -LogName $LogName -LogMessage "WhatIf: Deleting container for $UserName based on removed/non-existent state of account." -LogLevel "Info" $SpaceRemoved = $SpaceRemoved + $ContainerSize } Write-Log -LogPath $LogPath -LogName $LogName -LogMessage "Container deleted for $UserName." -LogLevel "Success" Continue } if ($False -eq $ADUser.Enabled -and $True -eq $DeleteDisabled) { Write-Log -LogPath $LogPath -LogName $LogName -LogMessage "Account for $UserName is disabled." -LogLevel "Info" if ($False -eq $WhatIf) { Write-Log -LogPath $LogPath -LogName $LogName -LogMessage "Deleting container for $UserName based on disabled state of account." -LogLevel "Info" try { Remove-Item -Path $ContainerDir -Recurse -Force -ErrorAction Stop } catch { Write-Log -LogPath $LogPath -LogName $LogName -LogMessage "Could not delete container for $UserName`: $($_)" -LogLevel "Warning" Continue } $SpaceDisabled = $SpaceDisabled + $ContainerSize } else { Write-Log -LogPath $LogPath -LogName $LogName -LogMessage "WhatIf: Deleting container for $UserName based on disabled state of account." -LogLevel "Info" $SpaceDisabled = $SpaceDisabled + $ContainerSize } Write-Log -LogPath $LogPath -LogName $LogName -LogMessage "Container deleted for $UserName." -LogLevel "Success" Continue } if ($ADUser.lastLogonDate -lt ((Get-Date).AddDays( - ($InactiveDays))) -and $True -eq $DeleteInactive) { Write-Log -LogPath $LogPath -LogName $LogName -LogMessage "Account for $UserName has been inactive for more than $InactiveDays days." -LogLevel "Info" if ($False -eq $WhatIf) { Write-Log -LogPath $LogPath -LogName $LogName -LogMessage "Deleting container for $UserName based on inactive state of account." -LogLevel "Info" try { Remove-Item -Path $ContainerDir -Recurse -Force -ErrorAction Stop } catch { Write-Log -LogPath $LogPath -LogName $LogName -LogMessage "Could not delete container for $UserName`: $($_)" -LogLevel "Warning" Continue } $SpaceInactive = $SpaceInactive + $ContainerSize } else { Write-Log -LogPath $LogPath -LogName $LogName -LogMessage "WhatIf: Deleting container for $UserName based on inactive state of account." -LogLevel "Info" $SpaceInactive = $SpaceInactive + $ContainerSize } Write-Log -LogPath $LogPath -LogName $LogName -LogMessage "Container deleted for $UserName." -LogLevel "Success" Continue } Write-Log -LogPath $LogPath -LogName $LogName -LogMessage "No action needed for $UserName." -LogLevel "Success"}Write-Log -LogPath $LogPath -LogName $LogName -LogMessage "Script execution completed" -LogLevel "Success"Write-Log -LogPath $LogPath -LogName $LogName -LogMessage "$ContainerCount containers processed." -LogLevel "Info"Write-Log -LogPath $LogPath -LogName $LogName -LogMessage "$("{0:N2} GB" -f $SpaceRemoved) reclaimed from removed/non-existent users." -LogLevel "Info"Write-Log -LogPath $LogPath -LogName $LogName -LogMessage "$("{0:N2} GB" -f $SpaceDisabled) reclaimed from disabled users." -LogLevel "Info"Write-Log -LogPath $LogPath -LogName $LogName -LogMessage "$("{0:N2} GB" -f $SpaceInactive) reclaimed from inactive users." -LogLevel "Info"Write-Log -LogPath $LogPath -LogName $LogName -LogMessage "$("{0:N2} GB" -f ($SpaceRemoved+$SpaceDisabled+$SpaceInactive)) reclaimed in total." -LogLevel "Info"

Of course, this script comes with ABSOLUTELY NO WARRANTY and I expect you to read before you execute. If you have any brilliant additions, please let me know in the comments or create a pull request. I would be happy to incorporate new functionality.

Automatically clean up FSLogix Profile Containers – The Cloud Admin (2024)
Top Articles
2024 Cost of Living Calculator - Compare Cost of Living by City & State
US marks 20th anniversary of 9/11: Live updates | CNN
Week 2 Defense (DEF) Streamers, Starters & Rankings: 2024 Fantasy Tiers, Rankings
Citibank Branch Locations In Orlando Florida
Mrh Forum
Frank Lloyd Wright, born 150 years ago, still fascinates
Chase Bank Operating Hours
Khatrimaza Movies
Pbr Wisconsin Baseball
Missing 2023 Showtimes Near Lucas Cinemas Albertville
Globe Position Fault Litter Robot
Raid Guides - Hardstuck
Washington Poe en Tilly Bradshaw 1 - Brandoffer, M.W. Craven | 9789024594917 | Boeken | bol
978-0137606801
Abortion Bans Have Delayed Emergency Medical Care. In Georgia, Experts Say This Mother’s Death Was Preventable.
Lowe's Garden Fence Roll
U Break It Near Me
Gentle Dental Northpointe
Drift Boss 911
Contracts for May 28, 2020
Mtr-18W120S150-Ul
Academy Sports Meridian Ms
Munis Self Service Brockton
Panola County Busted Newspaper
6892697335
Hesburgh Library Catalog
TMO GRC Fortworth TX | T-Mobile Community
Fuse Box Diagram Honda Accord (2013-2017)
UAE 2023 F&B Data Insights: Restaurant Population and Traffic Data
Uncovering the Enigmatic Trish Stratus: From Net Worth to Personal Life
Craigslist Auburn Al
Toonkor211
Neteller Kasiinod
LG UN90 65" 4K Smart UHD TV - 65UN9000AUJ | LG CA
The Monitor Recent Obituaries: All Of The Monitor's Recent Obituaries
United E Gift Card
APUSH Unit 6 Practice DBQ Prompt Answers & Feedback | AP US History Class Notes | Fiveable
Helloid Worthington Login
Newcardapply Com 21961
Reli Stocktwits
Edward Walk In Clinic Plainfield Il
Cranston Sewer Tax
Puretalkusa.com/Amac
Mugshots Journal Star
Gotrax Scooter Error Code E2
Content Page
Jaefeetz
A jovem que batizou lei após ser sequestrada por 'amigo virtual'
Germany’s intensely private and immensely wealthy Reimann family
Invitation Quinceanera Espanol
Salem witch trials - Hysteria, Accusations, Executions
Latest Posts
Article information

Author: Fredrick Kertzmann

Last Updated:

Views: 5870

Rating: 4.6 / 5 (66 voted)

Reviews: 89% of readers found this page helpful

Author information

Name: Fredrick Kertzmann

Birthday: 2000-04-29

Address: Apt. 203 613 Huels Gateway, Ralphtown, LA 40204

Phone: +2135150832870

Job: Regional Design Producer

Hobby: Nordic skating, Lacemaking, Mountain biking, Rowing, Gardening, Water sports, role-playing games

Introduction: My name is Fredrick Kertzmann, I am a gleaming, encouraging, inexpensive, thankful, tender, quaint, precious person who loves writing and wants to share my knowledge and understanding with you.