Public/Start-Psexec.ps1
function Start-Psexec { <# .SYNOPSIS Starts an interactive PsExec session with automatic installation and setup. .DESCRIPTION Provides a simplified interface for starting PsExec sessions with automatic download and installation if PsExec is not found. Optimized for local SYSTEM context testing of Intune packages and applications. Defaults to PowerShell in SYSTEM context for most common System Administrator use cases. .PARAMETER Command Specifies the shell to launch: - powershell: Interactive PowerShell session (default) - cmd: Interactive Command Prompt session .PARAMETER SessionType Specifies the type of session to start: - System: Interactive session as NT AUTHORITY\SYSTEM (default) - Interactive: Interactive session as current user - UserContext: Interactive session with specified credentials - NetworkService: Interactive session as NetworkService account .PARAMETER ComputerName Target computer name for remote sessions. Optional - defaults to local computer. .PARAMETER Credential PSCredential object for UserContext sessions or remote computer authentication. .PARAMETER WorkingDirectory Starting directory for the session. Defaults to C:\Windows\Temp for SYSTEM context. .PARAMETER Force Forces re-download of PsExec even if already available. .PARAMETER Remove Removes all PsExec installations from the system. .EXAMPLE Start-Psexec Starts interactive PowerShell as SYSTEM on local computer (most common use case). .EXAMPLE Start-Psexec cmd Starts interactive Command Prompt as SYSTEM on local computer. .EXAMPLE Start-Psexec -SessionType Interactive Starts interactive PowerShell session as current user. .EXAMPLE Start-Psexec -WorkingDirectory "C:\Temp\MyPackage" Starts SYSTEM PowerShell session in specific directory for package testing. .EXAMPLE Start-Psexec -ComputerName "Server01" -Credential $cred Starts session on remote server with credentials. .EXAMPLE Start-Psexec -Remove Removes all PsExec installations from the system. .NOTES Author: AutomateSilent Version: 1.0.0 Compatible: PowerShell 5.1 and later Purpose: Simplifies PsExec usage for System Administrators Requires: Administrative privileges for SYSTEM context operations #> [CmdletBinding(DefaultParameterSetName = 'Local')] param( [Parameter(Position = 0, ParameterSetName = 'Local')] [Parameter(Position = 0, ParameterSetName = 'Remote')] [Parameter(Position = 0, ParameterSetName = 'UserContext')] [ValidateSet('powershell', 'cmd')] [string]$Command = 'powershell', [Parameter(Position = 1, ParameterSetName = 'Local')] [Parameter(Position = 1, ParameterSetName = 'Remote')] [Parameter(Position = 1, ParameterSetName = 'UserContext')] [ValidateSet('System', 'Interactive', 'UserContext', 'NetworkService')] [string]$SessionType = 'System', [Parameter(ParameterSetName = 'Remote')] [ValidateNotNullOrEmpty()] [string]$ComputerName, [Parameter(ParameterSetName = 'UserContext', Mandatory = $true)] [Parameter(ParameterSetName = 'Remote')] [System.Management.Automation.PSCredential]$Credential, [Parameter(ParameterSetName = 'Local')] [Parameter(ParameterSetName = 'Remote')] [Parameter(ParameterSetName = 'UserContext')] [ValidateNotNullOrEmpty()] [string]$WorkingDirectory, [Parameter(ParameterSetName = 'Local')] [Parameter(ParameterSetName = 'Remote')] [Parameter(ParameterSetName = 'UserContext')] [switch]$Force, [Parameter(ParameterSetName = 'Cleanup', Mandatory = $true)] [switch]$Remove ) begin { function Write-DeploymentLog { <# .SYNOPSIS Writes formatted log entries for deployment operations with timestamp and severity. .DESCRIPTION Provides standardized logging functionality for deployment scripts with consistent formatting, timestamp inclusion, and severity level indication. Supports both file logging and console output with appropriate color-coding based on message severity. .PARAMETER Message The message text to be logged. .PARAMETER Level The severity level of the log message. Valid values are: - Info: Standard informational messages (default) - Warning: Warning messages that require attention - Error: Error messages indicating failures .PARAMETER NoConsole If specified, suppresses output to the console. Messages will only be written to the log file. .EXAMPLE Write-DeploymentLog "Starting application installation" Logs an informational message with timestamp. .EXAMPLE Write-DeploymentLog "Configuration file not found" -Level Warning Logs a warning message with yellow console output. #> [CmdletBinding()] param( [Parameter(Mandatory = $true)] [string]$Message, [ValidateSet('Info', 'Warning', 'Error')] [string]$Level = 'Info', [switch]$NoConsole ) $timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss" $logMessage = "[$timestamp] [$Level] $Message" if (-not $NoConsole) { switch ($Level) { 'Info' { Write-Host $logMessage -ForegroundColor Cyan } 'Warning' { Write-Warning $logMessage } 'Error' { Write-Error $logMessage } } } } function Find-PsExecExecutable { [CmdletBinding()] param( [switch]$Force, [switch]$Remove ) $searchPaths = @( (Get-Command "psexec.exe" -ErrorAction SilentlyContinue).Source, "$env:SystemRoot\System32\psexec.exe", "$env:SystemRoot\psexec.exe", "${env:ProgramFiles}\PSTools\psexec.exe", "${env:ProgramFiles(x86)}\PSTools\psexec.exe", "$env:TEMP\PSTools\psexec.exe", "C:\Tools\psexec.exe" ) if ($Remove) { Write-DeploymentLog "Starting PsExec cleanup process" -Level Info $removedCount = 0 foreach ($path in $searchPaths) { if ($path -and (Test-Path -Path $path -PathType Leaf)) { try { Write-DeploymentLog "Removing PsExec installation : $path" -Level Info Remove-Item -Path $path -Force -ErrorAction Stop $removedCount++ } catch { Write-DeploymentLog "Failed to remove $path : $($_.Exception.Message)" -Level Warning } } } if ($removedCount -gt 0) { Write-DeploymentLog "PsExec cleanup completed : $removedCount installation(s) removed" -Level Info } else { Write-DeploymentLog "No PsExec installations found to remove" -Level Info } return $removedCount } if ($Force) { Write-DeploymentLog "Force parameter specified - bypassing existing PsExec search" -Level Info return $null } foreach ($path in $searchPaths) { if ($path -and (Test-Path -Path $path -PathType Leaf)) { Write-DeploymentLog "Located existing PsExec installation : $path" -Level Info return $path } } Write-Verbose "No existing PsExec installation found in standard locations" return $null } function Install-PsExecTool { [CmdletBinding()] param() try { Write-DeploymentLog "PsExec not found - initiating automatic download and installation" -Level Info # Define paths and URLs $downloadUrl = "https://download.sysinternals.com/files/PSTools.zip" $tempDir = "$env:TEMP\PSTools_Setup_$((Get-Date).Ticks)" $zipFile = "$tempDir\PSTools.zip" $extractDir = "$tempDir\Extract" $installDir = "$env:SystemRoot\System32" # Create temporary directory New-Item -Path $tempDir -ItemType Directory -Force | Out-Null Write-DeploymentLog "Created temporary staging directory : $tempDir" -Level Info # Download PSTools.zip from Microsoft Sysinternals Write-DeploymentLog "Downloading PSTools archive from Microsoft Sysinternals" -Level Info $webClient = New-Object System.Net.WebClient $webClient.DownloadFile($downloadUrl, $zipFile) if (-not (Test-Path $zipFile)) { throw "Download operation failed - PSTools.zip file not created" } $fileSize = (Get-Item $zipFile).Length / 1MB Write-DeploymentLog "Download completed successfully : $('{0:N2}' -f $fileSize) MB" -Level Info # Extract archive contents Write-DeploymentLog "Extracting PSTools archive contents" -Level Info Add-Type -AssemblyName System.IO.Compression.FileSystem [System.IO.Compression.ZipFile]::ExtractToDirectory($zipFile, $extractDir) # Locate and validate PsExec executable $psexecFiles = Get-ChildItem -Path $extractDir -Name "psexec*.exe" -Recurse if (-not $psexecFiles) { throw "PsExec executable not found in downloaded archive contents" } # Select appropriate architecture version $psexecSource = $psexecFiles | Select-Object -First 1 $sourcePath = Join-Path $extractDir $psexecSource $destinationPath = Join-Path $installDir "psexec.exe" Write-DeploymentLog "Installing PsExec to system directory : $installDir" -Level Info Copy-Item -Path $sourcePath -Destination $destinationPath -Force # Verify successful installation if (Test-Path $destinationPath) { Write-DeploymentLog "PsExec installation completed successfully : $destinationPath" -Level Info } else { throw "Installation verification failed - PsExec not found in target directory" } # Cleanup temporary files and directories Write-DeploymentLog "Removing temporary files and cleaning up staging area" -Level Info Remove-Item -Path $tempDir -Recurse -Force -ErrorAction SilentlyContinue return $destinationPath } catch { Write-DeploymentLog "PsExec installation failed : $($_.Exception.Message)" -Level Error # Cleanup temporary files on failure if (Test-Path $tempDir) { Remove-Item -Path $tempDir -Recurse -Force -ErrorAction SilentlyContinue } throw } } function Get-PsExecExecutablePath { [CmdletBinding()] param( [switch]$Force ) # Attempt to locate existing installation $existingPath = Find-PsExecExecutable -Force:$Force if ($existingPath) { return $existingPath } # Install PsExec if not found Write-DeploymentLog "PsExec executable not available - initiating automatic installation" -Level Info return Install-PsExecTool } function Start-PsExecProcess { [CmdletBinding()] param( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string]$PsExecPath, [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string[]]$Arguments ) try { $argumentString = $Arguments -join ' ' Write-DeploymentLog "Launching PsExec process with arguments : $argumentString" -Level Info $startInfo = New-Object System.Diagnostics.ProcessStartInfo $startInfo.FileName = $PsExecPath $startInfo.Arguments = $argumentString $startInfo.UseShellExecute = $true $startInfo.WindowStyle = [System.Diagnostics.ProcessWindowStyle]::Normal $process = [System.Diagnostics.Process]::Start($startInfo) if ($process) { # Allow brief startup time for process initialization Start-Sleep -Milliseconds 750 if (-not $process.HasExited) { Write-DeploymentLog "PsExec process started successfully : Process ID $($process.Id)" -Level Info return $process } else { $exitCode = $process.ExitCode throw "PsExec process terminated unexpectedly with exit code : $exitCode" } } else { throw "Process creation failed - unable to start PsExec executable" } } catch { Write-DeploymentLog "PsExec process startup failed : $($_.Exception.Message)" -Level Error throw } } function Build-PsExecArgumentList { [CmdletBinding()] param( [Parameter(Mandatory = $true)] [ValidateSet('powershell', 'cmd')] [string]$Command, [Parameter(Mandatory = $true)] [ValidateSet('System', 'Interactive', 'UserContext', 'NetworkService')] [string]$SessionType, [string]$ComputerName, [System.Management.Automation.PSCredential]$Credential, [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string]$WorkingDirectory ) $arguments = @() # Accept EULA automatically to prevent interactive prompts $arguments += "-accepteula" # Computer specification for remote operations if ($ComputerName -and ($ComputerName -ne $env:COMPUTERNAME)) { $arguments += "\\$ComputerName" Write-DeploymentLog "Target system : $ComputerName (Remote)" -Level Info } else { Write-DeploymentLog "Target system : LOCAL" -Level Info } # Credential handling for authentication if ($Credential) { $arguments += "-u" $arguments += $Credential.UserName $arguments += "-p" $arguments += $Credential.GetNetworkCredential().Password Write-DeploymentLog "Authentication : $($Credential.UserName)" -Level Info } # Session-specific parameter configuration switch ($SessionType) { 'System' { $arguments += "-i", "-s" Write-DeploymentLog "Session context : NT AUTHORITY\SYSTEM" -Level Info } 'Interactive' { $arguments += "-i" Write-DeploymentLog "Session context : Current User Interactive" -Level Info } 'UserContext' { $arguments += "-i" Write-DeploymentLog "Session context : Specified User ($($Credential.UserName))" -Level Info } 'NetworkService' { $arguments += "-i", "-u", "NetworkService" Write-DeploymentLog "Session context : NT AUTHORITY\NetworkService" -Level Info } } # Working directory configuration $arguments += "-w" $arguments += "`"$WorkingDirectory`"" Write-DeploymentLog "Working directory : $WorkingDirectory" -Level Info # Command shell specification switch ($Command) { 'powershell' { $arguments += "powershell.exe" Write-DeploymentLog "Shell executable : Windows PowerShell" -Level Info } 'cmd' { $arguments += "cmd.exe" Write-DeploymentLog "Shell executable : Command Prompt" -Level Info } } return $arguments } } process { try { # Handle cleanup operation if Remove parameter is specified if ($PSCmdlet.ParameterSetName -eq 'Cleanup') { Write-DeploymentLog "PsExec cleanup operation initiated" -Level Info $removedCount = Find-PsExecExecutable -Remove return [PSCustomObject]@{ PSTypeName = 'PsExec.CleanupResult' Success = $true RemovedInstallations = $removedCount CompletionTime = Get-Date Message = if ($removedCount -gt 0) { "PsExec cleanup completed : $removedCount installation(s) removed" } else { "No PsExec installations found to remove" } } } # Standard session startup logic Write-DeploymentLog "Initializing PsExec session startup sequence" -Level Info # Set appropriate default working directory based on session context if (-not $WorkingDirectory) { $WorkingDirectory = if ($SessionType -eq 'System') { "C:\Windows\Temp" } else { $PWD.Path } } # Validate session type requirements if ($SessionType -eq 'UserContext' -and -not $Credential) { $errorMessage = "UserContext session type requires Credential parameter to be specified" Write-DeploymentLog $errorMessage -Level Error throw $errorMessage } # Validate working directory accessibility if (-not (Test-Path -Path $WorkingDirectory -PathType Container)) { $errorMessage = "Specified working directory does not exist or is not accessible : $WorkingDirectory" Write-DeploymentLog $errorMessage -Level Error throw $errorMessage } # Obtain PsExec executable path $psexecPath = Get-PsExecExecutablePath -Force:$Force # Build command line arguments $arguments = Build-PsExecArgumentList -Command $Command -SessionType $SessionType -ComputerName $ComputerName -Credential $Credential -WorkingDirectory $WorkingDirectory # Launch PsExec process $process = Start-PsExecProcess -PsExecPath $psexecPath -Arguments $arguments # Create session information object $sessionInfo = [PSCustomObject]@{ PSTypeName = 'PsExec.SessionInfo' ProcessId = $process.Id Command = $Command.ToUpper() SessionType = $SessionType ComputerName = if ($ComputerName) { $ComputerName } else { "LOCAL" } WorkingDirectory = $WorkingDirectory StartTime = Get-Date PsExecPath = $psexecPath UserContext = if ($Credential) { $Credential.UserName } else { "Default" } } Write-DeploymentLog "Session initialization completed - interactive shell ready in new window" -Level Info Write-Verbose "Session details : $($sessionInfo | Out-String)" return $sessionInfo } catch { $errorMessage = "PsExec session startup failed : $($_.Exception.Message)" Write-DeploymentLog $errorMessage -Level Error Write-Error $errorMessage -ErrorAction Stop } } end { Write-Verbose "Start-Psexec function execution completed" } } |