Public/Start-ADTProcessAsUser.ps1
#----------------------------------------------------------------------------- # # MARK: Start-ADTProcessAsUser # #----------------------------------------------------------------------------- function Start-ADTProcessAsUser { <# .SYNOPSIS Invokes a process in another user's session. .DESCRIPTION Invokes a process from SYSTEM in another user's session. .PARAMETER FilePath Path to the executable to invoke. .PARAMETER ArgumentList Arguments for the invoked executable. .PARAMETER WorkingDirectory The 'start-in' directory for the invoked executable. .PARAMETER HideWindow Specifies whether the window should be hidden or not. .PARAMETER ProcessCreationFlags One or more flags to control the process's invocation. .PARAMETER InheritEnvironmentVariables Specifies whether the process should inherit the user's environment state. .PARAMETER Wait Specifies whether to wait for the invoked excecutable to finish. .PARAMETER Username The username of the user's session to invoke the executable in. .PARAMETER SessionId The session ID of the user to invoke the executable in. .PARAMETER AllActiveUserSessions Specifies that the executable should be invoked in all active sessions. .PARAMETER UseLinkedAdminToken Specifies that an admin token (if available) should be used for the invocation. .PARAMETER SuccessExitCodes Specifies one or more exit codes that the function uses to consider the invocation successful. .PARAMETER ConsoleTimeoutInSeconds Specifies the timeout in seconds to wait for a console application to finish its task. .PARAMETER IsGuiApplication Indicates that the executed application is a GUI-based app, not a console-based app. .PARAMETER NoRedirectOutput Specifies that stdout/stderr output should not be redirected to file. .PARAMETER MergeStdErrAndStdOut Specifies that the stdout/stderr streams should be merged into a single output. .PARAMETER OutputDirectory Specifies the output directory for the redirected stdout/stderr streams. .PARAMETER NoTerminateOnTimeout Specifies that the process shouldn't terminate on timeout. .PARAMETER AdditionalEnvironmentVariables Specifies additional environment variables to inject into the user's session. .PARAMETER WaitOption Specifies the wait type to use when waiting for an invoked executable to finish. .PARAMETER SecureArgumentList Hides all parameters passed to the executable from the Toolkit log file. .PARAMETER PassThru If NoWait is not specified, returns an object with ExitCode, STDOut and STDErr output from the process. If NoWait is specified, returns an object with Id, Handle and ProcessName. .EXAMPLE Start-ADTProcessAsUser -FilePath "$($adtSession.DirFiles)\setup.exe" -ArgumentList '/S' -SuccessExitCodes 0, 500 .INPUTS None You cannot pipe objects to this function. .OUTPUTS System.Threading.Tasks.Task[System.Int32] Returns a task object indicating the process's result. .NOTES An active ADT session is NOT required to use this function. Tags: psadt Website: https://psappdeploytoolkit.com Copyright: (C) 2024 PSAppDeployToolkit Team (Sean Lillis, Dan Cunningham, Muhammad Mashwani, Mitch Richters, Dan Gough). License: https://opensource.org/license/lgpl-3-0 .LINK https://psappdeploytoolkit.com #> [CmdletBinding(DefaultParameterSetName = 'PrimaryActiveUserSession')] [OutputType([System.Threading.Tasks.Task[System.Int32]])] param ( [Parameter(Mandatory = $true, ParameterSetName = 'Username')] [Parameter(Mandatory = $true, ParameterSetName = 'SessionId')] [Parameter(Mandatory = $true, ParameterSetName = 'AllActiveUserSessions')] [Parameter(Mandatory = $true, ParameterSetName = 'PrimaryActiveUserSession')] [ValidateNotNullOrEmpty()] [System.String]$FilePath, [Parameter(Mandatory = $false, ParameterSetName = 'Username')] [Parameter(Mandatory = $false, ParameterSetName = 'SessionId')] [Parameter(Mandatory = $false, ParameterSetName = 'AllActiveUserSessions')] [Parameter(Mandatory = $false, ParameterSetName = 'PrimaryActiveUserSession')] [ValidateNotNullOrEmpty()] [System.String[]]$ArgumentList, [Parameter(Mandatory = $false, ParameterSetName = 'Username')] [Parameter(Mandatory = $false, ParameterSetName = 'SessionId')] [Parameter(Mandatory = $false, ParameterSetName = 'AllActiveUserSessions')] [Parameter(Mandatory = $false, ParameterSetName = 'PrimaryActiveUserSession')] [ValidateNotNullOrEmpty()] [System.String]$WorkingDirectory, [Parameter(Mandatory = $false, ParameterSetName = 'Username')] [Parameter(Mandatory = $false, ParameterSetName = 'SessionId')] [Parameter(Mandatory = $false, ParameterSetName = 'AllActiveUserSessions')] [Parameter(Mandatory = $false, ParameterSetName = 'PrimaryActiveUserSession')] [System.Management.Automation.SwitchParameter]$HideWindow, [Parameter(Mandatory = $false, ParameterSetName = 'Username')] [Parameter(Mandatory = $false, ParameterSetName = 'SessionId')] [Parameter(Mandatory = $false, ParameterSetName = 'AllActiveUserSessions')] [Parameter(Mandatory = $false, ParameterSetName = 'PrimaryActiveUserSession')] [ValidateNotNullOrEmpty()] [PSADT.PInvoke.CREATE_PROCESS]$ProcessCreationFlags, [Parameter(Mandatory = $false, ParameterSetName = 'Username')] [Parameter(Mandatory = $false, ParameterSetName = 'SessionId')] [Parameter(Mandatory = $false, ParameterSetName = 'AllActiveUserSessions')] [Parameter(Mandatory = $false, ParameterSetName = 'PrimaryActiveUserSession')] [System.Management.Automation.SwitchParameter]$InheritEnvironmentVariables, [Parameter(Mandatory = $false, ParameterSetName = 'Username')] [Parameter(Mandatory = $false, ParameterSetName = 'SessionId')] [Parameter(Mandatory = $false, ParameterSetName = 'AllActiveUserSessions')] [Parameter(Mandatory = $false, ParameterSetName = 'PrimaryActiveUserSession')] [ValidateNotNullOrEmpty()] [System.Management.Automation.SwitchParameter]$Wait, [Parameter(Mandatory = $true, ParameterSetName = 'Username')] [ValidateNotNullOrEmpty()] [System.String]$Username, [Parameter(Mandatory = $true, ParameterSetName = 'SessionId')] [ValidateNotNullOrEmpty()] [System.UInt32]$SessionId, [Parameter(Mandatory = $true, ParameterSetName = 'AllActiveUserSessions')] [System.Management.Automation.SwitchParameter]$AllActiveUserSessions, [Parameter(Mandatory = $false, ParameterSetName = 'Username')] [Parameter(Mandatory = $false, ParameterSetName = 'SessionId')] [Parameter(Mandatory = $false, ParameterSetName = 'AllActiveUserSessions')] [Parameter(Mandatory = $false, ParameterSetName = 'PrimaryActiveUserSession')] [System.Management.Automation.SwitchParameter]$UseLinkedAdminToken, [Parameter(Mandatory = $false, ParameterSetName = 'Username')] [Parameter(Mandatory = $false, ParameterSetName = 'SessionId')] [Parameter(Mandatory = $false, ParameterSetName = 'AllActiveUserSessions')] [Parameter(Mandatory = $false, ParameterSetName = 'PrimaryActiveUserSession')] [ValidateNotNullOrEmpty()] [System.Int32[]]$SuccessExitCodes, [Parameter(Mandatory = $false, ParameterSetName = 'Username')] [Parameter(Mandatory = $false, ParameterSetName = 'SessionId')] [Parameter(Mandatory = $false, ParameterSetName = 'AllActiveUserSessions')] [Parameter(Mandatory = $false, ParameterSetName = 'PrimaryActiveUserSession')] [ValidateNotNullOrEmpty()] [System.UInt32]$ConsoleTimeoutInSeconds, [Parameter(Mandatory = $false, ParameterSetName = 'Username')] [Parameter(Mandatory = $false, ParameterSetName = 'SessionId')] [Parameter(Mandatory = $false, ParameterSetName = 'AllActiveUserSessions')] [Parameter(Mandatory = $false, ParameterSetName = 'PrimaryActiveUserSession')] [System.Management.Automation.SwitchParameter]$IsGuiApplication, [Parameter(Mandatory = $false, ParameterSetName = 'Username')] [Parameter(Mandatory = $false, ParameterSetName = 'SessionId')] [Parameter(Mandatory = $false, ParameterSetName = 'AllActiveUserSessions')] [Parameter(Mandatory = $false, ParameterSetName = 'PrimaryActiveUserSession')] [System.Management.Automation.SwitchParameter]$NoRedirectOutput, [Parameter(Mandatory = $false, ParameterSetName = 'Username')] [Parameter(Mandatory = $false, ParameterSetName = 'SessionId')] [Parameter(Mandatory = $false, ParameterSetName = 'AllActiveUserSessions')] [Parameter(Mandatory = $false, ParameterSetName = 'PrimaryActiveUserSession')] [System.Management.Automation.SwitchParameter]$MergeStdErrAndStdOut, [Parameter(Mandatory = $false, ParameterSetName = 'Username')] [Parameter(Mandatory = $false, ParameterSetName = 'SessionId')] [Parameter(Mandatory = $false, ParameterSetName = 'AllActiveUserSessions')] [Parameter(Mandatory = $false, ParameterSetName = 'PrimaryActiveUserSession')] [ValidateNotNullOrEmpty()] [System.String]$OutputDirectory, [Parameter(Mandatory = $false, ParameterSetName = 'Username')] [Parameter(Mandatory = $false, ParameterSetName = 'SessionId')] [Parameter(Mandatory = $false, ParameterSetName = 'AllActiveUserSessions')] [Parameter(Mandatory = $false, ParameterSetName = 'PrimaryActiveUserSession')] [System.Management.Automation.SwitchParameter]$NoTerminateOnTimeout, [Parameter(Mandatory = $false, ParameterSetName = 'Username')] [Parameter(Mandatory = $false, ParameterSetName = 'SessionId')] [Parameter(Mandatory = $false, ParameterSetName = 'AllActiveUserSessions')] [Parameter(Mandatory = $false, ParameterSetName = 'PrimaryActiveUserSession')] [ValidateNotNullOrEmpty()] [System.Collections.IDictionary]$AdditionalEnvironmentVariables, [Parameter(Mandatory = $false, ParameterSetName = 'Username')] [Parameter(Mandatory = $false, ParameterSetName = 'SessionId')] [Parameter(Mandatory = $false, ParameterSetName = 'AllActiveUserSessions')] [Parameter(Mandatory = $false, ParameterSetName = 'PrimaryActiveUserSession')] [ValidateNotNullOrEmpty()] [PSADT.ProcessEx.WaitType]$WaitOption, [Parameter(Mandatory = $false, ParameterSetName = 'Username')] [Parameter(Mandatory = $false, ParameterSetName = 'SessionId')] [Parameter(Mandatory = $false, ParameterSetName = 'AllActiveUserSessions')] [Parameter(Mandatory = $false, ParameterSetName = 'PrimaryActiveUserSession')] [System.Management.Automation.SwitchParameter]$SecureArgumentList, [Parameter(Mandatory = $false, ParameterSetName = 'Username')] [Parameter(Mandatory = $false, ParameterSetName = 'SessionId')] [Parameter(Mandatory = $false, ParameterSetName = 'AllActiveUserSessions')] [Parameter(Mandatory = $false, ParameterSetName = 'PrimaryActiveUserSession')] [System.Management.Automation.SwitchParameter]$PassThru ) begin { # Initialise function. Initialize-ADTFunction -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState # Strip out parameters not destined for the C# code. $null = ('SecureArgumentList', 'PassThru').ForEach({ if ($PSBoundParameters.ContainsKey($_)) { $PSBoundParameters.Remove($_) } }) # If we're on the default parameter set, pass the right parameter through. if ($PSCmdlet.ParameterSetName.Equals('PrimaryActiveUserSession')) { $PSBoundParameters.Add('PrimaryActiveUserSession', [System.Management.Automation.SwitchParameter]$true) } elseif ($PSBoundParameters.ContainsKey('Username')) { if (!($userSessionId = Get-ADTLoggedOnUser | & { process { if ($_ -and $_.NTAccount.EndsWith($Username, [System.StringComparison]::InvariantCultureIgnoreCase)) { return $_ } } } | Select-Object -First 1 -ExpandProperty SessionId)) { $PSCmdlet.ThrowTerminatingError((New-ADTValidateScriptErrorRecord -ParameterName Username -ProvidedValue $Username -ExceptionMessage 'An active session could not be found for the specified user.')) } $PSBoundParameters.Add('SessionId', ($SessionId = $userSessionId)) $null = $PSBoundParameters.Remove('Username') } # Translate the environment variables into a dictionary. Using this type on the parameter is too hard on the caller. if ($PSBoundParameters.ContainsKey('AdditionalEnvironmentVariables')) { $AdditionalEnvironmentVariables = [System.Collections.Generic.Dictionary[System.String, System.String]]::new() $PSBoundParameters.AdditionalEnvironmentVariables.GetEnumerator() | & { process { $AdditionalEnvironmentVariables.Add($_.Key, $_.Value) } } $PSBoundParameters.AdditionalEnvironmentVariables = $AdditionalEnvironmentVariables } # Translate switches that require negation for the LaunchOptions. $null = ('RedirectOutput', 'TerminateOnTimeout').Where({ $PSBoundParameters.ContainsKey("No$_") }).ForEach({ $PSBoundParameters.$_ = !$PSBoundParameters."No$_" $PSBoundParameters.Remove("No$_") }) # Unless explicitly provided, don't terminate on timeout. if (!$PSBoundParameters.ContainsKey('TerminateOnTimeout')) { $PSBoundParameters.TerminateOnTimeout = $false } # Translate the process flags into a list of flags. No idea why the backend is coded like this... if ($PSBoundParameters.ContainsKey('ProcessCreationFlags')) { $PSBoundParameters.ProcessCreationFlags = $PSBoundParameters.ProcessCreationFlags.ToString().Split(',').Trim() } } process { # Announce start. switch ($PSCmdlet.ParameterSetName) { Username { Write-ADTLogEntry -Message "Invoking [$FilePath$(if (!$SecureArgumentList) { " $ArgumentList" })] as user [$Username]$(if ($Wait) { ", and waiting for invocation to finish" })." break } SessionId { Write-ADTLogEntry -Message "Invoking [$FilePath$(if (!$SecureArgumentList) { " $ArgumentList" })] for session [$SessionId]$(if ($Wait) { ", and waiting for invocation to finish" })." break } AllActiveUserSessions { Write-ADTLogEntry -Message "Invoking [$FilePath$(if (!$SecureArgumentList) { " $ArgumentList" })] for all active user sessions$(if ($Wait) { ", and waiting for all invocations to finish" })." break } PrimaryActiveUserSession { Write-ADTLogEntry -Message "Invoking [$FilePath$(if (!$SecureArgumentList) { " $ArgumentList" })] for the primary user session$(if ($Wait) { ", and waiting for invocation to finish" })." break } } # Create a new process object and invoke an execution. try { try { if (($result = ($process = [PSADT.ProcessEx.StartProcess]::new()).ExecuteAndMonitorAsync($PSBoundParameters)) -and $PassThru) { return $result } } catch { # Re-writing the ErrorRecord with Write-Error ensures the correct PositionMessage is used. Write-Error -ErrorRecord $_ } } catch { # Process the caught error, log it and throw depending on the specified ErrorAction. Invoke-ADTFunctionErrorHandler -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState -ErrorRecord $_ } finally { # Dispose of the process object to ensure things are cleaned up properly. $process.Dispose() } } end { # Finalise function. Complete-ADTFunction -Cmdlet $PSCmdlet } } |