How to use Invoke-Command to pass logged in credentials to the remote execution

I want to use Invoke-Command to execute a script block on a remote computer, where the script block needs to access a network file share while executing on the server side. I have read articles that I need to include -Authentication Credssp to allow client side credentials to be passed to the server side, but I do not want to be prompted to enter client side credentials when I include the -Credential arg on the Invoke-Command. I would just like to make the Invoke-Command call passing the same credentials that the client side script is executing with. Web searches mention that I could save the user credentials in a file on the client side, and then read this file to create a PSCredentials object, but I do not want to have to remember to update this file when a password change occurs.

Is there a way for Invoke-Command to just implicitly pass the client side credentials to the remote script block?

Thanks.

That’s what it does if you don’t use the -Credential parameter at all; you authenticate as yourself.

When I leave off the -Credential arg, the Invoke-Command aborts with the error

“Missing an argument for Parameter ‘Credential’. Specify a parameter of type System.Management.Automation.PSCredential’ and try again.”

The command line I used was:

Invoke-Command -ComputerName $RemoteHost -ScriptBlock $RemoteScriptBlock -Authentication CredSSP

I need the -Authentication CredSSP arg since the remote script block needs to use the client side credentials on the remote computer to access a network file share. If I add the -Credential $Username arg, the command is successful, but the -Credential arg prompts me for a password. I just want Invoke-Command to use the credentials of the user that is executing the Invoke-Command, implicitly.

Looks like the double hop problem to me.Enabling Multihop Remoting - Scripting Blog

Yes, this is a double hop. I have followed all the steps needed for passing credentials to the remote computer, where the Invoke-Command is successful.

The important point of this post is that I do not want to be prompted for credentials when the client side logic executes the Invoke-Command and also do not want to read credentials from a saved file. I just want the Invoke-Command to implicitly use the credentials of the user executing the command, and be successful with using the -Authentication CredSSP arg.

Is there is a way to capture the credentials of the user executing a script and create a PSCredentials object containing that info, where the PSCredentials object could them be passed as the -Credentials $MyCred arg on Invoke-Command? That would seem to be a good workaround.

“Is there is a way to capture the credentials of the user executing a script and create a PSCredentials object containing that info”

Absolutely not. That would be quite a gaping security hole :slight_smile: If that capability existed, literally any malware that happened to execute on your system would be able to just take your password.

I didn’t realize that you are required to use the -Credential parameter when using -Authentication CredSSP, but if that’s the way it is, then I guess that’s what you’ll have to work around. You might find the $PSDefaultParameterValues variable to be helpful in this regard, or a proxy function. I do something similar with my Nuget API key for the Publish-Module command; I have this in my PowerShell profile:

    function global:Publish-Module
    {
        [CmdletBinding(DefaultParameterSetName='ModuleNameParameterSet', SupportsShouldProcess = $true, ConfirmImpact='Medium', PositionalBinding=$false)]
        param(
            [Parameter(ParameterSetName='ModuleNameParameterSet', Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
            [ValidateNotNullOrEmpty()]
            [string]
            ${Name},

            [Parameter(ParameterSetName='ModulePathParameterSet', Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
            [ValidateNotNullOrEmpty()]
            [string]
            ${Path},

            [Parameter()]
            [ValidateNotNullOrEmpty()]
            [string]
            ${NuGetApiKey},

            [ValidateNotNullOrEmpty()]
            [string]
            ${Repository},

            [ValidateNotNullOrEmpty()]
            [string]
            ${ReleaseNotes},

            [ValidateNotNullOrEmpty()]
            [string[]]
            ${Tags},

            [ValidateNotNullOrEmpty()]
            [uri]
            ${LicenseUri},

            [ValidateNotNullOrEmpty()]
            [uri]
            ${IconUri},

            [ValidateNotNullOrEmpty()]
            [uri]
            ${ProjectUri}
        )

        begin
        {
            if (-not $PSBoundParameters.ContainsKey('NuGetApiKey'))
            {
                $apiKeyFile = "$PSScriptRoot\PowerShellGetApiKey.encrypted.xml"

                if ((Test-Path -Path $apiKeyFile) -and
                    (Get-Module ProtectedData -ListAvailable))
                {
                    $apiKey = Import-Clixml -Path $apiKeyFile | Unprotect-Data
                    if ($null -ne $apiKey) { $PSBoundParameters['NuGetApiKey'] = $apiKey }
                }
            }

            try {
                $outBuffer = $null
                if ($PSBoundParameters.TryGetValue('OutBuffer', [ref]$outBuffer))
                {
                    $PSBoundParameters['OutBuffer'] = 1
                }

                $wrappedCmd = Get-Command -Name PowerShellGet\Publish-Module -CommandType Function -ErrorAction Stop
                $scriptCmd = { & $wrappedCmd @PSBoundParameters }
                $steppablePipeline = $scriptCmd.GetSteppablePipeline($myInvocation.CommandOrigin)
                $steppablePipeline.Begin($PSCmdlet)
            } catch {
                throw
            }
        }

        process
        {
            try {
                $steppablePipeline.Process($_)
            } catch {
                throw
            }
        }

        end
        {
            try {
                $steppablePipeline.End()
            } catch {
                throw
            }
        }
    }

Most of that is just the auto-generated proxy function for Publish-Module, except for this bit in the Begin block:

            if (-not $PSBoundParameters.ContainsKey('NuGetApiKey'))
            {
                $apiKeyFile = "$PSScriptRoot\PowerShellGetApiKey.encrypted.xml"

                if ((Test-Path -Path $apiKeyFile) -and
                    (Get-Module ProtectedData -ListAvailable))
                {
                    $apiKey = Import-Clixml -Path $apiKeyFile | Unprotect-Data
                    if ($null -ne $apiKey) { $PSBoundParameters['NuGetApiKey'] = $apiKey }
                }
            }

I had previously saved my API key using the ProtectedData module (which is where the Unprotect-Data command comes from), and exported it to disk with Export-Clixml. This function just reversed that process: Import-Clixml and pipe to Unprotect-Data, assuming that I haven’t already passed in an API key when calling the command.

I see your example used the approach to save some protected data in an XML file.

This approach is similar to other posts I have seen where one could save a user account password as a secure string in an XML file and then read/convert the secure string back into a PSCredentials object just before calling the Invoke-Command.

Since my credentials are part of a domain where there is a password change required every 90 days, I did not want the maintenance chore to have to remember to re-create the XML credentials file on the password change day.

The very fact that one can even use a file system file to save a secure string password and then read the file when the need for the credentials is required is no less secure than allowing credentials to be implicitly passed to the remote script block.

If there is no other way for a script to determine the credentials it is executing under, and then use those credentials to pass to a remote script block, then I guess I will have to resort to saving the account password as a secure string in an XML file, and then remember to re-create the file when the password change occurs. I just thought I would bring this up in a technical forum to see if there is a better option.

You need CredSSP.