WinProfileOps.psm1
#Region './Classes/ProfileDeletionResult.ps1' -1 class ProfileDeletionResult { [string]$SID [string]$ProfilePath [bool]$DeletionSuccess [string]$DeletionMessage [string]$ComputerName # Constructor to initialize the properties ProfileDeletionResult([string]$sid, [string]$profilePath, [bool]$deletionSuccess, [string]$deletionMessage, [string]$computerName) { $this.SID = $sid $this.ProfilePath = $profilePath $this.DeletionSuccess = $deletionSuccess $this.DeletionMessage = $deletionMessage $this.ComputerName = $computerName } } #EndRegion './Classes/ProfileDeletionResult.ps1' 17 #Region './Classes/UserProfile.ps1' -1 class UserProfile { [string]$SID [string]$ProfilePath [bool]$IsOrphaned [string]$OrphanReason = $null [string]$ComputerName [bool]$IsSpecial # Constructor to initialize the properties UserProfile([string]$sid, [string]$profilePath, [bool]$isOrphaned, [string]$orphanReason, [string]$computerName, [bool]$isSpecial) { $this.SID = $sid $this.ProfilePath = $profilePath $this.IsOrphaned = $isOrphaned $this.OrphanReason = $orphanReason $this.ComputerName = $computerName $this.IsSpecial = $isSpecial } } #EndRegion './Classes/UserProfile.ps1' 21 #Region './Private/Get-ProfilePathFromSID.ps1' -1 <# .SYNOPSIS Retrieves the profile path associated with a specific SID from the registry. .DESCRIPTION The Get-ProfilePathFromSID function retrieves the "ProfileImagePath" registry value for the provided SID registry key. This path indicates the location of the user profile associated with the SID. .PARAMETER SidKey The registry key representing the Security Identifier (SID) from which to retrieve the profile path. .EXAMPLE Get-ProfilePathFromSID -SidKey $sidKey Retrieves the profile path for the given SID from the registry. .NOTES If the "ProfileImagePath" cannot be found, the function will return `$null` and a verbose message will indicate the issue. In case of an error during retrieval, an error message is logged and the function returns `$null`. #> function Get-ProfilePathFromSID { param ( [Microsoft.Win32.RegistryKey]$SidKey ) try { # Use Get-RegistryValue to retrieve the "ProfileImagePath" $profileImagePath = Get-RegistryValue -Key $SidKey -ValueName "ProfileImagePath" if (-not $profileImagePath) { Write-Verbose "ProfileImagePath not found for SID '$($SidKey.Name)'." } return $profileImagePath } catch { Write-Error "Failed to retrieve ProfileImagePath for SID '$($SidKey.Name)'. Error: $_" return $null } } #EndRegion './Private/Get-ProfilePathFromSID.ps1' 39 #Region './Private/Get-RegistryKeyForSID.ps1' -1 <# .SYNOPSIS Retrieves the registry key associated with a specified SID from the ProfileList. .DESCRIPTION The Get-RegistryKeyForSID function attempts to open and retrieve the registry subkey for a given Security Identifier (SID) from the ProfileList. If the SID does not exist or an error occurs while accessing the registry, the function returns `$null` and logs a warning or error message. .PARAMETER SID The Security Identifier (SID) for which to retrieve the registry subkey. .PARAMETER ProfileListKey The opened registry key representing the ProfileList, which contains the subkeys for user profiles. .EXAMPLE Get-RegistryKeyForSID -SID "S-1-5-21-123456789-1001" -ProfileListKey $profileListKey Retrieves the registry subkey associated with the specified SID from the ProfileList. .NOTES If the registry key for the SID cannot be found or accessed, the function returns `$null` and logs an appropriate warning or error message. The function relies on the Open-RegistrySubKey function to retrieve the subkey. #> function Get-RegistryKeyForSID { param ( [string]$SID, [Microsoft.Win32.RegistryKey]$ProfileListKey ) try { # Use the general Open-RegistrySubKey function to get the subkey for the SID $sidKey = Open-RegistrySubKey -ParentKey $ProfileListKey -SubKeyName $SID if ($sidKey -eq $null) { Write-Warning "The SID '$SID' does not exist in the ProfileList registry." return $null } return $sidKey } catch { Write-Error "Error accessing registry key for SID '$SID'. Error: $_" return $null } } #EndRegion './Private/Get-RegistryKeyForSID.ps1' 41 #Region './Private/New-UserProfileObject.ps1' -1 <# .SYNOPSIS Creates a new UserProfile object. .DESCRIPTION The New-UserProfileObject function creates and returns an instance of the UserProfile class. The function takes in various parameters such as SID, profile path, and whether the profile is orphaned or special, and returns a UserProfile object with these details. .PARAMETER SID The Security Identifier (SID) of the user profile. .PARAMETER ProfilePath The file path to the user profile folder. .PARAMETER IsOrphaned A boolean value indicating whether the profile is orphaned (i.e., exists in the registry but not on disk, or vice versa). .PARAMETER OrphanReason A description of why the profile is considered orphaned, if applicable. .PARAMETER ComputerName The name of the computer where the profile is located. .PARAMETER IsSpecial A boolean value indicating whether the profile is for a special account (e.g., system or default accounts). .EXAMPLE New-UserProfileObject -SID "S-1-5-21-123456789-1001" -ProfilePath "C:\Users\John" -IsOrphaned $true -OrphanReason "MissingRegistryEntry" -ComputerName "Server01" -IsSpecial $false Creates a new UserProfile object for the profile associated with the given SID, marking it as orphaned with a reason. .NOTES This function returns an instance of the UserProfile class, which is used for managing and reporting on user profiles across different systems. #> function New-UserProfileObject { [outputType([UserProfile])] param ( [string]$SID, [string]$ProfilePath, [bool]$IsOrphaned, [string]$OrphanReason = $null, [string]$ComputerName, [bool]$IsSpecial ) return [UserProfile]::new( $SID, $ProfilePath, $IsOrphaned, $OrphanReason, $ComputerName, $IsSpecial ) } #EndRegion './Private/New-UserProfileObject.ps1' 46 #Region './Private/Remove-RegistryKeyForSID.ps1' -1 <# .SYNOPSIS Deletes a registry key associated with a specific SID from the ProfileList. .DESCRIPTION The Remove-RegistryKeyForSID function deletes the registry key corresponding to a specific Security Identifier (SID) from the ProfileList in the Windows registry. It supports confirmation prompts and simulates actions with the -WhatIf parameter. .PARAMETER SID The Security Identifier (SID) for which the registry key should be deleted. .PARAMETER ProfileListKey The opened registry key representing the ProfileList where the profile's SID is located. .PARAMETER ComputerName The name of the computer where the profile registry key resides. By default, this is the current computer. .EXAMPLE Remove-RegistryKeyForSID -SID "S-1-5-21-123456789-1001" -ProfileListKey $profileListKey -ComputerName "Server01" Deletes the registry key for the specified SID from the ProfileList on "Server01". .NOTES This function supports 'ShouldProcess', so it can be used in conjunction with the -WhatIf or -Confirm parameters to simulate the deletion. It also includes error handling to ensure any failure during the registry key deletion is captured. #> function Remove-RegistryKeyForSID { # Deletes a single registry key for a SID. [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'High')] param ( [Parameter(Mandatory = $true)] [string]$SID, [Parameter(Mandatory = $true)] [Microsoft.Win32.RegistryKey]$ProfileListKey, [Parameter(Mandatory = $true)] [string]$ComputerName = $env:COMPUTERNAME ) try { # Check if ShouldProcess is approved (with -WhatIf and -Confirm support) if ($PSCmdlet.ShouldProcess("SID: $SID on $ComputerName", "Remove registry key")) { # Use the general Remove-RegistrySubKey function to delete the SID's subkey return Remove-RegistrySubKey -ParentKey $ProfileListKey -SubKeyName $SID -ComputerName $ComputerName -Confirm:$false } else { Write-Verbose "Removal of registry key for SID '$SID' was skipped." return $false } } catch { Write-Error "Failed to remove the profile registry key for SID '$SID' on $ComputerName. Error: $_" return $false } } #EndRegion './Private/Remove-RegistryKeyForSID.ps1' 55 #Region './Private/Remove-SIDProfile.ps1' -1 <# .SYNOPSIS Coordinates the deletion of a profile registry key for a given SID. .DESCRIPTION The Remove-SIDProfile function removes the registry key associated with a specific Security Identifier (SID) from the ProfileList on the specified computer. It supports confirmation prompts and -WhatIf scenarios by using the ShouldProcess pattern. The function also handles errors that occur during the deletion process and returns a ProfileDeletionResult object indicating success or failure. .PARAMETER SID The Security Identifier (SID) of the profile to be deleted. .PARAMETER ProfileListKey The registry key representing the ProfileList from which the SID's registry key will be removed. .PARAMETER ComputerName The name of the computer where the profile registry key resides. Defaults to the current computer. .PARAMETER ProfilePath The file path of the profile to be deleted, used for logging purposes in the ProfileDeletionResult object. .OUTPUTS [ProfileDeletionResult] An object that indicates whether the profile registry key was successfully deleted or if the action was skipped or failed. Includes the SID, ProfilePath, DeletionSuccess status, DeletionMessage, and ComputerName. .EXAMPLE Remove-SIDProfile -SID "S-1-5-21-123456789-1001" -ProfileListKey $profileListKey -ComputerName "Server01" -ProfilePath "C:\Users\John" Removes the registry key for the specified SID from the ProfileList on "Server01" and deletes the profile. .EXAMPLE Remove-SIDProfile -SID "S-1-5-21-123456789-1001" -ProfileListKey $profileListKey -ProfilePath "C:\Users\John" -WhatIf Simulates the removal of the profile registry key for the specified SID using the -WhatIf parameter, showing what would have been done without performing the action. .NOTES - The function supports 'ShouldProcess', allowing the use of -WhatIf and -Confirm parameters for safety. - In case of an error, the function returns a ProfileDeletionResult object with DeletionSuccess set to $false and logs the error message. - If the action is skipped (e.g., due to -WhatIf or confirmation denial), the function returns a ProfileDeletionResult with a status indicating that the action was skipped. #> function Remove-SIDProfile { [outputtype([ProfileDeletionResult])] # Coordinates the registry key deletion and provides a result for a single SID. [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'High')] param ( [string]$SID, [Microsoft.Win32.RegistryKey]$ProfileListKey, [string]$ComputerName, [string]$ProfilePath ) try { # Use ShouldProcess to check if the action should proceed (with -WhatIf and -Confirm support) if ($PSCmdlet.ShouldProcess("SID: $SID on $ComputerName", "Remove profile registry key")) { # Attempt to remove the registry key $deletionSuccess = Remove-RegistryKeyForSID -SID $SID -ProfileListKey $ProfileListKey -ComputerName $ComputerName if ($deletionSuccess) { return [ProfileDeletionResult]::new( $SID, $ProfilePath, $true, "Profile registry key for SID '$SID' successfully deleted.", $ComputerName ) } else { return [ProfileDeletionResult]::new( $SID, $ProfilePath, $false, "Failed to delete the profile registry key for SID '$SID'.", $ComputerName ) } } else { Write-Verbose "Removal of profile registry key for SID '$SID' on '$ComputerName' was skipped." return [ProfileDeletionResult]::new( $SID, $ProfilePath, $false, "Action skipped.", $ComputerName ) } } catch { Write-Error "Failed to remove the profile registry key for SID '$SID' on $ComputerName. Error: $_" return [ProfileDeletionResult]::new( $SID, $ProfilePath, $false, "Failed to delete the profile registry key for SID '$SID'. Error: $_", $ComputerName ) } } #EndRegion './Private/Remove-SIDProfile.ps1' 103 #Region './Private/Test-FolderExists.ps1' -1 <# .SYNOPSIS Checks if a profile folder exists on a specified computer. .DESCRIPTION The Test-FolderExists function determines whether a given profile folder exists on the specified computer by testing the path. If the profile path or computer name is not provided, the function will default to using the local computer. In the event of any errors (e.g., invalid paths or inaccessible directories), the function returns $false and logs the error. .PARAMETER ProfilePath The file path of the profile folder to check. This parameter is required. If it is null or empty, the function will return $false. .PARAMETER ComputerName The name of the computer where the profile folder is located. If not provided, the local computer is used by default. .OUTPUTS [bool] Returns $true if the folder exists at the specified path, and $false if it does not exist, or if an error occurs during execution. .EXAMPLE Test-FolderExists -ProfilePath "C:\Users\John" -ComputerName "Server01" Checks if the folder "C:\Users\John" exists on "Server01". .EXAMPLE Test-FolderExists -ProfilePath "C:\Users\Public" Checks if the folder "C:\Users\Public" exists on the local computer (since ComputerName is not specified). .EXAMPLE Test-FolderExists -ProfilePath "C:\InvalidPath" -ComputerName "Server01" Returns $false if the specified folder does not exist or if an error occurs while accessing the path. .NOTES The function includes error handling to catch and log any exceptions. In case of an error, the function returns $false. #> function Test-FolderExists { [outputType([bool])] [cmdletbinding()] param ( [string]$ProfilePath, [string]$ComputerName = $env:COMPUTERNAME ) # Check for null or empty ProfilePath if (-not $ProfilePath) { Write-Warning "ProfilePath is null or empty." return $false } # Check for null or empty ComputerName and default to the local computer if it's null if (-not $ComputerName) { Write-Warning "ComputerName is null or empty. Defaulting to the local computer." $ComputerName = $env:COMPUTERNAME } try { # Determine if the computer is local or remote $IsLocal = $ComputerName -eq $env:COMPUTERNAME # Get the directory path to check $pathToCheck = Get-DirectoryPath -BasePath $ProfilePath -ComputerName $ComputerName -IsLocal $IsLocal # Return whether the path exists return Test-Path $pathToCheck } catch { Write-Error "An error occurred: $_" return $false } } #EndRegion './Private/Test-FolderExists.ps1' 73 #Region './Private/Test-OrphanedProfile.ps1' -1 <# .SYNOPSIS Tests whether a profile is orphaned. .DESCRIPTION The Test-OrphanedProfile function checks if a profile is orphaned by evaluating the profile path, folder existence, and whether it's a special account. .PARAMETER SID The Security Identifier (SID) of the profile being tested. .PARAMETER ProfilePath The file path of the profile folder. .PARAMETER FolderExists Indicates whether the profile folder exists on the computer. .PARAMETER IgnoreSpecial Switch to ignore special or default profiles when determining if the profile is orphaned. .PARAMETER IsSpecial Indicates whether the profile is a special account. .PARAMETER ComputerName The name of the computer where the profile is being tested. .EXAMPLE Test-OrphanedProfile -SID "S-1-5-21-123456789-1001" -ProfilePath "C:\Users\John" -FolderExists $true -IgnoreSpecial -IsSpecial $false -ComputerName "Server01" Tests if the profile associated with the given SID is orphaned on "Server01". #> function Test-OrphanedProfile { param ( [string]$SID, [string]$ProfilePath, [bool]$FolderExists, [bool]$IgnoreSpecial, [bool]$IsSpecial, [string]$ComputerName ) if (-not $ProfilePath) { return New-UserProfileObject $SID $null $true "MissingProfileImagePath" $ComputerName $IsSpecial } elseif (-not $FolderExists) { return New-UserProfileObject $SID $ProfilePath $true "MissingFolder" $ComputerName $IsSpecial } else { return New-UserProfileObject $SID $ProfilePath $false $null $ComputerName $IsSpecial } } #EndRegion './Private/Test-OrphanedProfile.ps1' 47 #Region './Private/Test-SpecialAccount.ps1' -1 <# .SYNOPSIS Tests if a profile is considered a special or default account. .DESCRIPTION The Test-SpecialAccount function checks whether the profile is a special or default account by comparing the folder name, Security Identifier (SID), and profile path to predefined lists of ignored accounts, SIDs, and paths. If the profile matches any of the predefined entries, it is considered a special account. .PARAMETER FolderName The name of the folder representing the profile being tested. .PARAMETER SID The Security Identifier (SID) of the profile being tested. .PARAMETER ProfilePath The file path of the profile being tested. .EXAMPLE Test-SpecialAccount -FolderName "DefaultAppPool" -SID "S-1-5-18" -ProfilePath "C:\WINDOWS\system32\config\systemprofile" Checks if the profile associated with the folder "DefaultAppPool", SID "S-1-5-18", and profile path "C:\WINDOWS\system32\config\systemprofile" is a special account. .EXAMPLE Test-SpecialAccount -FolderName "JohnDoe" -SID "S-1-5-21-123456789-1001" -ProfilePath "C:\Users\JohnDoe" Tests a non-special account, which does not match any predefined special accounts. .NOTES This function returns $true if the account is considered special, and $false otherwise. #> function Test-SpecialAccount { param ( [string]$FolderName, [string]$SID, [string]$ProfilePath ) # List of default or special accounts to ignore $IgnoredAccounts = @( "defaultuser0", "DefaultAppPool", "servcm12", "Public", "PBIEgwService", "Default", "All Users", "win2kpro" ) $IgnoredSIDs = @( "S-1-5-18", # Local System "S-1-5-19", # Local Service "S-1-5-20" # Network Service ) $IgnoredPaths = @( "C:\WINDOWS\system32\config\systemprofile", # System profile "C:\WINDOWS\ServiceProfiles\LocalService", # Local service profile "C:\WINDOWS\ServiceProfiles\NetworkService" # Network service profile ) # Check if the account is special based on the folder name, SID, or profile path return ($IgnoredAccounts -contains $FolderName) -or ($IgnoredSIDs -contains $SID) -or ($IgnoredPaths -contains $ProfilePath) } #EndRegion './Private/Test-SpecialAccount.ps1' 49 #Region './Public/Get-AllUserProfiles.ps1' -1 <# .SYNOPSIS Retrieves all user profiles from both the registry and file system on a specified computer. .DESCRIPTION The Get-AllUserProfiles function collects user profile information from both the file system (profile folders) and the registry on the specified computer. It compares the two sets of profiles, identifying orphaned profiles that exist in one location but not the other. The function also allows the option to ignore special or default profiles, such as system or service accounts. .PARAMETER ComputerName The name of the computer from which to retrieve user profiles. Defaults to the local computer. .PARAMETER ProfileFolderPath The folder path where user profiles are stored. Defaults to "$env:SystemDrive\Users". .PARAMETER IgnoreSpecial Switch to ignore special or default profiles during the profile retrieval process. .EXAMPLE Get-AllUserProfiles -ComputerName "Server01" Retrieves all user profiles from both the file system and registry on "Server01". .EXAMPLE Get-AllUserProfiles -ProfileFolderPath "D:\UserProfiles" -IgnoreSpecial Retrieves user profiles from the specified folder and ignores special or default profiles. .NOTES This function compares user profiles found in the file system and the registry to identify orphaned profiles. It supports pipeline input for multiple computer names, allowing you to retrieve profiles from multiple systems. Special or default profiles, such as system accounts, can be ignored by using the -IgnoreSpecial parameter. #> function Get-AllUserProfiles { [CmdletBinding()] param ( [Parameter(Mandatory = $false, ValueFromPipeline = $true)] [string]$ComputerName = $env:COMPUTERNAME, [string]$ProfileFolderPath = "$env:SystemDrive\Users", [switch]$IgnoreSpecial ) # Begin block runs once before processing pipeline input begin { # Initialize an array to hold all UserProfile objects across multiple pipeline inputs $AllProfiles = @() } # Process block runs once for each input object (in case of pipeline) process { # Test if the computer is online before proceeding if (-not (Test-ComputerPing -ComputerName $ComputerName)) { Write-Warning "Computer '$ComputerName' is offline or unreachable." return # Skip to the next input in the pipeline } # Get profiles from folders and registry $UserFolders = Get-UserProfilesFromFolders -ComputerName $ComputerName -ProfileFolderPath $ProfileFolderPath $RegistryProfiles = Get-UserProfilesFromRegistry -ComputerName $ComputerName # Loop through registry profiles and check for folder existence and ProfileImagePath foreach ($regProfile in $RegistryProfiles) { $profilePath = $regProfile.ProfilePath $folderExists = Test-FolderExists -ProfilePath $profilePath -ComputerName $regProfile.ComputerName $folderName = Split-Path -Path $profilePath -Leaf $isSpecial = Test-SpecialAccount -FolderName $folderName -SID $regProfile.SID -ProfilePath $profilePath # Skip special profiles if IgnoreSpecial is set if ($IgnoreSpecial -and $isSpecial) { continue } # Detect if the profile is orphaned and create the user profile object $userProfile = Test-OrphanedProfile -SID $regProfile.SID -ProfilePath $profilePath ` -FolderExists $folderExists -IgnoreSpecial $IgnoreSpecial ` -IsSpecial $isSpecial -ComputerName $ComputerName $AllProfiles += $userProfile } # Loop through user folders and check if they exist in the registry foreach ($folder in $UserFolders) { $registryProfile = $RegistryProfiles | Where-Object { $_.ProfilePath -eq $folder.ProfilePath } $isSpecial = Test-SpecialAccount -FolderName $folder.FolderName -SID $null -ProfilePath $folder.ProfilePath # Skip special profiles if IgnoreSpecial is set if ($IgnoreSpecial -and $isSpecial) { continue } # Case 4: Folder exists in C:\Users but not in the registry if (-not $registryProfile) { $AllProfiles += New-UserProfileObject $null $folder.ProfilePath $true "MissingRegistryEntry" $ComputerName $isSpecial } } } # End block runs once after all processing is complete end { # Output all collected profiles $AllProfiles } } #EndRegion './Public/Get-AllUserProfiles.ps1' 103 #Region './Public/Get-OrphanedProfiles.ps1' -1 <# .SYNOPSIS Retrieves orphaned user profiles from a specified computer. .DESCRIPTION The Get-OrphanedProfiles function scans the user profiles on a specified computer and identifies profiles that are orphaned. Orphaned profiles are those that exist either in the filesystem but not in the registry, or vice versa. The function returns a list of only the orphaned profiles. .PARAMETER ComputerName The name of the computer from which to retrieve orphaned profiles. Defaults to the local computer. .PARAMETER ProfileFolderPath The path to the folder where user profiles are stored. Defaults to "$env:SystemDrive\Users". .PARAMETER IgnoreSpecial Switch to ignore special or default profiles (such as system or service accounts) during the orphan detection process. .EXAMPLE Get-OrphanedProfiles -ComputerName "Server01" Retrieves orphaned user profiles from "Server01". .EXAMPLE Get-OrphanedProfiles -ProfileFolderPath "D:\UserProfiles" -IgnoreSpecial Retrieves orphaned profiles from the specified folder while ignoring special or default profiles. .NOTES This function relies on the Get-AllUserProfiles function to retrieve profiles from both the filesystem and the registry. Orphaned profiles are returned as a filtered list where only profiles marked as orphaned are included. #> function Get-OrphanedProfiles { [CmdletBinding()] param ( [Parameter(Mandatory = $false)] [string]$ComputerName = $env:COMPUTERNAME, [Parameter(Mandatory = $false)] [string]$ProfileFolderPath = "$env:SystemDrive\Users", [switch]$IgnoreSpecial ) # Get all user profiles (both registry and filesystem) using the existing function $allProfiles = Get-AllUserProfiles -ComputerName $ComputerName -ProfileFolderPath $ProfileFolderPath -IgnoreSpecial # Filter the profiles to return only orphaned ones $orphanedProfiles = $allProfiles | Where-Object { $_.IsOrphaned -eq $true } # Return the orphaned profiles return $orphanedProfiles } #EndRegion './Public/Get-OrphanedProfiles.ps1' 45 #Region './Public/Get-SIDProfileInfo.ps1' -1 <# .SYNOPSIS Retrieves profile information from the registry for all SIDs on a specified computer. .DESCRIPTION The Get-SIDProfileInfo function queries the ProfileList registry key on the specified computer and retrieves profile information for each Security Identifier (SID). The function returns a list of profiles, including details such as the SID, profile path, and whether the profile exists in the registry. .PARAMETER ComputerName The name of the computer from which to retrieve profile information. Defaults to the local computer. .EXAMPLE Get-SIDProfileInfo -ComputerName "Server01" Retrieves profile information for all SIDs stored in the registry on "Server01". .EXAMPLE Get-SIDProfileInfo Retrieves profile information for all SIDs stored in the registry on the local computer. .NOTES This function returns a list of objects where each object contains the SID, profile path, and whether the profile exists in the registry. If a registry subkey for an SID cannot be opened, a warning is written to the output. #> function Get-SIDProfileInfo { [CmdletBinding()] param ( [string]$ComputerName = $env:COMPUTERNAME ) $RegistryPath = "SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList" $ProfileListKey = Open-RegistryKey -RegistryPath $RegistryPath -ComputerName $ComputerName if ($ProfileListKey -eq $null) { Write-Error "Failed to open registry path: $RegistryPath on $ComputerName." return } $ProfileRegistryItems = foreach ($sid in $ProfileListKey.GetSubKeyNames()) { # Use Open-RegistrySubKey to get the subkey for the SID $subKey = Open-RegistrySubKey -ParentKey $ProfileListKey -SubKeyName $sid if ($subKey -eq $null) { Write-Warning "Registry key for SID '$sid' could not be opened." continue } # Use Get-ProfilePathFromSID to get the ProfileImagePath for the SID $profilePath = Get-ProfilePathFromSID -SidKey $subKey # Return a PSCustomObject with SID, ProfilePath, and ComputerName [PSCustomObject]@{ SID = $sid ProfilePath = $profilePath ComputerName = $ComputerName ExistsInRegistry = $true } } return $ProfileRegistryItems } #EndRegion './Public/Get-SIDProfileInfo.ps1' 59 #Region './Public/Get-UserFolders.ps1' -1 <# .SYNOPSIS Retrieves a list of user profile folders from a specified computer. .DESCRIPTION The Get-UserFolders function scans the user profile directory on the specified computer and returns a list of folders that represent user profiles. It checks whether the target computer is local or remote and returns information such as the folder name and profile path for each folder. .PARAMETER ComputerName The name of the computer from which to retrieve user profile folders. .PARAMETER ProfileFolderPath The path to the folder where user profiles are stored. Defaults to "$env:SystemDrive\Users". .EXAMPLE Get-UserFolders -ComputerName "Server01" -ProfileFolderPath "D:\UserProfiles" Retrieves a list of user profile folders from the "D:\UserProfiles" directory on "Server01". .EXAMPLE Get-UserFolders -ComputerName $env:COMPUTERNAME Retrieves a list of user profile folders from the local computer's default user directory. .NOTES This function returns an array of objects where each object represents a user profile folder, including the folder name, profile path, and computer name. #> function Get-UserFolders { [CmdletBinding()] param ( [string]$ComputerName, [string]$ProfileFolderPath = "$env:SystemDrive\Users" ) $IsLocal = ($ComputerName -eq $env:COMPUTERNAME) $FolderPath = Get-DirectoryPath -BasePath $ProfileFolderPath -ComputerName $ComputerName -IsLocal $IsLocal # Get list of all folders in the user profile directory $ProfileFolders = Get-ChildItem -Path $FolderPath -Directory | ForEach-Object { [PSCustomObject]@{ FolderName = $_.Name ProfilePath = Get-DirectoryPath -basepath $_.FullName -ComputerName $ComputerName -IsLocal $true ComputerName = $ComputerName } } return $ProfileFolders } #EndRegion './Public/Get-UserFolders.ps1' 41 #Region './Public/Get-UserProfilesFromFolders.ps1' -1 <# .SYNOPSIS Retrieves user profile folders from a specified computer. .DESCRIPTION The Get-UserProfilesFromFolders function scans the user profile directory on the specified computer and returns information about the user profile folders found. This function is useful for identifying the profile folders stored on disk, which may or may not match entries in the registry. .PARAMETER ComputerName The name of the computer from which to retrieve user profile folders. Defaults to the local computer. .PARAMETER ProfileFolderPath The path to the folder where user profiles are stored. Defaults to "$env:SystemDrive\Users". .EXAMPLE Get-UserProfilesFromFolders -ComputerName "Server01" -ProfileFolderPath "D:\UserProfiles" Retrieves user profile folders from the "D:\UserProfiles" directory on "Server01". .EXAMPLE Get-UserProfilesFromFolders Retrieves user profile folders from the default "$env:SystemDrive\Users" directory on the local computer. .NOTES This function returns a list of user profile folders found in the specified directory on the specified computer. #> function Get-UserProfilesFromFolders { param ( [string]$ComputerName = $env:COMPUTERNAME, [string]$ProfileFolderPath = "$env:SystemDrive\Users" ) # Get user folders and return them $UserFolders = Get-UserFolders -ComputerName $ComputerName -ProfileFolderPath $ProfileFolderPath return $UserFolders } #EndRegion './Public/Get-UserProfilesFromFolders.ps1' 30 #Region './Public/Get-UserProfilesFromRegistry.ps1' -1 <# .SYNOPSIS Retrieves user profiles from the registry of a specified computer. .DESCRIPTION The Get-UserProfilesFromRegistry function queries the ProfileList registry key on the specified computer and returns information about the user profiles found in the registry. This includes details such as the SID and profile path. .PARAMETER ComputerName The name of the computer from which to retrieve user profiles. Defaults to the local computer. .EXAMPLE Get-UserProfilesFromRegistry -ComputerName "Server01" Retrieves the user profiles from the registry on "Server01". .EXAMPLE Get-UserProfilesFromRegistry Retrieves the user profiles from the local computer's registry. .NOTES This function returns a list of user profiles stored in the registry, including their SIDs and associated profile paths. #> function Get-UserProfilesFromRegistry { param ( [string] $ComputerName = $env:COMPUTERNAME ) # Get registry profiles and return them $RegistryProfiles = Get-SIDProfileInfo -ComputerName $ComputerName return $RegistryProfiles } #EndRegion './Public/Get-UserProfilesFromRegistry.ps1' 28 #Region './Public/Remove-OrphanedProfiles.ps1' -1 <# .SYNOPSIS Removes orphaned user profiles from a specified computer. .DESCRIPTION The Remove-OrphanedProfiles function identifies and removes orphaned profiles from the specified computer. Orphaned profiles are those that exist in the file system but not in the registry, or vice versa. The function can also optionally ignore special or default profiles. .PARAMETER ComputerName The name of the computer from which orphaned profiles will be removed. This is a required parameter. .PARAMETER ProfileFolderPath The path to the folder containing user profiles. Defaults to "$env:SystemDrive\Users". .PARAMETER IgnoreSpecial Switch to ignore special or default profiles during the removal process. .EXAMPLE Remove-OrphanedProfiles -ComputerName "Server01" -ProfileFolderPath "C:\Users" -IgnoreSpecial Removes orphaned profiles from "Server01", excluding special or default profiles. .EXAMPLE Remove-OrphanedProfiles -ComputerName "Server01" Removes orphaned profiles from "Server01" using the default profile folder path "$env:SystemDrive\Users". .NOTES This function supports 'ShouldProcess', allowing the use of -WhatIf or -Confirm to simulate the deletion process. The function first collects all orphaned profiles, identifies the SIDs associated with them, and then removes the corresponding registry entries. #> function Remove-OrphanedProfiles { [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'High')] param ( [Parameter(Mandatory = $true)] [string]$ComputerName, [Parameter(Mandatory = $false)] [string]$ProfileFolderPath = "$env:SystemDrive\Users", [switch]$IgnoreSpecial ) # Step 1: Get the list of orphaned profiles $orphanedProfiles = Get-OrphanedProfiles-ComputerName $ComputerName -ProfileFolderPath $ProfileFolderPath -IgnoreSpecial if (-not $orphanedProfiles) { Write-Verbose "No orphaned profiles found on $ComputerName." return } # Step 2: Extract the SIDs of orphaned profiles that exist in the registry $orphanedSIDs = $orphanedProfiles | Where-Object { $_.SID } | Select-Object -ExpandProperty SID if (-not $orphanedSIDs) { Write-Verbose "No orphaned profiles with valid SIDs found for removal on $ComputerName." return } # Step 3: Remove profiles for the collected SIDs $removalResults = Remove-ProfilesForSIDs -SIDs $orphanedSIDs -ComputerName $ComputerName -Confirm:$false # Step 4: Return the results of the removal process return $removalResults } #EndRegion './Public/Remove-OrphanedProfiles.ps1' 60 #Region './Public/Remove-ProfilesForSIDs.ps1' -1 <# .SYNOPSIS Orchestrates the deletion process for multiple profiles by SID. .DESCRIPTION The Remove-ProfilesForSIDs function removes profiles for multiple Security Identifiers (SIDs) from the ProfileList in the Windows registry. It loops through each SID provided, attempts to delete the associated profile, and returns the results of each deletion. If a profile cannot be found in the registry, the function will return a result indicating that the profile was not found. .PARAMETER SIDs An array of Security Identifiers (SIDs) for which the profile registry keys will be deleted. .PARAMETER ComputerName The name of the computer where the profiles reside. By default, this is the local computer. .EXAMPLE Remove-ProfilesForSIDs -SIDs "S-1-5-21-123456789-1001", "S-1-5-21-123456789-1002" -ComputerName "Server01" Deletes the profiles associated with the specified SIDs from the registry on "Server01" and returns the results of each deletion. .NOTES This function supports 'ShouldProcess', allowing the use of -WhatIf or -Confirm to simulate the deletion process. Each profile deletion is handled individually, with errors caught and returned in the final result. #> function Remove-ProfilesForSIDs { #Orchestrates the deletion process for multiple SIDs. [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'High')] param ( [Parameter(Mandatory = $true)] [string[]]$SIDs, # Accept multiple SIDs as an array [Parameter(Mandatory = $false)] [string]$ComputerName = $env:COMPUTERNAME # Default to local computer ) # Open the ProfileList registry key $RegistryPath = "SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList" $ProfileListKey = Open-RegistryKey -RegistryPath $RegistryPath -ComputerName $ComputerName if ($ProfileListKey -eq $null) { Write-Error "Failed to open ProfileList registry path on $ComputerName." return } $deletionResults = @() # Loop through each SID and process deletion foreach ($sid in $SIDs) { try { # Get profile information for the SID $sidProfileInfo = Get-SIDProfileInfo -SID $sid -ProfileListKey $ProfileListKey if (-not $sidProfileInfo.ExistsInRegistry) { $deletionResults += [ProfileDeletionResult]::new( $sid, $null, $false, $sidProfileInfo.Message, $ComputerName ) continue } # Process the deletion of the profile for the SID $deletionResult = Remove-SIDProfile -SID $sid ` -ProfileListKey $ProfileListKey ` -ComputerName $ComputerName ` -ProfilePath $sidProfileInfo.ProfilePath $deletionResults += $deletionResult } catch { Write-Error "An error occurred while processing SID '$sid'. $_" # Add a deletion result indicating failure due to error $deletionResults += [ProfileDeletionResult]::new( $sid, $null, $false, "Error occurred while processing SID '$sid'. Error: $_", $ComputerName ) } } # Close the registry key when done $ProfileListKey.Close() # Return the array of deletion results return $deletionResults } #EndRegion './Public/Remove-ProfilesForSIDs.ps1' 93 |