mirror of
https://github.com/Raphire/Win11Debloat.git
synced 2026-08-23 08:02:07 +00:00
Add comprehensive test suite, fix minor issues, rename function and file names to match approved verbs (#708)
This commit is contained in:
@@ -1,5 +1,8 @@
|
||||
# Add parameter to script and write to file
|
||||
function AddParameter {
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Adds or updates a value in the active parameter collection.
|
||||
#>
|
||||
function Add-Parameter {
|
||||
param (
|
||||
$parameterName,
|
||||
$value = $true
|
||||
@@ -39,25 +39,6 @@ function Convert-RegOperationToValueKind {
|
||||
}
|
||||
}
|
||||
|
||||
function Remove-RegistrySubKeyTreeIfExists {
|
||||
param(
|
||||
[Parameter(Mandatory)]
|
||||
[Microsoft.Win32.RegistryKey]$RootKey,
|
||||
[Parameter(Mandatory)]
|
||||
[string]$SubKeyPath
|
||||
)
|
||||
|
||||
try {
|
||||
$RootKey.DeleteSubKeyTree($SubKeyPath, $false)
|
||||
}
|
||||
catch [System.UnauthorizedAccessException], [System.Security.SecurityException] {
|
||||
throw
|
||||
}
|
||||
catch {
|
||||
# Best-effort cleanup only; missing keys are fine.
|
||||
}
|
||||
}
|
||||
|
||||
function Get-RegistryKeyForOperation {
|
||||
param(
|
||||
[Parameter(Mandatory)]
|
||||
+5
-4
@@ -1,7 +1,8 @@
|
||||
# Shows confirmation dialogs for apps that require extra caution before removal.
|
||||
# Returns $true if the user confirmed all warnings (or if no warnings were triggered),
|
||||
# $false if the user declined any warning.
|
||||
function ConfirmUnsafeAppRemoval {
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Confirms removal of applications that require an extra safety warning.
|
||||
#>
|
||||
function Confirm-UnsafeAppRemoval {
|
||||
param (
|
||||
[string[]]$SelectedApps,
|
||||
$Owner = $null
|
||||
@@ -1,5 +1,8 @@
|
||||
# Generates a list of apps to remove based on the Apps parameter
|
||||
function GenerateAppsList {
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Builds the validated application-removal list from the Apps parameter.
|
||||
#>
|
||||
function Generate-AppsList {
|
||||
if (-not ($script:Params["Apps"] -and $script:Params["Apps"] -is [string])) {
|
||||
return @()
|
||||
}
|
||||
@@ -8,12 +11,12 @@ function GenerateAppsList {
|
||||
|
||||
switch ($appMode) {
|
||||
'default' {
|
||||
$appsList = LoadAppsFromFile $script:AppsListFilePath
|
||||
$appsList = Import-AppsFromFile $script:AppsListFilePath
|
||||
return $appsList
|
||||
}
|
||||
default {
|
||||
$appsList = $script:Params["Apps"].Split(',') | ForEach-Object { $_.Trim() }
|
||||
$validatedAppsList = ValidateAppslist $appsList
|
||||
$validatedAppsList = Get-ValidatedAppList $appsList
|
||||
return $validatedAppsList
|
||||
}
|
||||
}
|
||||
+6
-2
@@ -1,4 +1,8 @@
|
||||
function GetFriendlyRegistryBackupTarget {
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Converts a registry-backup target identifier into a user-friendly label.
|
||||
#>
|
||||
function Get-FriendlyRegistryBackupTarget {
|
||||
param(
|
||||
[AllowNull()]
|
||||
[AllowEmptyString()]
|
||||
@@ -40,4 +44,4 @@ function GetFriendlyRegistryBackupTarget {
|
||||
}
|
||||
|
||||
return $Target
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Returns a readable description of the current app-removal target.
|
||||
#>
|
||||
function Get-FriendlyTargetUserName {
|
||||
$target = Get-TargetUserForAppRemoval
|
||||
|
||||
switch ($target) {
|
||||
"AllUsers" { return "all users" }
|
||||
"CurrentUser" { return "the current user" }
|
||||
default { return "user $target" }
|
||||
}
|
||||
}
|
||||
@@ -74,7 +74,10 @@ function Get-RegFileOperations {
|
||||
}
|
||||
|
||||
$parsedValue = Convert-RegValueData -valueData $matches.valueData.Trim()
|
||||
if (-not $parsedValue) { continue }
|
||||
if (-not $parsedValue) {
|
||||
Write-Warning "Skipping unsupported or malformed registry value '$valueName' in '$currentKeyPath'."
|
||||
continue
|
||||
}
|
||||
|
||||
$operations += [PSCustomObject]@{
|
||||
OperationType = $parsedValue.OperationType
|
||||
@@ -88,6 +91,10 @@ function Get-RegFileOperations {
|
||||
return $operations
|
||||
}
|
||||
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Converts a .reg value literal into an operation type, registry value type, and data.
|
||||
#>
|
||||
function Convert-RegValueData {
|
||||
param(
|
||||
[Parameter(Mandatory)]
|
||||
@@ -121,8 +128,13 @@ function Convert-RegValueData {
|
||||
}
|
||||
|
||||
if ($valueData -match '^hex(?:\((?<kind>[0-9a-fA-F]+)\))?:(?<bytes>[0-9a-fA-F,\s]+)$') {
|
||||
$bytes = Convert-HexStringToByteArray -hexValue $matches.bytes
|
||||
$parsedBytes = Convert-HexStringToByteArray -hexValue $matches.bytes
|
||||
if ($null -eq $parsedBytes) {
|
||||
return $null
|
||||
}
|
||||
$bytes = [byte[]]@($parsedBytes)
|
||||
$valueType = if ($matches.kind) { "Hex$($matches.kind)" } else { 'Binary' }
|
||||
|
||||
$value = switch ($matches.kind) {
|
||||
'2' { Convert-RegistryByteArrayToString -byteData $bytes }
|
||||
'7' { Convert-RegistryByteArrayToMultiString -byteData $bytes }
|
||||
@@ -150,16 +162,29 @@ function Convert-RegValueData {
|
||||
return $null
|
||||
}
|
||||
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Converts a comma-separated hexadecimal byte string into a byte array.
|
||||
#>
|
||||
function Convert-HexStringToByteArray {
|
||||
param(
|
||||
[Parameter(Mandatory)]
|
||||
[string]$hexValue
|
||||
)
|
||||
|
||||
$parts = $hexValue.Split(',') | ForEach-Object { $_.Trim() } | Where-Object { $_ }
|
||||
return [System.Linq.Enumerable]::Select($parts, [Func[object, byte]] {
|
||||
param($h) [System.Convert]::ToByte($h, 16)
|
||||
}) -as [byte[]]
|
||||
$parts = @($hexValue.Split(',') | ForEach-Object { $_.Trim() })
|
||||
if ($parts | Where-Object { [string]::IsNullOrWhiteSpace($_) }) {
|
||||
return $null
|
||||
}
|
||||
|
||||
$bytes = New-Object byte[] $parts.Count
|
||||
for ($i = 0; $i -lt $parts.Count; $i++) {
|
||||
if ($parts[$i] -notmatch '^[0-9a-fA-F]{1,2}$') {
|
||||
return $null
|
||||
}
|
||||
$bytes[$i] = [System.Convert]::ToByte($parts[$i], 16)
|
||||
}
|
||||
return ,$bytes
|
||||
}
|
||||
|
||||
function Convert-RegistryByteArrayToString {
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
# Target is determined from $script:Params["AppRemovalTarget"] or defaults to "AllUsers"
|
||||
# Target values: "AllUsers" (removes for all users + from image), "CurrentUser", or a specific username
|
||||
function GetTargetUserForAppRemoval {
|
||||
function Get-TargetUserForAppRemoval {
|
||||
if ($script:Params.ContainsKey("AppRemovalTarget")) {
|
||||
return $script:Params["AppRemovalTarget"]
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
# Returns the directory path of the specified user, exits script if user path can't be found
|
||||
function GetUserDirectory {
|
||||
function Get-UserDirectory {
|
||||
param (
|
||||
$userName,
|
||||
$fileName = "",
|
||||
@@ -29,7 +29,7 @@ function GetUserDirectory {
|
||||
}
|
||||
}
|
||||
|
||||
$userContext = ResolveUserProfileContext -UserName $userName
|
||||
$userContext = Resolve-UserProfileContext -UserName $userName
|
||||
$resolvedUserDirectory = if ($userContext) { $userContext.ProfilePath } else { $null }
|
||||
if ($resolvedUserDirectory) {
|
||||
$userPath = if ([string]::IsNullOrWhiteSpace($fileName)) {
|
||||
@@ -46,9 +46,9 @@ function GetUserDirectory {
|
||||
}
|
||||
catch {
|
||||
Write-Error "Something went wrong when trying to find the user directory path for user $userName. Please ensure the user exists on this system"
|
||||
AwaitKeyToExit
|
||||
Wait-ForKeyPress
|
||||
}
|
||||
|
||||
Write-Error "Unable to find user directory path for user $userName"
|
||||
AwaitKeyToExit
|
||||
Wait-ForKeyPress
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Returns the explicitly targeted user name or the current process user name.
|
||||
#>
|
||||
function Get-UserName {
|
||||
if ($script:Params.ContainsKey("User")) {
|
||||
return $script:Params.Item("User")
|
||||
}
|
||||
|
||||
return $env:USERNAME
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
function GetFriendlyTargetUserName {
|
||||
$target = GetTargetUserForAppRemoval
|
||||
|
||||
switch ($target) {
|
||||
"AllUsers" { return "all users" }
|
||||
"CurrentUser" { return "the current user" }
|
||||
default { return "user $target" }
|
||||
}
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
function GetUserName {
|
||||
if ($script:Params.ContainsKey("User")) {
|
||||
return $script:Params.Item("User")
|
||||
}
|
||||
|
||||
return $env:USERNAME
|
||||
}
|
||||
+16
-12
@@ -1,4 +1,8 @@
|
||||
function ImportConfigToParams {
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Imports valid application, tweak, and deployment selections from a configuration JSON file into active parameters.
|
||||
#>
|
||||
function Import-ConfigToParams {
|
||||
param (
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]$ConfigPath,
|
||||
@@ -22,7 +26,7 @@ function ImportConfigToParams {
|
||||
throw "Provided config file must be a .json file: $resolvedConfigPath"
|
||||
}
|
||||
|
||||
$configJson = LoadJsonFile -filePath $resolvedConfigPath -expectedVersion $ExpectedVersion
|
||||
$configJson = Import-JsonFile -filePath $resolvedConfigPath -expectedVersion $ExpectedVersion
|
||||
if ($null -eq $configJson) {
|
||||
throw "Failed to read config file: $resolvedConfigPath"
|
||||
}
|
||||
@@ -38,8 +42,8 @@ function ImportConfigToParams {
|
||||
)
|
||||
|
||||
if ($appIds.Count -gt 0) {
|
||||
AddParameter 'RemoveApps'
|
||||
AddParameter 'Apps' ($appIds -join ',')
|
||||
Add-Parameter 'RemoveApps'
|
||||
Add-Parameter 'Apps' ($appIds -join ',')
|
||||
$importedItems++
|
||||
}
|
||||
}
|
||||
@@ -59,7 +63,7 @@ function ImportConfigToParams {
|
||||
continue
|
||||
}
|
||||
|
||||
AddParameter $setting.Name $true
|
||||
Add-Parameter $setting.Name $true
|
||||
$importedItems++
|
||||
}
|
||||
}
|
||||
@@ -73,12 +77,12 @@ function ImportConfigToParams {
|
||||
}
|
||||
|
||||
if ($deploymentLookup.ContainsKey('CreateRestorePoint') -and [bool]$deploymentLookup['CreateRestorePoint']) {
|
||||
AddParameter 'CreateRestorePoint'
|
||||
Add-Parameter 'CreateRestorePoint'
|
||||
$importedItems++
|
||||
}
|
||||
|
||||
if ($deploymentLookup.ContainsKey('RestartExplorer') -and -not [bool]$deploymentLookup['RestartExplorer']) {
|
||||
AddParameter 'NoRestartExplorer'
|
||||
Add-Parameter 'NoRestartExplorer'
|
||||
$importedItems++
|
||||
}
|
||||
|
||||
@@ -87,12 +91,12 @@ function ImportConfigToParams {
|
||||
1 {
|
||||
$otherUserName = if ($deploymentLookup.ContainsKey('OtherUsername')) { "$($deploymentLookup['OtherUsername'])".Trim() } else { '' }
|
||||
if (-not [string]::IsNullOrWhiteSpace($otherUserName)) {
|
||||
AddParameter 'User' $otherUserName
|
||||
Add-Parameter 'User' $otherUserName
|
||||
$importedItems++
|
||||
}
|
||||
}
|
||||
2 {
|
||||
AddParameter 'Sysprep'
|
||||
Add-Parameter 'Sysprep'
|
||||
$importedItems++
|
||||
}
|
||||
}
|
||||
@@ -101,17 +105,17 @@ function ImportConfigToParams {
|
||||
if ($deploymentLookup.ContainsKey('AppRemovalScopeIndex') -and $script:Params.ContainsKey('RemoveApps')) {
|
||||
switch ([int]$deploymentLookup['AppRemovalScopeIndex']) {
|
||||
0 {
|
||||
AddParameter 'AppRemovalTarget' 'AllUsers'
|
||||
Add-Parameter 'AppRemovalTarget' 'AllUsers'
|
||||
$importedItems++
|
||||
}
|
||||
1 {
|
||||
AddParameter 'AppRemovalTarget' 'CurrentUser'
|
||||
Add-Parameter 'AppRemovalTarget' 'CurrentUser'
|
||||
$importedItems++
|
||||
}
|
||||
2 {
|
||||
$targetUser = if ($deploymentLookup.ContainsKey('OtherUsername')) { "$($deploymentLookup['OtherUsername'])".Trim() } else { '' }
|
||||
if (-not [string]::IsNullOrWhiteSpace($targetUser)) {
|
||||
AddParameter 'AppRemovalTarget' $targetUser
|
||||
Add-Parameter 'AppRemovalTarget' $targetUser
|
||||
$importedItems++
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,7 @@
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Normalizes a rooted registry path and returns its hive and subkey components.
|
||||
#>
|
||||
function Split-RegistryPath {
|
||||
param(
|
||||
[Parameter(Mandatory)]
|
||||
@@ -51,6 +55,10 @@ function Split-RegistryPath {
|
||||
}
|
||||
}
|
||||
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Returns the .NET registry root key for a supported registry hive name.
|
||||
#>
|
||||
function Get-RegistryRootKey {
|
||||
param(
|
||||
[Parameter(Mandatory)]
|
||||
@@ -67,6 +75,39 @@ function Get-RegistryRootKey {
|
||||
}
|
||||
}
|
||||
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Deletes a registry subkey tree and ignores a key that has already disappeared.
|
||||
#>
|
||||
function Remove-RegistrySubKeyTreeIfExists {
|
||||
param(
|
||||
[Parameter(Mandatory)]
|
||||
$RootKey,
|
||||
[Parameter(Mandatory)]
|
||||
[string]$SubKeyPath
|
||||
)
|
||||
|
||||
try {
|
||||
$RootKey.DeleteSubKeyTree($SubKeyPath, $false)
|
||||
}
|
||||
catch {
|
||||
$failure = $_.Exception
|
||||
while ($failure.InnerException) {
|
||||
$failure = $failure.InnerException
|
||||
}
|
||||
if ($failure -is [System.ArgumentException]) {
|
||||
# The key can disappear between snapshot inspection and deletion.
|
||||
return
|
||||
}
|
||||
|
||||
throw
|
||||
}
|
||||
}
|
||||
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Returns a feature's registry-file path, using the Sysprep layout when targeting another profile.
|
||||
#>
|
||||
function Get-RegistryFilePathForFeature {
|
||||
param(
|
||||
[Parameter(Mandatory)]
|
||||
+65
-65
@@ -12,7 +12,7 @@
|
||||
.OUTPUTS
|
||||
System.String
|
||||
#>
|
||||
function NormalizeUserLookupValue {
|
||||
function Normalize-UserLookupValue {
|
||||
param(
|
||||
[string]$Value
|
||||
)
|
||||
@@ -44,12 +44,12 @@ if (-not $script:ResolvedUserSidCache) {
|
||||
.OUTPUTS
|
||||
System.String
|
||||
#>
|
||||
function GetUserLookupCacheKey {
|
||||
function Get-UserLookupCacheKey {
|
||||
param(
|
||||
[string]$Value
|
||||
)
|
||||
|
||||
$normalizedValue = NormalizeUserLookupValue -Value $Value
|
||||
$normalizedValue = Normalize-UserLookupValue -Value $Value
|
||||
if ([string]::IsNullOrWhiteSpace($normalizedValue)) {
|
||||
return ''
|
||||
}
|
||||
@@ -72,13 +72,13 @@ function GetUserLookupCacheKey {
|
||||
.OUTPUTS
|
||||
System.String[]
|
||||
#>
|
||||
function GetNormalizedLookupCandidates {
|
||||
function Get-NormalizedLookupCandidates {
|
||||
param(
|
||||
[string[]]$Candidates
|
||||
)
|
||||
|
||||
$normalized = @($Candidates) |
|
||||
ForEach-Object { NormalizeUserLookupValue -Value $_ } |
|
||||
ForEach-Object { Normalize-UserLookupValue -Value $_ } |
|
||||
Where-Object { -not [string]::IsNullOrWhiteSpace($_) } |
|
||||
Select-Object -Unique
|
||||
|
||||
@@ -100,7 +100,7 @@ function GetNormalizedLookupCandidates {
|
||||
.OUTPUTS
|
||||
System.String
|
||||
#>
|
||||
function EscapeWqlString {
|
||||
function Escape-WqlString {
|
||||
param(
|
||||
[string]$Value
|
||||
)
|
||||
@@ -126,22 +126,22 @@ function EscapeWqlString {
|
||||
.OUTPUTS
|
||||
System.String
|
||||
#>
|
||||
function GetLocalUserNameSegment {
|
||||
function Get-LocalUserNameSegment {
|
||||
param(
|
||||
[string]$UserName
|
||||
)
|
||||
|
||||
$normalizedName = NormalizeUserLookupValue -Value $UserName
|
||||
$normalizedName = Normalize-UserLookupValue -Value $UserName
|
||||
if ([string]::IsNullOrWhiteSpace($normalizedName)) {
|
||||
return ''
|
||||
}
|
||||
|
||||
if ($normalizedName.Contains('\')) {
|
||||
return NormalizeUserLookupValue -Value (($normalizedName -split '\\')[-1])
|
||||
return Normalize-UserLookupValue -Value (($normalizedName -split '\\')[-1])
|
||||
}
|
||||
|
||||
if ($normalizedName.Contains('@')) {
|
||||
return NormalizeUserLookupValue -Value (($normalizedName -split '@')[0])
|
||||
return Normalize-UserLookupValue -Value (($normalizedName -split '@')[0])
|
||||
}
|
||||
|
||||
return $normalizedName
|
||||
@@ -165,12 +165,12 @@ function GetLocalUserNameSegment {
|
||||
.OUTPUTS
|
||||
System.String
|
||||
#>
|
||||
function ResolveNetBiosDomainName {
|
||||
function Resolve-NetBiosDomainName {
|
||||
param(
|
||||
[string]$RawDomain
|
||||
)
|
||||
|
||||
$trimmed = NormalizeUserLookupValue -Value $RawDomain
|
||||
$trimmed = Normalize-UserLookupValue -Value $RawDomain
|
||||
if ([string]::IsNullOrWhiteSpace($trimmed)) {
|
||||
return ''
|
||||
}
|
||||
@@ -194,7 +194,7 @@ function ResolveNetBiosDomainName {
|
||||
}
|
||||
|
||||
if ($ntDomainInstance -and -not [string]::IsNullOrWhiteSpace($ntDomainInstance.DomainName)) {
|
||||
$fromNtDomain = NormalizeUserLookupValue -Value $ntDomainInstance.DomainName
|
||||
$fromNtDomain = Normalize-UserLookupValue -Value $ntDomainInstance.DomainName
|
||||
if (-not [string]::IsNullOrWhiteSpace($fromNtDomain)) {
|
||||
return $fromNtDomain
|
||||
}
|
||||
@@ -205,7 +205,7 @@ function ResolveNetBiosDomainName {
|
||||
}
|
||||
|
||||
if ($trimmed.Contains('.')) {
|
||||
$leaf = NormalizeUserLookupValue -Value (($trimmed -split '\.')[0])
|
||||
$leaf = Normalize-UserLookupValue -Value (($trimmed -split '\.')[0])
|
||||
if (-not [string]::IsNullOrWhiteSpace($leaf)) {
|
||||
return $leaf
|
||||
}
|
||||
@@ -221,7 +221,7 @@ function ResolveNetBiosDomainName {
|
||||
.DESCRIPTION
|
||||
Cached in script scope for the process lifetime. Returns $false on
|
||||
error or workgroup. When joined, also caches the NetBIOS domain label
|
||||
(ResolveNetBiosDomainName) for use as a profile-folder suffix.
|
||||
(Resolve-NetBiosDomainName) for use as a profile-folder suffix.
|
||||
|
||||
.OUTPUTS
|
||||
System.Boolean
|
||||
@@ -239,7 +239,7 @@ function Test-MachineIsDomainJoined {
|
||||
$computerSystem = Get-CimInstance -ClassName Win32_ComputerSystem -ErrorAction Stop
|
||||
if ($null -ne $computerSystem -and $computerSystem.PartOfDomain) {
|
||||
$script:MachineIsDomainJoined = $true
|
||||
$script:MachineNetBiosDomain = ResolveNetBiosDomainName -RawDomain ([string]$computerSystem.Domain)
|
||||
$script:MachineNetBiosDomain = Resolve-NetBiosDomainName -RawDomain ([string]$computerSystem.Domain)
|
||||
}
|
||||
}
|
||||
catch {
|
||||
@@ -262,7 +262,7 @@ function Test-MachineIsDomainJoined {
|
||||
.OUTPUTS
|
||||
System.String
|
||||
#>
|
||||
function GetProfileFolderDomainSuffix {
|
||||
function Get-ProfileFolderDomainSuffix {
|
||||
if (-not (Test-MachineIsDomainJoined)) {
|
||||
return ''
|
||||
}
|
||||
@@ -301,12 +301,12 @@ function GetProfileFolderDomainSuffix {
|
||||
.OUTPUTS
|
||||
System.String[]
|
||||
#>
|
||||
function GetUserNameMatchCandidates {
|
||||
function Get-UserNameMatchCandidates {
|
||||
param(
|
||||
[string]$Value
|
||||
)
|
||||
|
||||
$normalized = NormalizeUserLookupValue -Value $Value
|
||||
$normalized = Normalize-UserLookupValue -Value $Value
|
||||
if ([string]::IsNullOrWhiteSpace($normalized)) {
|
||||
return @()
|
||||
}
|
||||
@@ -314,13 +314,13 @@ function GetUserNameMatchCandidates {
|
||||
$candidates = New-Object 'System.Collections.Generic.List[string]'
|
||||
[void]$candidates.Add($normalized)
|
||||
|
||||
$localSegment = GetLocalUserNameSegment -UserName $normalized
|
||||
$localSegment = Get-LocalUserNameSegment -UserName $normalized
|
||||
if (-not [string]::IsNullOrWhiteSpace($localSegment) -and ($localSegment -ine $normalized)) {
|
||||
[void]$candidates.Add($localSegment)
|
||||
}
|
||||
|
||||
# Domain-suffixed forms only apply where Windows writes them.
|
||||
$domainSuffix = GetProfileFolderDomainSuffix
|
||||
$domainSuffix = Get-ProfileFolderDomainSuffix
|
||||
if (-not [string]::IsNullOrWhiteSpace($domainSuffix)) {
|
||||
# Prefer the local segment as the stem so DOMAIN\user still yields
|
||||
# user.CONTOSO rather than the fully qualified string.
|
||||
@@ -340,7 +340,7 @@ function GetUserNameMatchCandidates {
|
||||
# (registry, backup metadata), so add that form too.
|
||||
$suffixWithDot = ".$domainSuffix"
|
||||
if ($normalized.Length -gt $suffixWithDot.Length -and $normalized.EndsWith($suffixWithDot, [System.StringComparison]::OrdinalIgnoreCase)) {
|
||||
$bareStem = NormalizeUserLookupValue -Value ($normalized.Substring(0, $normalized.Length - $suffixWithDot.Length))
|
||||
$bareStem = Normalize-UserLookupValue -Value ($normalized.Substring(0, $normalized.Length - $suffixWithDot.Length))
|
||||
if (-not [string]::IsNullOrWhiteSpace($bareStem)) {
|
||||
$alreadyPresent = $false
|
||||
foreach ($existing in $candidates) {
|
||||
@@ -361,7 +361,7 @@ function GetUserNameMatchCandidates {
|
||||
Test whether a user name and a profile folder leaf share an account.
|
||||
|
||||
.DESCRIPTION
|
||||
Compares candidate sets (via GetUserNameMatchCandidates) instead of
|
||||
Compares candidate sets (via Get-UserNameMatchCandidates) instead of
|
||||
raw strings, so different forms of the same account still match.
|
||||
|
||||
.PARAMETER UserName
|
||||
@@ -383,8 +383,8 @@ function Test-UserNameMatchesProfileLeaf {
|
||||
return $false
|
||||
}
|
||||
|
||||
$leafCandidates = @(GetUserNameMatchCandidates -Value $ProfileLeaf)
|
||||
$userCandidates = @(GetUserNameMatchCandidates -Value $UserName)
|
||||
$leafCandidates = @(Get-UserNameMatchCandidates -Value $ProfileLeaf)
|
||||
$userCandidates = @(Get-UserNameMatchCandidates -Value $UserName)
|
||||
|
||||
foreach ($leaf in $leafCandidates) {
|
||||
foreach ($user in $userCandidates) {
|
||||
@@ -430,13 +430,13 @@ function Test-UserNameMatch {
|
||||
|
||||
# Workgroup: strict equality (no suffix disambiguation available).
|
||||
if (-not (Test-MachineIsDomainJoined)) {
|
||||
$normalizedA = NormalizeUserLookupValue -Value $UserNameA
|
||||
$normalizedB = NormalizeUserLookupValue -Value $UserNameB
|
||||
$normalizedA = Normalize-UserLookupValue -Value $UserNameA
|
||||
$normalizedB = Normalize-UserLookupValue -Value $UserNameB
|
||||
return ($normalizedA -ieq $normalizedB)
|
||||
}
|
||||
|
||||
$candidatesA = @(GetUserNameMatchCandidates -Value $UserNameA)
|
||||
$candidatesB = @(GetUserNameMatchCandidates -Value $UserNameB)
|
||||
$candidatesA = @(Get-UserNameMatchCandidates -Value $UserNameA)
|
||||
$candidatesB = @(Get-UserNameMatchCandidates -Value $UserNameB)
|
||||
|
||||
foreach ($a in $candidatesA) {
|
||||
foreach ($b in $candidatesB) {
|
||||
@@ -463,7 +463,7 @@ function Test-UserNameMatch {
|
||||
.PARAMETER Sid
|
||||
Resolved SID to cache.
|
||||
#>
|
||||
function SetResolvedUserSidCache {
|
||||
function Set-ResolvedUserSidCache {
|
||||
param(
|
||||
[string[]]$Candidates,
|
||||
[string]$Sid
|
||||
@@ -474,7 +474,7 @@ function SetResolvedUserSidCache {
|
||||
}
|
||||
|
||||
foreach ($candidate in @($Candidates)) {
|
||||
$cacheKey = GetUserLookupCacheKey -Value $candidate
|
||||
$cacheKey = Get-UserLookupCacheKey -Value $candidate
|
||||
if ($cacheKey) {
|
||||
$script:ResolvedUserSidCache[$cacheKey] = $Sid
|
||||
}
|
||||
@@ -494,13 +494,13 @@ function SetResolvedUserSidCache {
|
||||
.OUTPUTS
|
||||
System.String
|
||||
#>
|
||||
function GetCachedResolvedUserSid {
|
||||
function Get-CachedResolvedUserSid {
|
||||
param(
|
||||
[string[]]$Candidates
|
||||
)
|
||||
|
||||
foreach ($candidate in @($Candidates)) {
|
||||
$cacheKey = GetUserLookupCacheKey -Value $candidate
|
||||
$cacheKey = Get-UserLookupCacheKey -Value $candidate
|
||||
if ($cacheKey -and $script:ResolvedUserSidCache.ContainsKey($cacheKey)) {
|
||||
return $script:ResolvedUserSidCache[$cacheKey]
|
||||
}
|
||||
@@ -523,7 +523,7 @@ function GetCachedResolvedUserSid {
|
||||
.OUTPUTS
|
||||
System.String
|
||||
#>
|
||||
function TryResolveSidByNtAccount {
|
||||
function Try-ResolveSidByNtAccount {
|
||||
param(
|
||||
[string]$UserName
|
||||
)
|
||||
@@ -560,12 +560,12 @@ function TryResolveSidByNtAccount {
|
||||
.OUTPUTS
|
||||
System.String
|
||||
#>
|
||||
function TryResolveSidByLocalLookup {
|
||||
function Try-ResolveSidByLocalLookup {
|
||||
param(
|
||||
[string[]]$Candidates
|
||||
)
|
||||
|
||||
$lookupCandidates = GetNormalizedLookupCandidates -Candidates $Candidates
|
||||
$lookupCandidates = Get-NormalizedLookupCandidates -Candidates $Candidates
|
||||
if ($lookupCandidates.Count -eq 0) {
|
||||
return $null
|
||||
}
|
||||
@@ -586,8 +586,8 @@ function TryResolveSidByLocalLookup {
|
||||
|
||||
foreach ($candidate in $lookupCandidates) {
|
||||
try {
|
||||
$escapedCandidate = EscapeWqlString -Value $candidate
|
||||
$escapedComputerName = EscapeWqlString -Value $env:COMPUTERNAME
|
||||
$escapedCandidate = Escape-WqlString -Value $candidate
|
||||
$escapedComputerName = Escape-WqlString -Value $env:COMPUTERNAME
|
||||
$filter = "LocalAccount=True AND (Name='$escapedCandidate' OR FullName='$escapedCandidate' OR Caption='$escapedComputerName\$escapedCandidate')"
|
||||
$matchingAccount = Get-CimInstance -ClassName Win32_UserAccount -Filter $filter -ErrorAction Stop | Select-Object -First 1
|
||||
|
||||
@@ -617,12 +617,12 @@ function TryResolveSidByLocalLookup {
|
||||
.OUTPUTS
|
||||
System.String
|
||||
#>
|
||||
function TryResolveSidFromProfileList {
|
||||
function Try-ResolveSidFromProfileList {
|
||||
param(
|
||||
[string[]]$Candidates
|
||||
)
|
||||
|
||||
$lookupCandidates = GetNormalizedLookupCandidates -Candidates $Candidates
|
||||
$lookupCandidates = Get-NormalizedLookupCandidates -Candidates $Candidates
|
||||
if ($lookupCandidates.Count -eq 0) {
|
||||
return $null
|
||||
}
|
||||
@@ -635,7 +635,7 @@ function TryResolveSidFromProfileList {
|
||||
if ([string]::IsNullOrWhiteSpace($imagePath)) { continue }
|
||||
|
||||
$expandedPath = [System.Environment]::ExpandEnvironmentVariables($imagePath)
|
||||
$leafName = NormalizeUserLookupValue -Value (Split-Path -Leaf $expandedPath)
|
||||
$leafName = Normalize-UserLookupValue -Value (Split-Path -Leaf $expandedPath)
|
||||
|
||||
foreach ($candidate in $lookupCandidates) {
|
||||
if (Test-MachineIsDomainJoined) {
|
||||
@@ -680,7 +680,7 @@ function TryResolveSidFromProfileList {
|
||||
.OUTPUTS
|
||||
System.Management.Automation.PSCustomObject
|
||||
#>
|
||||
function NewResolvedUserContext {
|
||||
function New-ResolvedUserContext {
|
||||
param(
|
||||
[string]$UserName,
|
||||
[string]$UserSid,
|
||||
@@ -708,12 +708,12 @@ function NewResolvedUserContext {
|
||||
.OUTPUTS
|
||||
System.String
|
||||
#>
|
||||
function GetQualifiedProcessIdentityName {
|
||||
function Get-QualifiedProcessIdentityName {
|
||||
param(
|
||||
[string]$Candidate
|
||||
)
|
||||
|
||||
$normalizedCandidate = NormalizeUserLookupValue -Value $Candidate
|
||||
$normalizedCandidate = Normalize-UserLookupValue -Value $Candidate
|
||||
if ([string]::IsNullOrWhiteSpace($normalizedCandidate)) {
|
||||
return $null
|
||||
}
|
||||
@@ -735,7 +735,7 @@ function GetQualifiedProcessIdentityName {
|
||||
return $null
|
||||
}
|
||||
|
||||
$currentLocalSegment = GetLocalUserNameSegment -UserName $currentName
|
||||
$currentLocalSegment = Get-LocalUserNameSegment -UserName $currentName
|
||||
if (-not [string]::IsNullOrWhiteSpace($currentLocalSegment) -and $currentLocalSegment -ieq $normalizedCandidate) {
|
||||
return $currentName
|
||||
}
|
||||
@@ -762,19 +762,19 @@ function GetQualifiedProcessIdentityName {
|
||||
.OUTPUTS
|
||||
System.String
|
||||
#>
|
||||
function ResolveUserSid {
|
||||
function Resolve-UserSid {
|
||||
param(
|
||||
[Parameter(Mandatory)]
|
||||
[string]$UserName
|
||||
)
|
||||
|
||||
$candidateUserName = NormalizeUserLookupValue -Value $UserName
|
||||
$candidateUserName = Normalize-UserLookupValue -Value $UserName
|
||||
if ([string]::IsNullOrWhiteSpace($candidateUserName)) {
|
||||
return $null
|
||||
}
|
||||
|
||||
$hasQualifiedIdentity = $candidateUserName.Contains('\') -or $candidateUserName.Contains('@')
|
||||
$localNameSegment = GetLocalUserNameSegment -UserName $candidateUserName
|
||||
$localNameSegment = Get-LocalUserNameSegment -UserName $candidateUserName
|
||||
$leafNameCandidates = @()
|
||||
if ($hasQualifiedIdentity -and -not [string]::IsNullOrWhiteSpace($localNameSegment) -and $localNameSegment -ine $candidateUserName) {
|
||||
$leafNameCandidates = @($localNameSegment)
|
||||
@@ -796,7 +796,7 @@ function ResolveUserSid {
|
||||
@($candidateUserName)
|
||||
}
|
||||
|
||||
$cachedSid = GetCachedResolvedUserSid -Candidates $lookupCandidates
|
||||
$cachedSid = Get-CachedResolvedUserSid -Candidates $lookupCandidates
|
||||
if ($cachedSid) {
|
||||
return $cachedSid
|
||||
}
|
||||
@@ -811,12 +811,12 @@ function ResolveUserSid {
|
||||
}
|
||||
elseif (Test-MachineIsDomainJoined) {
|
||||
# Prefer process identity (authoritative), then USERDOMAIN\input.
|
||||
$processQualifiedName = GetQualifiedProcessIdentityName -Candidate $candidateUserName
|
||||
$processQualifiedName = Get-QualifiedProcessIdentityName -Candidate $candidateUserName
|
||||
if (-not [string]::IsNullOrWhiteSpace($processQualifiedName)) {
|
||||
[void]$qualifiedNamesToTry.Add($processQualifiedName)
|
||||
}
|
||||
|
||||
$domainSuffix = GetProfileFolderDomainSuffix
|
||||
$domainSuffix = Get-ProfileFolderDomainSuffix
|
||||
if (-not [string]::IsNullOrWhiteSpace($domainSuffix)) {
|
||||
$domainQualifiedName = "$domainSuffix\$candidateUserName"
|
||||
if (-not ($qualifiedNamesToTry -contains $domainQualifiedName)) {
|
||||
@@ -831,10 +831,10 @@ function ResolveUserSid {
|
||||
|
||||
# Step 2: resolve qualified form(s) via NTAccount.Translate.
|
||||
foreach ($qualifiedName in $qualifiedNamesToTry) {
|
||||
$resolvedSid = TryResolveSidByNtAccount -UserName $qualifiedName
|
||||
$resolvedSid = Try-ResolveSidByNtAccount -UserName $qualifiedName
|
||||
if ($resolvedSid) {
|
||||
$allCacheKeys = @($candidateUserName) + $qualifiedNamesToTry | Select-Object -Unique
|
||||
SetResolvedUserSidCache -Candidates $allCacheKeys -Sid $resolvedSid
|
||||
Set-ResolvedUserSidCache -Candidates $allCacheKeys -Sid $resolvedSid
|
||||
return $resolvedSid
|
||||
}
|
||||
}
|
||||
@@ -842,20 +842,20 @@ function ResolveUserSid {
|
||||
# Step 3: local SAM fallback (workgroup only; skipped on domain to avoid
|
||||
# nameshare shadowing).
|
||||
if (-not (Test-MachineIsDomainJoined)) {
|
||||
$resolvedSid = TryResolveSidByLocalLookup -Candidates $lookupCandidates
|
||||
$resolvedSid = Try-ResolveSidByLocalLookup -Candidates $lookupCandidates
|
||||
if ($resolvedSid) {
|
||||
$allCacheKeys = @($candidateUserName) + $qualifiedNamesToTry | Select-Object -Unique
|
||||
SetResolvedUserSidCache -Candidates $allCacheKeys -Sid $resolvedSid
|
||||
Set-ResolvedUserSidCache -Candidates $allCacheKeys -Sid $resolvedSid
|
||||
return $resolvedSid
|
||||
}
|
||||
}
|
||||
|
||||
# Step 4: ProfileList leaf heuristic (last resort; disambiguates by
|
||||
# on-disk folder name, suffix-aware on domain boxes).
|
||||
$resolvedSid = TryResolveSidFromProfileList -Candidates $profileHeuristicCandidates
|
||||
$resolvedSid = Try-ResolveSidFromProfileList -Candidates $profileHeuristicCandidates
|
||||
if ($resolvedSid) {
|
||||
$allCacheKeys = @($candidateUserName) + $qualifiedNamesToTry | Select-Object -Unique
|
||||
SetResolvedUserSidCache -Candidates $allCacheKeys -Sid $resolvedSid
|
||||
Set-ResolvedUserSidCache -Candidates $allCacheKeys -Sid $resolvedSid
|
||||
return $resolvedSid
|
||||
}
|
||||
|
||||
@@ -878,13 +878,13 @@ function ResolveUserSid {
|
||||
.OUTPUTS
|
||||
System.Management.Automation.PSCustomObject
|
||||
#>
|
||||
function ResolveUserProfileContext {
|
||||
function Resolve-UserProfileContext {
|
||||
param(
|
||||
[Parameter(Mandatory)]
|
||||
[string]$UserName
|
||||
)
|
||||
|
||||
$candidateUserName = NormalizeUserLookupValue -Value $UserName
|
||||
$candidateUserName = Normalize-UserLookupValue -Value $UserName
|
||||
if ([string]::IsNullOrWhiteSpace($candidateUserName)) {
|
||||
return $null
|
||||
}
|
||||
@@ -902,14 +902,14 @@ function ResolveUserProfileContext {
|
||||
|
||||
$defaultProfilePath = Join-Path $rootPath 'Default'
|
||||
if (Test-Path -LiteralPath $defaultProfilePath -PathType Container) {
|
||||
return (NewResolvedUserContext -UserName $candidateUserName -UserSid $null -ProfilePath $defaultProfilePath)
|
||||
return (New-ResolvedUserContext -UserName $candidateUserName -UserSid $null -ProfilePath $defaultProfilePath)
|
||||
}
|
||||
}
|
||||
|
||||
return $null
|
||||
}
|
||||
|
||||
$userSid = ResolveUserSid -UserName $candidateUserName
|
||||
$userSid = Resolve-UserSid -UserName $candidateUserName
|
||||
|
||||
if ($userSid) {
|
||||
$sidRegistryPath = "Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList\$userSid"
|
||||
@@ -919,7 +919,7 @@ function ResolveUserProfileContext {
|
||||
if (-not [string]::IsNullOrWhiteSpace($registryImagePath)) {
|
||||
$expandedPath = [System.Environment]::ExpandEnvironmentVariables($registryImagePath)
|
||||
if (Test-Path -LiteralPath $expandedPath -PathType Container) {
|
||||
return (NewResolvedUserContext -UserName $candidateUserName -UserSid $userSid -ProfilePath $expandedPath)
|
||||
return (New-ResolvedUserContext -UserName $candidateUserName -UserSid $userSid -ProfilePath $expandedPath)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -932,7 +932,7 @@ function ResolveUserProfileContext {
|
||||
$matchingProfiles = @(Get-CimInstance -ClassName Win32_UserProfile -Filter "SID='$userSid'" -ErrorAction Stop)
|
||||
$resolvedProfile = $matchingProfiles | Where-Object { -not [string]::IsNullOrWhiteSpace($_.LocalPath) } | Select-Object -First 1
|
||||
if ($resolvedProfile -and (Test-Path -LiteralPath $resolvedProfile.LocalPath -PathType Container)) {
|
||||
return (NewResolvedUserContext -UserName $candidateUserName -UserSid $userSid -ProfilePath $resolvedProfile.LocalPath)
|
||||
return (New-ResolvedUserContext -UserName $candidateUserName -UserSid $userSid -ProfilePath $resolvedProfile.LocalPath)
|
||||
}
|
||||
}
|
||||
catch {
|
||||
@@ -948,7 +948,7 @@ function ResolveUserProfileContext {
|
||||
# Exact leaf match first (common case; avoids an unnecessary scan).
|
||||
$candidateUserPath = Join-Path $rootPath $candidateUserName
|
||||
if (Test-Path -LiteralPath $candidateUserPath -PathType Container) {
|
||||
return (NewResolvedUserContext -UserName $candidateUserName -UserSid $userSid -ProfilePath $candidateUserPath)
|
||||
return (New-ResolvedUserContext -UserName $candidateUserName -UserSid $userSid -ProfilePath $candidateUserPath)
|
||||
}
|
||||
|
||||
# Only domain-joined boxes write suffixed folders; scanning workgroup
|
||||
@@ -957,7 +957,7 @@ function ResolveUserProfileContext {
|
||||
try {
|
||||
foreach ($child in @(Get-ChildItem -LiteralPath $rootPath -Directory -ErrorAction SilentlyContinue)) {
|
||||
if (Test-UserNameMatchesProfileLeaf -UserName $candidateUserName -ProfileLeaf $child.Name) {
|
||||
return (NewResolvedUserContext -UserName $candidateUserName -UserSid $userSid -ProfilePath $child.FullName)
|
||||
return (New-ResolvedUserContext -UserName $candidateUserName -UserSid $userSid -ProfilePath $child.FullName)
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
# Check if this machine supports S0 Modern Standby power state. Returns true if S0 Modern Standby is supported, false otherwise.
|
||||
function CheckModernStandbySupport {
|
||||
function Test-ModernStandbySupport {
|
||||
$count = 0
|
||||
|
||||
try {
|
||||
@@ -23,7 +23,7 @@ function Test-TargetUserName {
|
||||
}
|
||||
}
|
||||
|
||||
if (-not (CheckIfUserExists -userName $normalizedUserName)) {
|
||||
if (-not (Test-UserProfileExists -userName $normalizedUserName)) {
|
||||
return [PSCustomObject]@{
|
||||
IsValid = $false
|
||||
UserName = $normalizedUserName
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
function CheckIfUserExists {
|
||||
function Test-UserProfileExists {
|
||||
param (
|
||||
[string]$userName
|
||||
)
|
||||
@@ -10,7 +10,7 @@ function CheckIfUserExists {
|
||||
$lookupName = $userName.Trim()
|
||||
|
||||
# Validate special characters against the local username segment (user in DOMAIN\user or user@domain).
|
||||
$localUserName = GetLocalUserNameSegment -UserName $lookupName
|
||||
$localUserName = Get-LocalUserNameSegment -UserName $lookupName
|
||||
|
||||
if ($localUserName.IndexOfAny([System.IO.Path]::GetInvalidFileNameChars()) -ge 0) {
|
||||
return $false
|
||||
@@ -22,7 +22,7 @@ function CheckIfUserExists {
|
||||
}
|
||||
|
||||
try {
|
||||
$userContext = ResolveUserProfileContext -UserName $lookupName
|
||||
$userContext = Resolve-UserProfileContext -UserName $lookupName
|
||||
if (-not $userContext -or [string]::IsNullOrWhiteSpace($userContext.ProfilePath)) {
|
||||
return $false
|
||||
}
|
||||
@@ -31,12 +31,12 @@ function Resolve-TargetUserHiveContext {
|
||||
[string]$TargetUserName
|
||||
)
|
||||
|
||||
$normalizedTargetUserName = NormalizeUserLookupValue -Value $TargetUserName
|
||||
$normalizedTargetUserName = Normalize-UserLookupValue -Value $TargetUserName
|
||||
if ([string]::IsNullOrWhiteSpace($normalizedTargetUserName)) {
|
||||
throw 'Target user name for registry hive resolution is empty.'
|
||||
}
|
||||
|
||||
$userContext = ResolveUserProfileContext -UserName $normalizedTargetUserName
|
||||
$userContext = Resolve-UserProfileContext -UserName $normalizedTargetUserName
|
||||
if (-not $userContext -or [string]::IsNullOrWhiteSpace([string]$userContext.ProfilePath)) {
|
||||
throw "Unable to resolve profile path for target user '$normalizedTargetUserName'."
|
||||
}
|
||||
Reference in New Issue
Block a user