WinProfileOps.psm1
#Region './prefix.ps1' -1 # Your functions # Check if the current user is an administrator $windowsIdentity = [Security.Principal.WindowsIdentity]::GetCurrent() $windowsPrincipal = New-Object Security.Principal.WindowsPrincipal($windowsIdentity) $env:WinProfileOps_IsAdmin = $windowsPrincipal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator) $env:WinProfileOps_RegistryPath = "SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList" $env:WinProfileOps_RegistryHive = [Microsoft.Win32.RegistryHive]::LocalMachine $env:WinProfileOps_RegBackUpDirectory = "C:\LHStuff\RegBackUp" $env:WinProfileOps_ProfileFolderPath = $env:SystemDrive + "\Users" [scriptblock]$SB = { if (Test-Path Env:\WinProfileOps_IsAdmin) { Remove-Item Env:\WinProfileOps_IsAdmin -errorAction SilentlyContinue Remove-Item Env:\WinProfileOps_RegistryPath -ErrorAction SilentlyContinue Remove-Item Env:\WinProfileOps_RegistryHive -ErrorAction SilentlyContinue Remove-Item Env:\WinProfileOps_RegBackUpDirectory -ErrorAction SilentlyContinue Remove-Item Env:\WinProfileOps_ProfileFolderPath -ErrorAction SilentlyContinue } } Register-EngineEvent -SourceIdentifier PowerShell.Exiting -Action { $sb.Invoke() } # Define the OnRemove script block for the module $MyInvocation.MyCommand.ScriptBlock.Module.OnRemove = { $sb.Invoke() } #EndRegion './prefix.ps1' 32 #Region './Classes/ProfileDeletionResult.ps1' -1 class ProfileDeletionResult { [string]$SID [string]$ProfilePath [bool]$DeletionSuccess [string]$DeletionMessage [string]$ComputerName # Constructor 1: Full constructor 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 } # Constructor 2: Only SID and DeletionSuccess, with default values for others ProfileDeletionResult([string]$sid, [bool]$deletionSuccess) { $this.SID = $sid $this.ProfilePath = $null $this.DeletionSuccess = $deletionSuccess if ($deletionSuccess) { $this.DeletionMessage = "Operation successful" } else { $this.DeletionMessage = "Operation failed" } $this.ComputerName = $env:COMPUTERNAME } # Constructor 3: Minimal constructor with defaults for all except SID ProfileDeletionResult([string]$sid) { $this.SID = $sid $this.ProfilePath = $null $this.DeletionSuccess = $false $this.DeletionMessage = "No action performed" $this.ComputerName = $env:COMPUTERNAME } # Optional method [string] ToString() { return "[$($this.SID)] DeletionSuccess: $($this.DeletionSuccess), Message: $($this.DeletionMessage)" } } #EndRegion './Classes/ProfileDeletionResult.ps1' 52 #Region './Classes/UserProfile.ps1' -1 class UserProfile { [string]$SID [string]$ProfilePath [bool]$IsOrphaned [string]$OrphanReason = $null [string]$ComputerName [bool]$IsSpecial [string] GetUserNameFromPath() { return [System.IO.Path]::GetFileName($this.ProfilePath) # Extract the leaf (username) from the ProfilePath } # 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' 25 #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 -BaseKey $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 -BaseKey $ProfileListKey -Name $SID -writable $false 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/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). It validates the SID format, opens the corresponding registry subkeys, and fetches the ProfileImagePath for each valid 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. .parameter RegistryPath The registry path to the ProfileList key. Defaults to "SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList". .EXAMPLE Get-SIDProfileInfo -ComputerName "Server01" Retrieves profile information for all valid SIDs stored in the registry on "Server01". .EXAMPLE Get-SIDProfileInfo Retrieves profile information for all valid SIDs stored in the registry on the local computer. .OUTPUTS [PSCustomObject[]] An array of custom objects, where each object contains the following properties: - SID: [string] The Security Identifier of the profile. - ProfilePath: [string] The path to the user profile folder in the file system. - ComputerName: [string] The name of the computer from which the profile information was retrieved. - ExistsInRegistry: [bool] Indicates whether the profile exists in the registry. .NOTES - If a registry subkey for an SID cannot be opened, a warning is written to the output. - Invalid SID formats are skipped with a warning. - If a ProfileImagePath is not found for a valid SID, a verbose message is logged and the profile is returned with a null ProfilePath. - The function returns an empty array if no SIDs are found or if the registry path cannot be opened. #> function Get-SIDProfileInfo { [OutputType([PSCustomObject[]])] [CmdletBinding()] param ( [string]$ComputerName = $env:COMPUTERNAME, [string]$RegistryPath = $env:WinProfileOps_RegistryPath ) $ProfileListKey = Open-RegistryKey -RegistryPath $RegistryPath -ComputerName $ComputerName -Writable $false -RegistryHive $env:WinProfileOps_RegistryHive # Handle null or empty registry key if (-not $ProfileListKey) { Write-Error "Failed to open registry path: $RegistryPath on $ComputerName." return @() # Return an empty array } $subKeyNames = $ProfileListKey.GetSubKeyNames() # If no SIDs are found, return an empty array if (-not $subKeyNames -or $subKeyNames.Count -eq 0) { Write-Verbose "No SIDs found in the registry key on $ComputerName." return @() # Return an empty array } $ProfileRegistryItems = foreach ($sid in $subKeyNames) { # Validate SID format (SIDs typically start with 'S-1-' and follow a specific pattern) if (-not (Validate-SIDFormat -SID $sid)) { continue } # Use Open-RegistrySubKey to get the subkey for the SID $subKey = Open-RegistrySubKey -BaseKey $ProfileListKey -Name $sid -writable $false 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 if (-not $profilePath) { Write-Verbose "ProfileImagePath not found for SID '$sid'." $profilePath = $null } # Return a PSCustomObject with SID, ProfilePath, and ComputerName [PSCustomObject]@{ SID = $sid ProfilePath = $profilePath ComputerName = $ComputerName ExistsInRegistry = $true } } return $ProfileRegistryItems } #EndRegion './Private/Get-SIDProfileInfo.ps1' 104 #Region './Private/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 determines whether the target computer is local or remote and retrieves information such as the folder name, profile path, and computer name for each folder. If an error occurs during the folder retrieval, the function logs the error and returns an empty array. .PARAMETER ComputerName The name of the computer from which to retrieve user profile folders. If not provided, defaults to the local computer. .PARAMETER ProfileFolderPath The path to the folder where user profiles are stored. Defaults to "$env:SystemDrive\Users". This can be overridden to target a custom directory path for user profiles. .OUTPUTS [PSCustomObject[]] An array of custom objects, where each object contains the following properties: - FolderName: [string] The name of the user profile folder. - ProfilePath: [string] The full path to the user profile folder. - ComputerName: [string] The name of the computer from which the user profile folder was retrieved. .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. .EXAMPLE Get-UserFolders -ComputerName "RemotePC" Retrieves a list of user profile folders from the default user directory on the remote computer "RemotePC". .NOTES - If the Get-ChildItem command fails (e.g., due to access issues), the function logs an error and returns an empty array. - The ProfilePath for local computers is returned as a local path, while for remote computers, the folder is first accessed using a UNC path, but the returned ProfilePath is formatted as a local path for consistency. - Use the optional ProfileFolderPath parameter to target custom directories for user profiles. #> function Get-UserFolders { [OutputType([PSCustomObject[]])] [CmdletBinding()] param ( [string]$ComputerName = $env:COMPUTERNAME, [string]$ProfileFolderPath = $env:WinProfileOps_ProfileFolderPath ) $IsLocal = ($ComputerName -eq $env:COMPUTERNAME) $FolderPath = Get-DirectoryPath -BasePath $ProfileFolderPath -ComputerName $ComputerName -IsLocal $IsLocal try { # 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 } catch { # Handle the error when Get-ChildItem fails Write-Error "Failed to retrieve folders from '$FolderPath' on '$ComputerName'. Error: $_" return @() # Return an empty array in case of failure } } #EndRegion './Private/Get-UserFolders.ps1' 76 #Region './Private/Helpers/Get-SIDFromUsername.ps1' -1 <# .SYNOPSIS Retrieves the Security Identifier (SID) for a given username. .DESCRIPTION The `Get-SIDFromUsername` function resolves the Security Identifier (SID) associated with a given username using the .NET `System.Security.Principal.NTAccount` class. The function translates the provided username into a SID by querying the local system. If the user exists and the SID can be resolved, it is returned. Otherwise, a warning is displayed, and the function returns `$null`. .PARAMETER Username Specifies the username for which to retrieve the SID. This parameter is mandatory. .EXAMPLE Get-SIDFromUsername -Username 'JohnDoe' Description: This command retrieves the SID for the user 'JohnDoe' from the local computer. If the user exists and the SID is found, it is returned; otherwise, a warning will be displayed. .EXAMPLE Get-SIDFromUsername -Username 'LocalAdmin' Description: This command retrieves the SID for the user 'LocalAdmin' from the local computer. If the user exists and the SID is found, it is returned; otherwise, a warning will be displayed. .NOTES This function does not use WMI or CIM for querying user information, but rather the .NET `System.Security.Principal.NTAccount` class, which directly translates the username to a SID. As a result, this function works for both local and domain accounts if the appropriate access is available. #> function Get-SIDFromUsername { [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [string]$Username ) try { # Query WMI to get the SID for the given username $ntAccount = New-Object System.Security.Principal.NTAccount($Username) $SID = $ntAccount.Translate([System.Security.Principal.SecurityIdentifier]) if ($Null -ne $SID -and $Null -ne $SID.Value) { return $SID.value } else { Write-Warning "Could not find SID for username $Username." return $null } } catch { Write-Warning "An error occurred while trying to resolve SID for username $Username . Error: $_" return $null } } #EndRegion './Private/Helpers/Get-SIDFromUsername.ps1' 58 #Region './Private/Helpers/New-DirectoryIfNeeded.ps1' -1 <# .SYNOPSIS Creates a directory if it does not already exist. .DESCRIPTION The `New-DirectoryIfNeeded` function checks if the specified directory exists. If it doesn't, the function will create the directory and return the created directory object. If the directory already exists, the function returns `$true`. In case of any errors during directory creation, the function returns `$false` and logs the error. .PARAMETER Directory Specifies the full path of the directory to check or create. This parameter is mandatory. If the directory path is `null`, empty, or contains only whitespace, the function throws an error. .EXAMPLE New-DirectoryIfNeeded -Directory 'C:\Temp\NewFolder' Description: This command checks if the directory 'C:\Temp\NewFolder' exists. If it doesn't, the directory will be created. If the directory already exists, the function will return `$true`. .EXAMPLE New-DirectoryIfNeeded -Directory 'D:\Logs' Description: This command checks if the directory 'D:\Logs' exists. If it does not, the function will create the directory. If the directory already exists, it returns `$true`. .EXAMPLE $directory = New-DirectoryIfNeeded -Directory 'C:\Data\Reports' Description: This command attempts to create the directory 'C:\Data\Reports' if it doesn't exist and assigns the result to `$directory`. If successful, `$directory` will contain the created directory object. If the directory already exists, `$true` will be assigned to `$directory`. .NOTES If the directory path is invalid or if an error occurs during the creation process, the function writes an error message and returns `$false`. #> function New-DirectoryIfNeeded { param ( [Parameter(Mandatory = $true)] [string]$Directory ) try { # Check if the Directory parameter is null or an empty string if ([string]::IsNullOrWhiteSpace($Directory)) { throw [System.ArgumentException]::new("The 'Directory' parameter cannot be null or empty.") } # If the directory does not exist, attempt to create it if (-not (Test-Path -Path $Directory)) { $newDirectory = New-Item -Path $Directory -ItemType Directory -Force -ErrorAction Stop return $newDirectory } # If the directory exists, return $true return $true } catch { Write-Error "Failed to create directory: $Directory. Error: $_" return $false } } #EndRegion './Private/Helpers/New-DirectoryIfNeeded.ps1' 64 #Region './Private/Helpers/ShouldContinueWrapper.ps1' -1 <# .SYNOPSIS Handles user confirmation prompts using the `ShouldContinue` method. .DESCRIPTION The `ShouldContinueWrapper` function prompts the user to confirm whether they want to proceed with an operation. It uses the `ShouldContinue` method from the execution context to display a message to the user. The function logs whether the user chose to continue or not and returns the result. .PARAMETER Context Specifies the execution context, typically used to invoke the `ShouldContinue` method. .PARAMETER QueryMessage Specifies the message to display to the user asking if they are sure they want to proceed. .PARAMETER CaptionMessage Specifies the caption of the confirmation prompt, providing additional context about the operation. .EXAMPLE $context = Get-ExecutionContext ShouldContinueWrapper -Context $context -QueryMessage "Are you sure you want to delete these items?" -CaptionMessage "Confirm Deletion" Description: Prompts the user with the message "Are you sure you want to delete these items?" and the caption "Confirm Deletion". The function returns `$true` if the user chooses to continue, otherwise it returns `$false`. .NOTES This function assumes that it is called within an appropriate execution context where `ShouldContinue` can be invoked. #> function ShouldContinueWrapper { param ( [Parameter(Mandatory = $true)] $Context, [Parameter(Mandatory = $true)] [string]$QueryMessage, [Parameter(Mandatory = $true)] [string]$CaptionMessage ) $result = $Context.ShouldContinue($QueryMessage, $CaptionMessage) Write-Verbose "User chose to continue: $result" return $result } #EndRegion './Private/Helpers/ShouldContinueWrapper.ps1' 45 #Region './Private/Helpers/ShouldProcessWrapper.ps1' -1 <# .SYNOPSIS Handles the user confirmation for actions using the `ShouldProcess` method. .DESCRIPTION The `ShouldProcessWrapper` function prompts the user to confirm whether they want to proceed with a specified action on a specified target. It uses the `ShouldProcess` method from the execution context, logging the action and the target for verbose output. The function returns the result of the user's decision, allowing the calling function to proceed or halt based on the confirmation. .PARAMETER Context Specifies the execution context, typically used to invoke the `ShouldProcess` method. .PARAMETER Target Specifies the target of the action, such as a computer, file, or registry path, that the user is being asked to confirm. .PARAMETER ActionMessage Specifies the action that will be performed on the target, such as "Deleting", "Modifying", or "Stopping a service." .EXAMPLE $context = Get-ExecutionContext ShouldProcessWrapper -Context $context -Target "Server01" -ActionMessage "Delete profiles" Description: Prompts the user to confirm if they want to proceed with deleting profiles from "Server01". The function logs the action and the target, then returns `$true` if the user agrees, otherwise returns `$false`. .EXAMPLE ShouldProcessWrapper -Context $context -Target "C:\Temp\File.txt" -ActionMessage "Remove the file" Description: Prompts the user with the message "Remove the file" for the target file "C:\Temp\File.txt". It logs the action and returns the user's response. .NOTES This function is typically used in cmdlets or scripts that support the `ShouldProcess` functionality to allow confirmation before destructive or critical actions. #> function ShouldProcessWrapper { param ( [Parameter(Mandatory = $true)] $Context, [Parameter(Mandatory = $true)] [string]$Target, [Parameter(Mandatory = $true)] [string]$ActionMessage ) # Log the action message for verbose output Write-Verbose "About to perform action: $ActionMessage on $Target" # Use the ShouldProcess method from the context $result = $Context.ShouldProcess($Target, $ActionMessage) Write-Verbose "User chose to process: $result" return $result } #EndRegion './Private/Helpers/ShouldProcessWrapper.ps1' 57 #Region './Private/Helpers/Test-EnvironmentVariable.ps1' -1 <# .SYNOPSIS Validates the presence of a specific environment variable. .DESCRIPTION The Test-EnvironmentVariable function checks if the specified environment variable exists. If the variable is found, it returns its value. If not, an error is thrown. .PARAMETER Name The name of the environment variable to check. .EXAMPLE Test-EnvironmentVariable -Name 'Path' This command checks if the 'Path' environment variable is present and returns its value if found. .OUTPUTS String (Value of the environment variable) .NOTES This function will throw an error if the environment variable is missing from sesion. #> function Test-EnvironmentVariable { param ([string]$Name) # Dynamically retrieve the environment variable $value = Get-Item -Path "Env:$Name" -ErrorAction SilentlyContinue if (-not $value) { throw "Missing required environment variable: $Name" } return $value.Value } #EndRegion './Private/Helpers/Test-EnvironmentVariable.ps1' 37 #Region './Private/Helpers/Update-JsonFile.ps1' -1 <# .SYNOPSIS Updates an existing JSON file with new registry data or creates a new file if one doesn't exist. .DESCRIPTION The `Update-JsonFile` function checks if a specified JSON file exists and either updates it with new registry data or creates a new file. If the file exists, it reads the current data, appends the new registry data, and writes it back to the file. If the file does not exist, it creates a new file with the provided data. The function handles registry data in a generic array format. .PARAMETER OutputFile Specifies the path to the JSON file that should be updated or created. This parameter is mandatory. .PARAMETER RegistryData Specifies the new registry data to add to the JSON file. This should be passed as an array. The function will append this data to any existing data in the file, or it will create a new file with this data if the file doesn't exist. .EXAMPLE $registryData = @( @{ Name = 'HKEY_LOCAL_MACHINE\Software\TestKey'; Value = 'TestValue1' }, @{ Name = 'HKEY_LOCAL_MACHINE\Software\AnotherKey'; Value = 'TestValue2' } ) Update-JsonFile -OutputFile 'C:\Temp\RegistryData.json' -RegistryData $registryData Description: This example updates the file `RegistryData.json` in `C:\Temp` with the provided `$registryData`. If the file doesn't exist, it will be created. .EXAMPLE Update-JsonFile -OutputFile 'C:\Config\Settings.json' -RegistryData @(@{ Name = 'HKEY_CURRENT_USER\Software\MyApp'; Value = 'UserSetting' }) Description: This command appends the new registry data to the `Settings.json` file located in `C:\Config`. If the file doesn't exist, a new file is created with the registry data. .OUTPUTS None. This function writes updated data back to the file specified in the `OutputFile` parameter. .NOTES - The function automatically handles appending new data to existing arrays in the JSON file. - JSON files are written with a depth of 10 to ensure nested objects are properly serialized. #> function Update-JsonFile { param ( [Parameter(Mandatory = $true)] [string]$OutputFile, [Parameter(Mandatory = $true)] [array]$RegistryData # Generic data for registry keys ) if (Test-Path $OutputFile) { # Get the existing data and convert it from JSON $existingData = Get-Content -Path $OutputFile -Raw | ConvertFrom-Json # Ensure existing data is an array, wrap in an array if necessary if (-not ($existingData -is [System.Collections.IEnumerable])) { $existingData = @($existingData) } # Ensure the existing data is an array of objects if ($existingData -isnot [array]) { $existingData = @($existingData) } # Concatenate the existing data and the new data $combinedData = @($existingData + $RegistryData) # Write the updated data back to the file $combinedData | ConvertTo-Json -Depth 10 | Set-Content -Path $OutputFile -Confirm:$false } else { # Create a new JSON file with the provided registry data $RegistryData | ConvertTo-Json -Depth 10 | Out-File -FilePath $OutputFile } } #EndRegion './Private/Helpers/Update-JsonFile.ps1' 77 #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/RemoveProfileReg/Backup-RegistryKeyForSID.ps1' -1 <# .SYNOPSIS Backs up a registry key associated with a specific SID to a specified directory. .DESCRIPTION The `Backup-RegistryKeyForSID` function creates a backup of the registry key associated with the provided SID from a remote or local machine. It ensures that the backup directory exists before proceeding, creates a JSON representation of the registry data, and appends the backup to an existing JSON file. .PARAMETER SID Specifies the Security Identifier (SID) for which the registry key backup is created. .PARAMETER BaseKey Specifies the base registry key under which the SID subkey exists. .PARAMETER RegBackUpDirectory Specifies the directory where the registry backup will be saved. .PARAMETER ComputerName Specifies the name of the computer from which the registry key is being backed up. .EXAMPLE Backup-RegistryKeyForSID -SID 'S-1-5-21-...' -BaseKey $RegistryKey -RegBackUpDirectory 'C:\Backups' -ComputerName 'Server01' Description: Backs up the registry key for the specified SID from Server01 to the 'C:\Backups' directory. .OUTPUTS Boolean indicating success or failure. .NOTES This function relies on helper functions like `New-DirectoryIfNeeded` and `New-RegistryKeyValuesObject` to handle registry operations. #> function Backup-RegistryKeyForSID { param ( [Parameter(Mandatory = $true)] [string]$SID, [Parameter(Mandatory = $true)] [Microsoft.Win32.RegistryKey]$BaseKey, [Parameter(Mandatory = $true)] [string]$RegBackUpDirectory, [Parameter(Mandatory = $true)] [string]$ComputerName ) try { # Ensure the backup directory exists $directoryCreated = New-DirectoryIfNeeded -Directory $RegBackUpDirectory # Check if directory creation failed if (-not $directoryCreated) { Write-Error "Error creating or accessing backup directory: $RegBackUpDirectory" return $false } # Backup the registry key associated with the SID $RegBackUpObject = New-RegistryKeyValuesObject -RegistryKey $BaseKey -ComputerName $ComputerName -SubKeyName $SID $RegBackUpObjectJson = $RegBackUpObject.psobject.copy() $RegBackUpObjectJson.BackUpDate = $RegBackUpObject.BackUpDate.tostring("o") # Update the backup JSON file with the registry data Update-JsonFile -OutputFile "$RegBackUpDirectory\RegBackUp.json" -RegistryData $RegBackUpObjectJson return $true } catch { Write-Error "Error backing up registry for SID $SID`: $_" return $false } } #EndRegion './Private/RemoveProfileReg/Backup-RegistryKeyForSID.ps1' 77 #Region './Private/RemoveProfileReg/Confirm-ProfileRemoval.ps1' -1 <# .SYNOPSIS Verifies whether a registry key for a specific SID has been successfully removed. .DESCRIPTION The `Confirm-ProfileRemoval` function checks whether the registry key associated with the specified SID still exists. If the key no longer exists, the function returns `$true`; otherwise, it returns `$false`. .PARAMETER SID Specifies the Security Identifier (SID) whose registry key removal is being confirmed. .PARAMETER BaseKey Specifies the base registry key under which the SID subkey exists. .EXAMPLE Confirm-ProfileRemoval -SID 'S-1-5-21-...' -BaseKey $RegistryKey Description: Checks if the registry key for the specified SID has been successfully removed. .OUTPUTS Boolean indicating whether the registry key was removed. #> function Confirm-ProfileRemoval { param ( [string]$SID, [Microsoft.Win32.RegistryKey]$BaseKey ) try { return ($BaseKey.GetSubKeyNames() -notcontains $SID) } catch { Write-Error "Error verifying profile removal for SID $SID`: $_" return $false } } #EndRegion './Private/RemoveProfileReg/Confirm-ProfileRemoval.ps1' 41 #Region './Private/RemoveProfileReg/Invoke-UserProfileRegRemoval.ps1' -1 <# .SYNOPSIS Removes user profile registry entries from local or remote computers, with optional confirmation. .DESCRIPTION The `Invoke-UserProfileRegRemoval` function processes user profiles for removal based on Security Identifiers (SIDs). It retrieves profiles from a specified registry path and profile folder, performs an audit, and optionally prompts for confirmation before removal. The `Force` switch can bypass the confirmation prompt, and the `AuditOnly` switch allows auditing without any removal action. If the registry key cannot be opened or the audit fails, the function terminates early to prevent further processing. .PARAMETER ComputerName Specifies the name of the computer where the profile removal is executed. This can be a local or remote machine. .PARAMETER SID Specifies the Security Identifier (SID) of the user profile to remove. This parameter accepts pipeline input, allowing multiple SIDs to be processed sequentially. .PARAMETER RegistryPath Specifies the registry path where user profile information is stored. For example, `SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList`. .PARAMETER ProfileFolderPath Specifies the folder path where user profile data is stored. For example, `C:\Users`. .PARAMETER RegistryHive Specifies the registry hive (e.g., HKLM for HKEY_LOCAL_MACHINE or HKCU for HKEY_CURRENT_USER) under which the profile keys are located. .PARAMETER Force Forces the removal of profiles without prompting for confirmation. When this switch is used, profiles are removed without any user interaction. .PARAMETER AuditOnly Performs an audit without removing any profiles. The audit results are output to the pipeline, and no changes are made to the registry. .PARAMETER Confirm If specified, the user is prompted for confirmation before removing each profile. The prompt is skipped if `Force` or `AuditOnly` switches are used. .EXAMPLE Get-UserProfiles | Invoke-UserProfileRegRemoval -ComputerName 'Server01' -RegistryPath 'SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList' -ProfileFolderPath 'C:\Users' -RegistryHive 'LocalMachine' -Force Description: Removes all user profiles from the registry on Server01 without prompting for confirmation, as the `Force` switch is used. .EXAMPLE Get-UserProfiles | Invoke-UserProfileRegRemoval -ComputerName 'Server02' -RegistryPath 'SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList' -ProfileFolderPath 'C:\Users' -RegistryHive 'LocalMachine' -AuditOnly Description: Performs an audit of the user profiles on Server02, but does not remove any profiles. The audit results are output to the pipeline. .EXAMPLE 'S-1-5-21-12345' | Invoke-UserProfileRegRemoval -ComputerName 'Server03' -RegistryPath 'SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList' -ProfileFolderPath 'C:\Users' -RegistryHive 'LocalMachine' Description: Processes the specified SID ('S-1-5-21-12345') for removal on Server03. If `Confirm` is specified, the user is prompted before the profile is removed. .NOTES - This function uses pipeline input to process multiple SIDs. - The function handles both local and remote computers. - Errors during registry key access or audit failure result in early termination. - If special system profiles are detected during the audit, they can be skipped based on the implementation of the audit function. #> function Invoke-UserProfileRegRemoval { [CmdletBinding()] param ( [string]$ComputerName, # Accept pipeline input for each SID [Parameter(ValueFromPipeline = $true)] [string]$SID, [string]$RegistryPath, [string]$ProfileFolderPath, [Microsoft.Win32.RegistryHive]$RegistryHive, [switch]$Force, [switch]$AuditOnly, [bool]$Confirm ) Begin { # Initialize a flag to determine if processing should continue $continueProcessing = $true try { # Try to open the registry key $BaseKey = Open-RegistryKey -ComputerName $ComputerName -RegistryHive $RegistryHive -RegistryPath $RegistryPath -ErrorAction SilentlyContinue # Check if the registry key is valid if (-not $BaseKey) { throw "Failed to open registry key on computer $ComputerName" } # Perform the audit if the BaseKey is valid $userProfileAudit = Invoke-UserProfileAudit -ComputerName $ComputerName -ProfileFolderPath $ProfileFolderPath -IgnoreSpecial if (-not $userProfileAudit) { throw "Failed to audit user profiles on computer $ComputerName" } } catch { # Catch any exceptions that occur during the process Write-Error $_.Exception.Message $continueProcessing = $false # Set the flag to prevent further processing return # Exit the function early if an error occurs } } Process { # Only proceed if the flag allows processing if ($continueProcessing) { # Process each SID as it flows through the pipeline $SelectedProfile = Resolve-UserProfileForDeletion -SID $SID -AuditResults $userProfileAudit -ComputerName $ComputerName if ($SelectedProfile -is [ProfileDeletionResult]) { # Output the ProfileDeletionResult directly to the pipeline $SelectedProfile } else { # Skip confirmation if AuditOnly is used if (-not $AuditOnly) { if (-not $Force -and (ShouldContinueWrapper -Context $PSCmdlet -QueryMessage "Do you want to delete SID $SID from $($SelectedProfile.ComputerName)?" -CaptionMessage "Confirm Deletion")) { $result = Remove-UserProfileRegistryEntry -SelectedProfile $SelectedProfile -BaseKey $BaseKey -AuditOnly:$AuditOnly $result } elseif ($Force) { $result = Remove-UserProfileRegistryEntry -SelectedProfile $SelectedProfile -BaseKey $BaseKey -AuditOnly:$AuditOnly $result } } else { # Just process without confirmation $result = Remove-UserProfileRegistryEntry -SelectedProfile $SelectedProfile -BaseKey $BaseKey -AuditOnly:$AuditOnly $result } } } } End { # Clean up resources if ($BaseKey) { $BaseKey.Dispose() } } } #EndRegion './Private/RemoveProfileReg/Invoke-UserProfileRegRemoval.ps1' 158 #Region './Private/RemoveProfileReg/New-ProfileDeletionResult.ps1' -1 <# .SYNOPSIS Creates a new `ProfileDeletionResult` object with details of a user profile deletion. .DESCRIPTION The `New-ProfileDeletionResult` function generates a new object representing the outcome of a user profile deletion operation. This object can include details such as the SID, profile path, deletion status, and computer name. .PARAMETER SID Specifies the Security Identifier (SID) of the user profile. .PARAMETER ProfilePath Specifies the path to the user profile that was deleted (optional). .PARAMETER DeletionSuccess Specifies whether the profile deletion was successful. .PARAMETER DeletionMessage Provides a message regarding the profile deletion result. .PARAMETER ComputerName Specifies the name of the computer from which the profile was removed. .EXAMPLE New-ProfileDeletionResult -SID 'S-1-5-21-...' -DeletionSuccess $true -DeletionMessage 'Profile removed successfully.' Description: Creates a `ProfileDeletionResult` object indicating that the profile for the specified SID was successfully removed. .OUTPUTS ProfileDeletionResult object containing the details of the deletion operation. #> function New-ProfileDeletionResult { [CmdletBinding(DefaultParameterSetName = 'Minimal')] param ( # SID is mandatory in all parameter sets [Parameter(Mandatory = $true, ParameterSetName = 'Full')] [Parameter(Mandatory = $true, ParameterSetName = 'SuccessOnly')] [Parameter(Mandatory = $true, ParameterSetName = 'Minimal')] [string]$SID, # Full parameter set properties [Parameter(Mandatory = $false, ParameterSetName = 'Full')] [string]$ProfilePath =$null, [Parameter(Mandatory = $true, ParameterSetName = 'Full')] [Parameter(Mandatory = $true, ParameterSetName = 'SuccessOnly')] [bool]$DeletionSuccess, [Parameter(Mandatory = $true, ParameterSetName = 'Full')] [string]$DeletionMessage, [Parameter(Mandatory = $true, ParameterSetName = 'Full')] [string]$ComputerName ) switch ($PSCmdlet.ParameterSetName) { 'Full' { return [ProfileDeletionResult]::new($SID, $ProfilePath, $DeletionSuccess, $DeletionMessage, $ComputerName) } 'SuccessOnly' { return [ProfileDeletionResult]::new($SID, $DeletionSuccess) } 'Minimal' { return [ProfileDeletionResult]::new($SID) } } } #EndRegion './Private/RemoveProfileReg/New-ProfileDeletionResult.ps1' 73 #Region './Private/RemoveProfileReg/PromptForConfirmation.ps1' -1 <# .SYNOPSIS Prompts the user for confirmation before proceeding with a deletion operation. .DESCRIPTION The `PromptForConfirmation` function asks the user to confirm before performing a deletion operation on a specified computer's registry. If the `AuditOnly` flag is specified, the prompt is skipped. If `Confirm` is set to `$true`, the function displays a confirmation message with details about the number of items to delete and the target computer. The user response is handled by the `ShouldContinueWrapper` function, which manages the confirmation prompt. .PARAMETER ComputerName Specifies the name of the computer where the deletion operation will take place. .PARAMETER ItemCount Specifies the number of profiles to delete from the computer's registry. This is displayed in the confirmation message. .PARAMETER AuditOnly If this switch is specified, the function will skip the confirmation prompt and proceed without making any changes. This is typically used for audit or dry-run scenarios. .PARAMETER Confirm If this switch is specified, the function will always prompt the user for confirmation before proceeding. .PARAMETER context Specifies the execution context, typically used to access methods like `ShouldContinue` for the confirmation prompt. .EXAMPLE PromptForConfirmation -ComputerName 'Server01' -ItemCount 5 -Confirm Description: Prompts the user to confirm the deletion of 5 profiles from the registry of 'Server01'. If the user confirms, the function returns `$true`; otherwise, it returns `$false`. .EXAMPLE PromptForConfirmation -ComputerName 'Server02' -ItemCount 10 -AuditOnly Description: Skips the confirmation prompt since the `AuditOnly` switch is used, and returns `$true` to proceed with the audit operation. .NOTES The function assumes that `ShouldContinueWrapper` is available to handle the actual confirmation prompt. #> function PromptForConfirmation { param ( [string]$ComputerName, [int]$ItemCount, [switch]$AuditOnly, [switch]$Confirm, $context ) # Skip prompt if in AuditOnly mode if ($AuditOnly) { return $true } # Always prompt unless Force is specified or Confirm is explicitly set to false if ($Confirm -eq $true) { $QueryMessage = "Are you sure you want to delete $ItemCount profiles from $ComputerName's registry?" $CaptionMessage = "Confirm Deletion" # Use the ShouldContinueWrapper to handle the prompt return (ShouldContinueWrapper -Context $context -QueryMessage $QueryMessage -CaptionMessage $CaptionMessage) } return $true # Proceed if Force is used or if AuditOnly is true } #EndRegion './Private/RemoveProfileReg/PromptForConfirmation.ps1' 68 #Region './Private/RemoveProfileReg/Remove-ProfileRegistryKey.ps1' -1 <# .SYNOPSIS Removes a registry key associated with a specific SID. .DESCRIPTION The `Remove-ProfileRegistryKey` function deletes the registry key associated with a specified SID. If the operation fails, an error is logged. .PARAMETER SID Specifies the Security Identifier (SID) whose registry key is being removed. .PARAMETER BaseKey Specifies the base registry key under which the SID subkey exists. .EXAMPLE Remove-ProfileRegistryKey -SID 'S-1-5-21-...' -BaseKey $RegistryKey Description: Removes the registry key for the specified SID from the provided base key. .OUTPUTS Boolean indicating whether the registry key was successfully removed. #> function Remove-ProfileRegistryKey { param ( [string]$SID, [Microsoft.Win32.RegistryKey]$BaseKey ) try { Remove-RegistrySubKey -ParentKey $BaseKey -SubKeyName $SID -ThrowOnMissingSubKey $false -Confirm:$false return $true } catch { Write-Error "Error removing registry key for SID $SID`: $_" return $false } } #EndRegion './Private/RemoveProfileReg/Remove-ProfileRegistryKey.ps1' 42 #Region './Private/RemoveProfileReg/Remove-UserProfileRegistryEntry.ps1' -1 <# .SYNOPSIS Removes a user profile registry entry and backs up the registry data before deletion. .DESCRIPTION The `Remove-UserProfileRegistryEntry` function removes a user profile from the Windows registry. Before removal, it backs up the registry data to a specified directory. The function also supports audit mode, where no deletion occurs but an audit log is created. .PARAMETER SelectedProfile Specifies the user profile object representing the profile to be deleted. .PARAMETER BaseKey Specifies the base registry key under which the profile's SID subkey exists. .PARAMETER AuditOnly If specified, the function will only perform an audit and will not delete the registry entry. .EXAMPLE Remove-UserProfileRegistryEntry -SelectedProfile $Profile -BaseKey $RegistryKey -AuditOnly Description: Performs an audit of the profile without deleting it from the registry. .OUTPUTS ProfileDeletionResult object indicating the outcome of the deletion or audit operation. #> function Remove-UserProfileRegistryEntry { [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'High')] param ( [Parameter(ValueFromPipelineByPropertyName = $true)] [UserProfile]$SelectedProfile, [Microsoft.Win32.RegistryKey]$BaseKey, [switch]$AuditOnly ) Process { # Prepare the deletion result parameters $deletionResultParams = @{ SID = $SelectedProfile.SID ProfilePath = $SelectedProfile.ProfilePath ComputerName = $SelectedProfile.ComputerName DeletionSuccess = $false DeletionMessage = "Profile not removed." } # Check if BaseKey is null if (-not $BaseKey) { $deletionResultParams.DeletionMessage = "Failed: BaseKey is null, cannot remove the profile." New-ProfileDeletionResult @deletionResultParams return # Return early to stop further processing } # If in audit mode, output an audit-only result directly to the pipeline and return if ($AuditOnly) { $deletionResultParams.DeletionSuccess = $true $deletionResultParams.DeletionMessage = "Audit only, no deletion performed." New-ProfileDeletionResult @deletionResultParams return # Return to allow pipeline to continue with the next item } # Determine backup directory $RegBackUpDirectory = Get-DirectoryPath -basePath $env:WinProfileOps_RegBackUpDirectory -ComputerName $SelectedProfile.ComputerName -IsLocal ($SelectedProfile.ComputerName -eq $env:COMPUTERNAME) # Backup the registry key, output failure message if backup fails and skip further processing if (-not (Backup-RegistryKeyForSID -SID $SelectedProfile.SID -BaseKey $BaseKey -RegBackUpDirectory $RegBackUpDirectory -ComputerName $SelectedProfile.ComputerName)) { $deletionResultParams.DeletionMessage = "Failed to backup profile." New-ProfileDeletionResult @deletionResultParams return # Return to allow pipeline to continue with the next item } # Remove the registry key, output failure message if removal fails if (-not (Remove-ProfileRegistryKey -SID $SelectedProfile.SID -BaseKey $BaseKey)) { $deletionResultParams.DeletionMessage = "Failed to remove profile registry key." New-ProfileDeletionResult @deletionResultParams return # Return to allow pipeline to continue with the next item } # Verify the removal and update the result if (Confirm-ProfileRemoval -SID $SelectedProfile.SID -BaseKey $BaseKey) { $deletionResultParams.DeletionSuccess = $true $deletionResultParams.DeletionMessage = "Profile removed successfully." } else { $deletionResultParams.DeletionMessage = "Profile removal verification failed." } # Output the final deletion result New-ProfileDeletionResult @deletionResultParams } } #EndRegion './Private/RemoveProfileReg/Remove-UserProfileRegistryEntry.ps1' 99 #Region './Private/RemoveProfileReg/Resolve-UsernamesToSIDs.ps1' -1 <# .SYNOPSIS Resolves a list of usernames to their corresponding Security Identifiers (SIDs). .DESCRIPTION The `Resolve-UsernamesToSIDs` function resolves each provided username to its corresponding Security Identifier (SID) using the .NET `System.Security.Principal.NTAccount` class. For each username in the input array, the function attempts to resolve the username locally. If a username cannot be resolved, a warning is logged, and the function continues processing the next username. .PARAMETER Usernames Specifies an array of usernames to resolve to SIDs. This parameter is mandatory. .EXAMPLE Resolve-UsernamesToSIDs -Usernames 'user1', 'user2' Description: Resolves the SIDs for 'user1' and 'user2' on the local computer. .OUTPUTS Array of SIDs corresponding to the provided usernames. If a username cannot be resolved, it will not be included in the output array, and a warning will be logged. .NOTES This function uses the `Get-SIDFromUsername` function, which internally uses the .NET `System.Security.Principal.NTAccount` class for resolving SIDs. It does not support resolving SIDs from remote computers and works only on the local system. #> function Resolve-UsernamesToSIDs { param ( [string[]]$Usernames ) $SIDs = @() foreach ($Username in $Usernames) { $SID = Get-SIDFromUsername -Username $Username if ($SID) { $SIDs += $SID } else { Write-Warning "Could not resolve SID for username $Username." } } return $SIDs } #EndRegion './Private/RemoveProfileReg/Resolve-UsernamesToSIDs.ps1' 44 #Region './Private/RemoveProfileReg/Resolve-UserProfileForDeletion.ps1' -1 <# .SYNOPSIS Finds the user profile for a specific SID in an audit result. .DESCRIPTION The `Resolve-UserProfileForDeletion` function searches through audit results to find the profile associated with a given SID. If the profile is not found, a warning is logged, and a `ProfileDeletionResult` is returned indicating failure. .PARAMETER SID Specifies the Security Identifier (SID) of the profile to search for. .PARAMETER AuditResults Specifies the audit results to search for the profile. .PARAMETER ComputerName Specifies the name of the computer where the profile is located. .EXAMPLE Resolve-UserProfileForDeletion -SID 'S-1-5-21-...' -AuditResults $AuditResults -ComputerName 'Server01' Description: Finds the user profile associated with the specified SID in the audit results for Server01. .OUTPUTS UserProfile or ProfileDeletionResult object. #> function Resolve-UserProfileForDeletion { param ( [Parameter(Mandatory = $true)] [string]$SID, # The SID to search for [Parameter(Mandatory = $false)] [UserProfile[]]$AuditResults, # The audit results [Parameter(Mandatory = $true)] [string]$ComputerName # The target computer name ) # Find the corresponding user profile from the audit $SelectedProfile = $AuditResults | Where-Object { $_.SID -eq $SID } # Handle cases where profile is not found if ($null -eq $SelectedProfile) { # Determine if it's an invalid SID or just not found $message = if (Validate-SIDFormat -SID $SID) { "Profile not found" Write-Warning "Profile not found for SID: $SID on $ComputerName." } else { "Invalid SID format encountered" Write-Warning "Invalid SID format encountered: $SID on $ComputerName." } # Return a ProfileDeletionResult if the profile is not found or invalid return New-ProfileDeletionResult -SID $SID -ProfilePath $null -DeletionSuccess $false -DeletionMessage $message -ComputerName $ComputerName } # If profile is found, return the UserProfile object return $SelectedProfile } #EndRegion './Private/RemoveProfileReg/Resolve-UserProfileForDeletion.ps1' 62 #Region './Private/UserProfileAudit/Process-FolderProfiles.ps1' -1 <# .SYNOPSIS Processes user profile folders to identify profiles missing in the registry. .DESCRIPTION The Process-FolderProfiles function compares user profiles found in the file system (folders) against those found in the registry. It identifies folder profiles that do not have a corresponding entry in the registry and marks them as orphaned. The function also allows the option to ignore special or default profiles, such as system or service accounts. .PARAMETER UserFolders An array of user profile folders found in the file system (e.g., C:\Users). .PARAMETER RegistryProfiles An array of user profiles found in the registry. .PARAMETER ComputerName The name of the computer being audited for user profiles. .PARAMETER IgnoreSpecial Switch to ignore special or default profiles (such as system or service accounts) during processing. .OUTPUTS [UserProfile[]] Returns an array of UserProfile objects representing user profiles found in the file system but missing in the registry. .EXAMPLE $userFolders = Get-UserProfilesFromFolders -ComputerName "Server01" $registryProfiles = Get-UserProfilesFromRegistry -ComputerName "Server01" Process-FolderProfiles -UserFolders $userFolders -RegistryProfiles $registryProfiles -ComputerName "Server01" Processes the user profile folders on "Server01" to identify any profiles missing from the registry. .EXAMPLE Process-FolderProfiles -UserFolders $folders -RegistryProfiles $registryProfiles -ComputerName "Server01" -IgnoreSpecial Processes the user profile folders on "Server01" while ignoring special or system profiles. .NOTES This function compares profiles in the file system with those in the registry to identify orphaned profiles that exist only in the file system. Special profiles can be ignored with the -IgnoreSpecial switch. #> function Process-FolderProfiles { [OutputType ([UserProfile[]])] param ( [array]$UserFolders, [array]$RegistryProfiles, [string]$ComputerName, [switch]$IgnoreSpecial ) $processedProfiles = @() foreach ($folder in $UserFolders) { $registryProfile = $RegistryProfiles | Where-Object { $_.ProfilePath -eq $folder.ProfilePath } $isSpecial = Test-SpecialAccount -FolderName $folder.FolderName -SID $null -ProfilePath $folder.ProfilePath if ($IgnoreSpecial -and $isSpecial) { continue } if (-not $registryProfile) { $processedProfiles += New-UserProfileObject $null $folder.ProfilePath $true "MissingRegistryEntry" $ComputerName $isSpecial } } return $processedProfiles } #EndRegion './Private/UserProfileAudit/Process-FolderProfiles.ps1' 73 #Region './Private/UserProfileAudit/Process-RegistryProfiles.ps1' -1 <# .SYNOPSIS Processes user profiles found in the registry to identify orphaned profiles. .DESCRIPTION The Process-RegistryProfiles function compares user profiles found in the registry against the corresponding profile folders on the file system. It identifies registry profiles that are orphaned, meaning they exist in the registry but are missing in the file system. The function also provides an option to ignore special or default profiles, such as system or service accounts. .PARAMETER RegistryProfiles An array of user profiles retrieved from the registry. .PARAMETER ComputerName The name of the computer being audited for user profiles. .PARAMETER IgnoreSpecial Switch to ignore special or default profiles (such as system or service accounts) during processing. .OUTPUTS [UserProfile[]] Returns an array of UserProfile objects representing user profiles found in the registry but missing in the file system. .EXAMPLE $registryProfiles = Get-UserProfilesFromRegistry -ComputerName "Server01" Process-RegistryProfiles -RegistryProfiles $registryProfiles -ComputerName "Server01" Processes the registry profiles on "Server01" to identify any profiles that are orphaned in the registry. .EXAMPLE Process-RegistryProfiles -RegistryProfiles $registryProfiles -ComputerName "Server01" -IgnoreSpecial Processes the registry profiles on "Server01" while ignoring special or system profiles. .NOTES This function compares profiles in the registry with their corresponding profile folders on the file system to identify orphaned profiles that exist only in the registry. Special profiles can be ignored with the -IgnoreSpecial switch. #> function Process-RegistryProfiles { [OutputType ([UserProfile[]])] param ( [array]$RegistryProfiles, [string]$ComputerName, [switch]$IgnoreSpecial ) $processedProfiles = @() foreach ($regProfile in $RegistryProfiles) { $profilePath = $regProfile.ProfilePath $folderExists = $false $accessError = $false if ($null -eq $profilePath) { $isSpecial = Test-SpecialAccount -SID $regProfile.SID $userProfile = Test-OrphanedProfile -SID $regProfile.SID -ProfilePath $null ` -FolderExists $folderExists -AccessError $accessError -IgnoreSpecial $IgnoreSpecial ` -IsSpecial $isSpecial -ComputerName $ComputerName # Add this line to include the user profile in the processed array $processedProfiles += $userProfile continue } $folderName = Split-Path -Path $profilePath -Leaf $isSpecial = Test-SpecialAccount -FolderName $folderName -SID $regProfile.SID -ProfilePath $profilePath if ($IgnoreSpecial -and $isSpecial) { continue } try { $folderExists = Test-FolderExists -ProfilePath $profilePath -ComputerName $ComputerName -ErrorAction Stop } catch [UnauthorizedAccessException] { $folderExists = $false $accessError = $true } catch { $folderExists = $false Write-Warning "Error testing folder existence for profile: $profilePath. Error: $_" } $userProfile = Test-OrphanedProfile -SID $regProfile.SID -ProfilePath $profilePath ` -FolderExists $folderExists -AccessError $accessError -IgnoreSpecial $IgnoreSpecial ` -IsSpecial $isSpecial -ComputerName $ComputerName # Add this line to include the user profile in the processed array $processedProfiles += $userProfile } return $processedProfiles } #EndRegion './Private/UserProfileAudit/Process-RegistryProfiles.ps1' 103 #Region './Private/UserProfileAudit/Test-ComputerReachability.ps1' -1 <# .SYNOPSIS Tests whether a specified computer is reachable by performing a network ping. .DESCRIPTION The Test-ComputerReachability function checks whether a specified computer is reachable by using a ping test. If the computer is unreachable, the function logs a warning message and returns `$false`. If the computer is reachable, it returns `$true`. .PARAMETER ComputerName The name of the computer to test for network reachability. .OUTPUTS [bool] Returns `$true` if the computer is reachable, `$false` if the computer is offline or unreachable. .EXAMPLE Test-ComputerReachability -ComputerName "Server01" Tests the reachability of "Server01" and returns `$true` if it is reachable, `$false` otherwise. .NOTES This function uses Test-ComputerPing to perform the ping test. If the computer is offline or unreachable, a warning is logged and the function returns `$false`. #> function Test-ComputerReachability { [OutputType ([bool])] param ( [string]$ComputerName = $env:COMPUTERNAME ) if ($ComputerName -eq $null) { Write-Warning "No computer name provided." return $false } if (-not (Test-ComputerPing -ComputerName $ComputerName)) { Write-Warning "Computer '$ComputerName' is offline or unreachable." return $false } return $true } #EndRegion './Private/UserProfileAudit/Test-ComputerReachability.ps1' 46 #Region './Private/ValidateFunctions/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 [UnauthorizedAccessException] { Write-Warning "Access denied when testing folder existence for profile: $ProfilePath. Error: $_" throw } catch { Write-Error "An error occurred: $_" return $false } } #EndRegion './Private/ValidateFunctions/Test-FolderExists.ps1' 78 #Region './Private/ValidateFunctions/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 AccessError Indicates whether an access error occurred while testing the profile folder. .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]$AccessError, [bool]$IgnoreSpecial, [bool]$IsSpecial, [string]$ComputerName ) if ($AccessError) { return New-UserProfileObject -SID $SID -ProfilePath $ProfilePath -IsOrphaned $false ` -OrphanReason "AccessDenied" -ComputerName $ComputerName -IsSpecial $IsSpecial } elseif (-not $ProfilePath) { return New-UserProfileObject -SID $SID -ProfilePath $null -IsOrphaned $true ` -OrphanReason "MissingProfileImagePath" -ComputerName $ComputerName -IsSpecial $IsSpecial } elseif (-not $FolderExists) { return New-UserProfileObject -SID $SID -ProfilePath $ProfilePath -IsOrphaned $true ` -OrphanReason "MissingFolder" -ComputerName $ComputerName -IsSpecial $IsSpecial } elseif (-not $ProfilePath -and -not $FolderExists) { return New-UserProfileObject -SID $SID -ProfilePath $null -IsOrphaned $true ` -OrphanReason "MissingProfileImagePathAndFolder" -ComputerName $ComputerName -IsSpecial $IsSpecial } else { return New-UserProfileObject -SID $SID -ProfilePath $ProfilePath -IsOrphaned $false ` -OrphanReason $null -ComputerName $ComputerName -IsSpecial $IsSpecial } } #EndRegion './Private/ValidateFunctions/Test-OrphanedProfile.ps1' 62 #Region './Private/ValidateFunctions/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/ValidateFunctions/Test-SpecialAccount.ps1' 49 #Region './Private/ValidateFunctions/Validate-SIDFormat.ps1' -1 <# .SYNOPSIS Validates whether a given string follows the correct SID (Security Identifier) format. .DESCRIPTION The Validate-SIDFormat function checks if a given string matches the standard SID format. SIDs typically start with 'S-1-' followed by a series of digits separated by hyphens. This function returns $true if the SID format is valid and $false if it is not. .PARAMETER SID The SID string to validate. This should follow the typical format: 'S-1-' followed by a series of digits and hyphens. .OUTPUTS [bool] Returns $true if the SID format is valid; otherwise, returns $false. .EXAMPLE PS> Validate-SIDFormat -SID 'S-1-5-18' True This example checks if the SID 'S-1-5-18' is valid. .EXAMPLE PS> Validate-SIDFormat -SID 'Invalid-SID' WARNING: Invalid SID format encountered: 'Invalid-SID'. False This example demonstrates how the function handles an invalid SID format by returning $false and issuing a warning. .NOTES .LINK https://docs.microsoft.com/en-us/windows/win32/secauthz/security-identifiers #> function Validate-SIDFormat { param ( [OutPutType([bool])] [CmdletBinding()] [Parameter(Mandatory = $true)] [string]$SID ) # Regular expression pattern for validating the SID format $sidPattern = '^S-1-\d+(-\d+)+$' if ($SID -notmatch $sidPattern) { Write-Warning "Invalid SID format encountered: '$SID'." return $false } return $true } #EndRegion './Private/ValidateFunctions/Validate-SIDFormat.ps1' 58 #Region './Public/Get-OrphanedProfiles.ps1' -1 <# .SYNOPSIS Retrieves orphaned user profiles from a specified computer. .DESCRIPTION The Get-OrphanedProfiles function scans both the user profile folders and registry on a specified computer and returns profiles that are considered orphaned. Orphaned profiles are those that exist either in the file system but not in the registry, or in the registry but no longer have a corresponding folder in the file system. .PARAMETER ComputerName The name of the computer from which to retrieve orphaned user 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 during the profile retrieval process. .OUTPUTS [UserProfile[]] Returns an array of UserProfile objects that represent the profiles found during the audit. Each UserProfile object contains the following properties: - SID: [string] The security identifier of the user profile. - ProfilePath: [string] The file system path of the user profile. - IsOrphaned: [bool] Whether the profile is considered orphaned. - OrphanReason: [string] The reason for orphaned status if applicable. - ComputerName: [string] The name of the computer where the audit was performed. - IsSpecial: [bool] Whether the profile is considered special or a system account. .EXAMPLE Get-OrphanedProfiles -ComputerName "Server01" Retrieves all orphaned user profiles from both the file system and registry on "Server01". .EXAMPLE Get-OrphanedProfiles -ProfileFolderPath "D:\UserProfiles" -IgnoreSpecial Retrieves orphaned user profiles from the specified folder and ignores special or default profiles. .NOTES This function filters orphaned profiles based on the results of the Invoke-UserProfileAudit function. #> function Get-OrphanedProfiles { [CmdletBinding()] [OutputType([UserProfile[]])] param ( [string]$ComputerName = $env:COMPUTERNAME, [string]$ProfileFolderPath = $env:WinProfileOps_ProfileFolderPath, [switch]$IgnoreSpecial ) # Call Invoke-UserProfileAudit to get all profiles $allProfiles = Invoke-UserProfileAudit -ComputerName $ComputerName -ProfileFolderPath $ProfileFolderPath -IgnoreSpecial:$IgnoreSpecial # Filter to return only orphaned profiles $orphanedProfiles = $allProfiles | Where-Object { $_.IsOrphaned } # Handle the case where no orphaned profiles are found if (-not $orphanedProfiles) { Write-Verbose "No orphaned profiles found on computer '$ComputerName'." return @() # Return an empty array } return $orphanedProfiles } #EndRegion './Public/Get-OrphanedProfiles.ps1' 55 #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 profile folders stored on disk, which may or may not match entries in the registry. The function checks if the specified computer is online before attempting to retrieve the profile folders. If the computer is unreachable, it logs a warning and returns an empty array. .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". .OUTPUTS PSCustomObject[] Returns an array of custom objects representing the user profile folders found. Each object contains: - FolderName: The name of the user profile folder. - ProfilePath: The full path to the user profile folder. - ComputerName: The name of the computer where the profile folder was retrieved from. .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 an array of objects representing the user profile folders found in the specified directory on the specified computer. It logs a warning if the target computer is unreachable and returns an empty array in case of errors during the retrieval process. #> function Get-UserProfilesFromFolders { [OutputType([PSCustomObject[]])] [CmdletBinding()] param ( [string]$ComputerName = $env:COMPUTERNAME, [string]$ProfileFolderPath = $env:WinProfileOps_ProfileFolderPath ) try { # Test if the computer is online before proceeding if (-not (Test-ComputerPing -ComputerName $ComputerName)) { Write-Warning "Computer '$ComputerName' is offline or unreachable." return @() # Return an empty array } # Get user folders and return them $UserFolders = Get-UserFolders -ComputerName $ComputerName -ProfileFolderPath $ProfileFolderPath -ErrorAction Stop return $UserFolders } catch { Write-Error "Error retrieving user folders from '$ProfileFolderPath' on computer '$ComputerName'. Error: $_" return @() # Return an empty array in case of failure } } #EndRegion './Public/Get-UserProfilesFromFolders.ps1' 60 #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 security identifier (SID) and the profile path. The function checks if the computer is reachable before proceeding with the operation and handles errors gracefully if there are issues accessing the registry. .PARAMETER ComputerName The name of the computer from which to retrieve user profiles. Defaults to the local computer. .OUTPUTS PSCustomObject[] Returns an array of custom objects representing the user profiles found in the registry. Each object contains the following properties: - SID: [string] The security identifier (SID) of the user profile. - ProfilePath: [string] The path to the user profile. - ComputerName: [string] The name of the computer where the profile was retrieved. .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 - The function first checks if the target computer is reachable. If the computer is unreachable, a warning is logged, and the function returns an empty array. - If an error occurs while accessing the registry, an error is logged, and the function returns an empty array. #> function Get-UserProfilesFromRegistry { [OutputType([PSCustomObject[]])] [CmdletBinding()] param ( [string] $ComputerName = $env:COMPUTERNAME ) try { # Test if the computer is online before proceeding if (-not (Test-ComputerPing -ComputerName $ComputerName)) { Write-Warning "Computer '$ComputerName' is offline or unreachable." return @() # Return an empty array } # Get registry profiles and return them $RegistryProfiles = Get-SIDProfileInfo -ComputerName $ComputerName -ErrorAction Stop return $RegistryProfiles } catch { Write-Error "Error accessing registry profiles on computer '$ComputerName'. Error: $_" return @() # Return an empty array in case of failure } } #EndRegion './Public/Get-UserProfilesFromRegistry.ps1' 56 #Region './Public/Invoke-UserProfileAudit.ps1' -1 <# .SYNOPSIS Audits user profiles on a specified computer by comparing profiles found in the registry and file system. .DESCRIPTION The Invoke-UserProfileAudit function retrieves user profile information from both the file system (user folders) and the registry on a specified computer. It compares these profiles to identify orphaned profiles, profiles that exist in one location but not the other, and optionally ignores special or default profiles. This function is useful for auditing user profiles and detecting inconsistencies across the registry and file system. .PARAMETER ComputerName The name of the computer from which to audit user 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 accounts or service accounts) during the audit process. .OUTPUTS [UserProfile[]] Returns an array of UserProfile objects that represent the profiles found during the audit. Each UserProfile object contains the following properties: - SID: [string] The security identifier of the user profile. - ProfilePath: [string] The file system path of the user profile. - IsOrphaned: [bool] Whether the profile is considered orphaned. - OrphanReason: [string] The reason for orphaned status if applicable. - ComputerName: [string] The name of the computer where the audit was performed. - IsSpecial: [bool] Whether the profile is considered special or a system account. .EXAMPLE Invoke-UserProfileAudit -ComputerName "Server01" Audits all user profiles from both the file system and the registry on "Server01". .EXAMPLE Invoke-UserProfileAudit -ProfileFolderPath "D:\UserProfiles" -IgnoreSpecial Audits user profiles from the "D:\UserProfiles" folder on the local computer, ignoring special or default profiles. .EXAMPLE Get-AllUserProfiles -ComputerName "Server01" This alias performs the same audit as Invoke-UserProfileAudit, returning all user profiles for "Server01". .NOTES This function performs a profile audit by comparing user profiles in the file system and registry. It supports pipeline input for multiple computer names and includes an alias `Get-AllUserProfiles`. #> function Invoke-UserProfileAudit { [OutputType([UserProfile[]])] [CmdletBinding()] [Alias("Get-AllUserProfiles")] param ( [Parameter(Mandatory = $false, ValueFromPipeline = $true)] [string]$ComputerName = $env:COMPUTERNAME, [string]$ProfileFolderPath = $env:WinProfileOps_ProfileFolderPath, [switch]$IgnoreSpecial ) begin { $AllProfiles = @() if ($null -eq $ComputerName) { $ComputerName = $env:COMPUTERNAME } } process { if (-not (Test-ComputerReachability -ComputerName $ComputerName)) { return } try { $UserFolders = Get-UserProfilesFromFolders -ComputerName $ComputerName -ProfileFolderPath $ProfileFolderPath $RegistryProfiles = Get-UserProfilesFromRegistry -ComputerName $ComputerName $AllProfiles += Process-RegistryProfiles -RegistryProfiles $RegistryProfiles -ComputerName $ComputerName -IgnoreSpecial:$IgnoreSpecial $AllProfiles += Process-FolderProfiles -UserFolders $UserFolders -RegistryProfiles $RegistryProfiles -ComputerName $ComputerName -IgnoreSpecial:$IgnoreSpecial } catch { Write-Error "Error processing profiles for computer '$ComputerName'. Error: $_" } } end { $AllProfiles } } #EndRegion './Public/Invoke-UserProfileAudit.ps1' 95 #Region './Public/Remove-UserProfilesFromRegistry.ps1' -1 <# .SYNOPSIS Removes user profiles from the Windows registry based on SIDs, Usernames, or UserProfile objects. .DESCRIPTION The Remove-UserProfilesFromRegistry function allows you to remove user profiles from the Windows registry. It supports three parameter sets: UserProfileSet, SIDSet, and UserNameSet. The function can be used in audit-only mode, where no actual removal is performed, or in deletion mode where profiles are removed. If AuditOnly is specified, the function will simply output the profiles to be removed without actually performing any deletions. The function can prompt for confirmation before deletion if required, or use the Force switch to bypass confirmation. .PARAMETER UserProfiles An array of UserProfile objects to remove from the registry. This parameter is mandatory in the "UserProfileSet" parameter set. UserProfiles should include the necessary information such as SID, ProfilePath, and ComputerName. .PARAMETER SIDs An array of SIDs of user profiles to remove from the registry. This parameter is mandatory in the "SIDSet" parameter set. .PARAMETER Usernames An array of usernames to resolve into SIDs and remove from the registry. This parameter is mandatory in the "UserNameSet" parameter set. .PARAMETER ComputerName Specifies the computer name from which the user profiles should be removed. If not provided, it defaults to the local computer. .PARAMETER AuditOnly When specified, the function only audits the user profiles and does not perform actual deletion. It will output information about the profiles that would have been removed. .PARAMETER Force Forces the removal of the user profiles without prompting for confirmation. .Outputs ProfileDeletionResult objects that contain information about the deletion results. .EXAMPLE Remove-UserProfilesFromRegistry -SIDs "S-1-5-21-1234567890-1", "S-1-5-21-1234567890-2" Removes user profiles associated with the provided SIDs from the registry of the local computer. .EXAMPLE Remove-UserProfilesFromRegistry -Usernames "john.doe", "jane.smith" -ComputerName "SERVER01" -Force Removes the profiles associated with the specified usernames on the "SERVER01" machine without prompting for confirmation. .EXAMPLE Remove-UserProfilesFromRegistry -UserProfiles $userProfileList -AuditOnly Audits the profiles in the $userProfileList and outputs what would have been removed without performing actual deletions. .NOTES Requires administrative privileges to remove profiles from the registry. .LINK Get-Help about_Registry Get-Help about_Profiles #> function Remove-UserProfilesFromRegistry { [outputType([ProfileDeletionResult])] [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'High')] param ( [Parameter(Mandatory = $true, ValueFromPipeline = $true, ParameterSetName = "UserProfileSet")] [UserProfile[]]$UserProfiles, [Parameter(Mandatory = $true, ValueFromPipeline = $true, ParameterSetName = "SIDSet")] [string[]]$SIDs, [Parameter(Mandatory = $true, ValueFromPipeline = $true, ParameterSetName = "UserNameSet")] [string[]]$Usernames, [string]$ComputerName = $env:COMPUTERNAME, [switch]$AuditOnly, [switch]$Force # Default confirm behavior to true ) Begin { # Retrieve necessary environment variables $RegistryPath = Test-EnvironmentVariable -Name 'WinProfileOps_RegistryPath' $ProfileFolderPath = Test-EnvironmentVariable -Name 'WinProfileOps_ProfileFolderPath' $RegistryHive = Test-EnvironmentVariable -Name 'WinProfileOps_RegistryHive' # Resolve SIDs if Usernames are provided if ($PSCmdlet.ParameterSetName -eq 'UserNameSet') { $SIDs = Resolve-UsernamesToSIDs -Usernames $Usernames # If no SIDs were resolved, return early if (-not $SIDs) { Write-Error "No SIDs could be resolved for the provided usernames." return } } # Group UserProfiles by computer name if using UserProfileSet if ($PSCmdlet.ParameterSetName -eq 'UserProfileSet') { $profilesByComputer = $UserProfiles | Group-Object -Property ComputerName } # Handle confirmation: default behavior should be prompting unless explicitly set to false $Confirm = if ($PSCmdlet.MyInvocation.BoundParameters.ContainsKey('Confirm')) { $PSCmdlet.MyInvocation.BoundParameters['Confirm'] } else { $true # Default to true, always prompt unless explicitly overridden } } Process { # Process UserProfileSet - prompt per computer if ($PSCmdlet.ParameterSetName -eq 'UserProfileSet') { foreach ($profileGroup in $profilesByComputer) { $thisComputerName = $profileGroup.Name $SIDs = $profileGroup.Group.GetEnumerator().SID $profileCount = $profileGroup.Count try { # Call the confirmation prompt and skip this group if the user does not confirm if (-not (PromptForConfirmation -ComputerName $thisComputerName -ItemCount $profileCount -AuditOnly:$AuditOnly -Context $PSCmdlet -confirm:$Confirm)) { Write-Verbose "User chose not to continue for $thisComputerName, skipping." continue } # Process the profiles for this computer $SIDs | Invoke-UserProfileRegRemoval -ComputerName $thisComputerName ` -RegistryPath $RegistryPath -ProfileFolderPath $ProfileFolderPath ` -RegistryHive $RegistryHive -Force:$Force -AuditOnly:$AuditOnly -Confirm:$Confirm } catch { # Handle any errors that occur during processing of this computer Write-Error "Failed to process $thisComputerName. Error: $_.Exception.Message" continue # Move to the next computer in the loop } } } # Process SIDSet and UserNameSet - prompt once for the given computer name if ($PSCmdlet.ParameterSetName -eq 'SIDSet' -or $PSCmdlet.ParameterSetName -eq 'UserNameSet') { $itemCount = $SIDs.Count # Call the confirmation prompt and stop if the user does not confirm if (-not (PromptForConfirmation -ComputerName $ComputerName -ItemCount $itemCount -AuditOnly:$AuditOnly -Context $PSCmdlet -confirm:$Confirm)) { Write-Verbose "User chose not to continue for $thisComputerName, skipping." return } # Process the SIDs for this computer name $SIDs | Invoke-UserProfileRegRemoval -ComputerName $ComputerName ` -RegistryPath $RegistryPath -ProfileFolderPath $ProfileFolderPath ` -RegistryHive $RegistryHive -Force:$Force -AuditOnly:$AuditOnly -Confirm:$Confirm } } End { # No need to manually return results; PowerShell will output naturally } } #EndRegion './Public/Remove-UserProfilesFromRegistry.ps1' 177 |