Public/Resolve-EnvkExecutablePath.ps1
|
#Requires -Version 5.1 <# .SYNOPSIS Resolves and validates an executable path across filesystem, PATH, and UWP types. .DESCRIPTION Auto-detects the path type from the string content and validates accordingly: - shell:AppsFolder\<AUMID> — UWP (delegated to Resolve-UwpPath via Shell.Application COM) - Contains slashes (/ or \) — Filesystem path; environment variables are expanded first - Plain name (no slashes) — PATH executable lookup via Get-Command Environment variable tokens (e.g. %LOCALAPPDATA%) are expanded using [System.Environment]::ExpandEnvironmentVariables before filesystem and PATH detection. The UWP branch is tested before expansion because shell:AppsFolder paths require no expansion and the colon/backslash would otherwise route to the filesystem branch. Returns a PSCustomObject with three properties: Valid — [bool] Whether the path was successfully resolved Reason — [string] Human-readable explanation of the result ResolvedPath — [string] The expanded/resolved path, or $null if not found .PARAMETER Path The path string to resolve. May be a filesystem path (with or without environment variable tokens), a PATH-available executable name, or a shell:AppsFolder AUMID. .OUTPUTS PSCustomObject with properties: Valid (bool), Reason (string), ResolvedPath (string). .EXAMPLE # Filesystem path with environment variable Resolve-EnvkExecutablePath -Path '%PROGRAMFILES%\Mozilla Firefox\firefox.exe' .EXAMPLE # PATH executable Resolve-EnvkExecutablePath -Path 'explorer.exe' .EXAMPLE # UWP AUMID Resolve-EnvkExecutablePath -Path 'shell:AppsFolder\MSTeams_8wekyb3d8bbwe!MSTeams' .NOTES Author: Aaron AlAnsari Created: 2026-02-25 Detection order (CRITICAL — ordering affects correctness): 1. UWP check BEFORE env-var expansion (shell:AppsFolder must not be expanded) 2. Env-var expansion BEFORE slash check (%LOCALAPPDATA%\app.exe has slashes after expansion) 3. Slash check BEFORE PATH lookup (filesystem paths always contain slashes) #> function Resolve-EnvkExecutablePath { [CmdletBinding()] [OutputType([PSCustomObject])] param ( [Parameter()] [AllowEmptyString()] [string]$Path = '' ) # Guard: null/empty input. if ([string]::IsNullOrWhiteSpace($Path)) { return [PSCustomObject]@{ Valid = $false Reason = 'Path is null or empty.' ResolvedPath = $null } } # Branch 1: UWP shell:AppsFolder AUMID — check BEFORE env-var expansion. if ($Path -like 'shell:AppsFolder\*') { $aumid = $Path.Substring('shell:AppsFolder\'.Length) Write-EnvkLog -Level 'DEBUG' -Message "Resolve-EnvkExecutablePath: detected UWP path — $aumid" return Resolve-UwpPath -Aumid $aumid -OriginalPath $Path } # Expand environment variables (e.g. %WINDIR%, %LOCALAPPDATA%) — must happen BEFORE # the slash check so that tokens like %LOCALAPPDATA%\app.exe route to the filesystem branch. $expanded = [System.Environment]::ExpandEnvironmentVariables($Path) # Branch 2: Filesystem path — contains slashes after expansion. if ($expanded -match '[/\\]') { Write-EnvkLog -Level 'DEBUG' -Message "Resolve-EnvkExecutablePath: detected filesystem path — $expanded" if (Test-Path -Path $expanded -PathType Leaf) { Write-EnvkLog -Level 'DEBUG' -Message "Resolve-EnvkExecutablePath: filesystem path valid — $expanded" return [PSCustomObject]@{ Valid = $true Reason = 'Filesystem path exists.' ResolvedPath = $expanded } } else { Write-EnvkLog -Level 'DEBUG' -Message "Resolve-EnvkExecutablePath: filesystem path not found — $expanded" return [PSCustomObject]@{ Valid = $false Reason = "Path not found: '$expanded'." ResolvedPath = $expanded } } } # Branch 3: PATH executable — plain name with no slashes. Write-EnvkLog -Level 'DEBUG' -Message "Resolve-EnvkExecutablePath: detected PATH executable — $expanded" $cmd = Get-Command $expanded -ErrorAction SilentlyContinue if ($null -ne $cmd) { Write-EnvkLog -Level 'DEBUG' -Message "Resolve-EnvkExecutablePath: PATH executable found — $($cmd.Source)" return [PSCustomObject]@{ Valid = $true Reason = 'Executable found in PATH.' ResolvedPath = $cmd.Source } } else { Write-EnvkLog -Level 'DEBUG' -Message "Resolve-EnvkExecutablePath: PATH executable not found — $expanded" return [PSCustomObject]@{ Valid = $false Reason = "Executable '$expanded' not found in PATH." ResolvedPath = $null } } } |