---
title: "PowerShell Invoke-Command: A Comprehensive Guide"
description: "Learn how to use the PowerShell Invoke-Command cmdlet to execute code inside a PSSession, a commonly used cmdlet for remote management and automation tasks."
canonical: "https://adamtheautomator.com/invoke-command/"
---

# PowerShell Invoke-Command: A Comprehensive Guide

> Learn how to use the PowerShell Invoke-Command cmdlet to execute code inside a PSSession, a commonly used cmdlet for remote management and automation tasks.

Source: https://adamtheautomator.com/invoke-command/

---

ATA Learning

Tap to hide

[

ATA Learning

](/)

*   [Home](/)
*   [Tutorials](/tutorials/)
*   [Instructors](/author/)
*   [Advertising](/advertising/)
*   [Recommended Resources](/resources/)
*   [About Adam](/about-adam/)

Search for:  

*   [](https://twitter.com/adbertram)
*   [](https://github.com/Adam-the-Automator)
*   [](https://www.linkedin.com/company/adam-the-automator-llc)
*   [](/feed/)

![PowerShell Invoke-Command: A Comprehensive Guide](https://adamtheautomator.com/wp-content/uploads/2019/06/5d238ae5c824b514689ea538.jpg)

# PowerShell Invoke-Command: A Comprehensive Guide

[![](https://secure.gravatar.com/avatar/d0b9d42e21e5622713f8b693aa5c0f9244d5f7dd200ed29b8398f52dee5de337?s=192&d=mm&r=g)Adam Bertram](https://adamtheautomator.com/author/adam-bertram/)16 June 20196 min. read

Categories: [IT Ops](/category/it-ops/)

Tags:[PowerShell](/tag/powershell/)

Table of Contents

*   [Passing Local Variables to Remote Scriptblocks](#passing-local-variables-to-remote-scriptblocks)
*   [The ArgumentList Parameter](#the-argumentlist-parameter)
*   [The $Using Construct](#the-using-construct)
*   [Invoke-Command and New-PSSession](#invoke-command-and-new-pssession)
*   [Summary](#summary)

IT professionals rarely work just on our local computer. Using the PowerShell Invoke-Command cmdlet, we don’t have to! This cmdlet allows us to seamlessly write code as if we were working on our local computer.

By using the PowerShell Remoting feature, The `Invoke-Command` cmdlet is a commonly used PowerShell cmdlet that allows the user to execute code inside of a PSSession. This PSSession can either be one created previously with the `New-PSSession` cmdlet or it can quickly create and tear down a temporary session as well.

**_Related: [PowerShell Remoting: The Ultimate Guide](https://adamtheautomator.com/psremoting/)_**

Think of Invoke-Command as the PowerShell [psexec](https://docs.microsoft.com/en-us/sysinternals/downloads/psexec). Though they are implemented differently, the concept is the same. Take a bit code or command and run it “locally” on the remote computer.

For `Invoke-Command` to work though, you must have PowerShell Remoting enabled and available on the remote computer. By default, all Windows Server 2012 R2 or later machines do have it enabled along with the appropriate firewall exceptions. If you’re unfortunate enough to still have Server 2008 machines, there are multiple ways to set up Remoting but an easy way is by running `winrm quickconfig` or `Enable-PSRemoting` on the remote machine.

To demonstrate how Invoke-Command works with an “ad-hoc command” meaning one that doesn’t require a new PSSession to be created, let’s say you’ve got a remote Windows Server 2012 R2 or later domain-joined computer. Things get a little messy when [working on workgroup computers](https://docs.microsoft.com/en-us/archive/blogs/wmi/powershell-remoting-between-two-workgroup-machines). I’ll open up my PowerShell console, type `Invoke-Command` and hit Enter.

```powershell
PS> Invoke-Command
cmdlet Invoke-Command at command pipeline position 1
Supply values for the following parameters:
ScriptBlock:
```

I’m immediately asked to provide a scriptblock. The scriptblock is the code that we’re going to run on the remote computer.

So that we can prove that the code inside of the scriptblock is executed on the remote computer, let’s just run the `hostname` command. This command will return the hostname of the computer it is running on. Running `hostname` on my local computer yields, it’s name.

```powershell
PS> hostname
MACWINVM
```

Let’s now pass a scriptblock with that same code inside of a scriptblock to `Invoke-Command`. Before we do that though, we’re forgetting a required parameter: `ComputerName`. We have to tell `Invoke-Command` what remote computer to run this command on.

```powershell
PS> Invoke-Command -ScriptBlock { hostname } -ComputerName WEBSRV1 WEBSRV1
```

Notice that the output of `hostname` is now the name of the remote computer `WEBSRV1`. You’ve run some code on _WEBSRV1_. Running simple code inside of a scriptblock and passing to a single remote machine is the easiest application of `Invoke-Command` but it can do so much more.

## Passing Local Variables to Remote Scriptblocks

You’re not going to have a single Invoke-Command reference inside of a script. Your script is probably going to be dozens of lines long, have variables defined places, functions defined in modules and so on. Even though just enclosing some code in a couple curly braces may look innocent, you’re in fact changing the entire scope that code is running in. After all, you’re sending that code to a remote computer. That remote computer has no idea of all of the local code on your machine other than what’s in the scriptblock.

For example, perhaps you’ve got a function with computer name and a file path parameter. This function’s purpose is to run some software installer on the remote computer. You are able to pass the computer name and the “local” file path to the installer that’s already located on the remote computer.

The function below seems reasonable, right? Let’s run it.

```powershell
function Install-Stuff {
    param(
    	[Parameter(Mandatory)]
        [ValidateNotNullOrEmpty()]
        [string]$ComputerName,
        
        [Parameter(Mandatory)]
        [ValidateNotNullOrEmpty()]
        [string]$InstallerFilePath
	)
    Invoke-Command -ComputerName $ComputerName -ScriptBlock { & $InstallerFilePath }
}

PS> Install-Stuff -ComputerName websrv1 -InstallerFilePath 'C:\install.exe'
The expression after '&' in a pipeline element produced an object that was not valid. It must result in a command name, a script block, or a CommandInfo object.
+ CategoryInfo          : InvalidOperation: (:) [], RuntimeException
+ FullyQualifiedErrorId : BadExpression
+ PSComputerName        : websrv1
```

It fails with an obscure error error message due to my use of the ampersand operator. The code wasn’t wrong but it failed because `$InstallerFilePath` was empty even though you passed a value in with the function parameter. We can test this by replacing the ampersand with [`Write-Host`](https://adamtheautomator.com/powershell-write-host/ "Write-Host").

New function line: `Invoke-Command -ComputerName $ComputerName -ScriptBlock { Write-Host "Installer path is: $InstallerFilePath" }`

```powershell
PS> Install-Stuff -ComputerName websrv1 -InstallerFilePath 'C:\install.exe'
Installer path is:
PS>
```

Notice that the value of `$InstallerFilePath` is nothing. The variable hasn’t expanded because it wasn’t passed to the remote machine. To pass locally defined variables to the remote scriptblock, we’ve got two options; we can preface the variable name with `$using:` inside of the scriptblock or we can use `Invoke-Command` parameter `ArgumentList`. Let’s look at both.

### The ArgumentList Parameter

One way to pass local variables to a remote scriptblock is to use the `Invoke-Command` `ArgumentList` parameter. This parameter allows you to pass local variables to the parameter and replace local variable references in the scriptblock with placeholders.

Passing the local variables to the `ArgumentList` parameter is easy.

```powershell
Invoke-Command -ComputerName WEBSRV1 -ScriptBlock { & $InstallerFilePath } -ArgumentList $InstallerFilePath
```

The part that trips up some people is how to structure the variables inside of the scriptblock. Instead of using `{ & $InstallerPath }`, we need to change it to either be `{ & $args[0] }` or `{param($foo) & $foo }`. Either way works the same but which one should you use?

The `ArgumentList` parameter is an object collection. Object collections allow you to pass one or more objects at a time. In this instance, I’m just passing one.

When executed, the Invoke-Command cmdlet takes that collection and then injects it into the scriptblock essentially transforming it into an array called `$args`. Remember that `$args -eq ArgumentList`. At this point, you’d reference each index of the collection just like you would an array. In our case, we only had one element in the collection (`$InstallerFilePath`) which “translated” to `$args[0]` meaning the first index in that collection. However, if you had more, you’d reference with them `$args[1]`, `$args[2]` and so on.

Additionally, if you’d rather assign better variable names to scriptblock variables, you can also add parameters to the scriptblock just like a function. After all, a scriptblock _is_ just an anonymous function. To create scriptblock parameters, create a _param_ block with the name of the parameter. Once created, then reference that parameter in the scriptblock like below.

```powershell
Invoke-Command -ComputerName WEBSRV1 -ScriptBlock { param($foo) & $foo } -ArgumentList $InstallerFilePath
```

In this instance, the elements in the `ArgumentList` collection are “mapped” to the defined parameters in order. The parameter names don’t matter; it’s the order that’s important. `Invoke-Command` will take the first element in the `ArgumentList` collection, look for the first parameter and map those values, do the same for the second, the third and so on.

### The $Using Construct

The `$using` construct is another popular way to pass local variables to a remote scriptblock. This construct allows you to reuse the existing local variables but simply prefacing the variable name with `$using:`. No need to worry about an `$args` collection nor adding a parameter block.

```powershell
Invoke-Command -ComputerName WEBSRV1 -ScriptBlock { & $using:InstallerFilePath }
```

The PowerShell `$using` construct is a lot simpler but if you ever get into [learning Pester](https://leanpub.com/pesterbook), you’ll see that `ArgumentList` will be your friend.

## Invoke-Command and New-PSSession

Technically, this post is only about Invoke-Command but to demonstrate it’s usefulness, we need to briefly touch on the `New-PSSession` command as well. Recall earlier that I mentioned `Invoke-Command` can use “ad-hoc” commands or use existing sessions.

Throughout this post, we’ve just been running “ad-hoc” commands on remote computers. We’ve been bringing up a new session, running code and tearing it down. This is fine for one-off situations but not so much for a time when you’re performing dozens of commands on the same computer. In this case, it’s better to reuse an existing PSSession by creating one with `New-PSSession` ahead of time.

Before running any commands, you’ll first need to create a PSSession with `New-PSSession`. We can do this by simply running `$session = New-PSSession -ComputerName WEBSRV1`. This creates a remote session on the server as well as a reference to that session on my local machine. At this point, I can replace my `ComputerName` references with `Session` and point `Session` to my saved `$session` variable.

```powershell
Invoke-Command -Session $session -ScriptBlock { & $using:InstallerFilePath }
```

When ran, you’ll notice performance is faster because the session has already been built. When complete though, it’s important to remove the open session with `Remove-PSSession`.

## Summary

The Invoke-Command PowerShell cmdlet is one of the most common and powerful cmdlets there is. It is one I personally use the most out of nearly all of them. It’s ease of use and ability to run any code on remote computers is extremely powerful and is one command I recommend learning top to bottom!

Share this article

[Share on X](https://twitter.com/intent/tweet?url=https%3A%2F%2Fadamtheautomator.com%2Finvoke-command%2F&text=PowerShell%20Invoke-Command%3A%20A%20Comprehensive%20Guide)[Share on Facebook](https://www.facebook.com/sharer/sharer.php?u=https%3A%2F%2Fadamtheautomator.com%2Finvoke-command%2F)[Share on LinkedIn](https://www.linkedin.com/sharing/share-offsite/?url=https%3A%2F%2Fadamtheautomator.com%2Finvoke-command%2F)

## Related Posts

![](https://adamtheautomator.com/wp-content/uploads/2026/06/26995-troubleshoot-dns-issues-powershell-codex.webp)

### [Troubleshoot DNS Issues with PowerShell](/troubleshoot-dns-issues-powershell/)

Troubleshoot DNS issues with PowerShell by testing name resolution, DNS client settings, cache entries, and network connectivity in a repeatable workflow.

![](https://adamtheautomator.com/wp-content/uploads/2025/10/image_2025-10-24_095322075.png)

### [Migrating from PowerShell 6 to 7.5: Breaking Changes/New Features](/migrating-powershell-6-to-7-5/)

Migrate from PowerShell Core 6 to 7.5: breaking changes, features, and testing tips.

![](https://adamtheautomator.com/wp-content/uploads/2025/06/featured-image-6.png)

### [How to Add Timeouts to Pester Tests with PowerShell Runspaces](/pester-test-timeout-runspaces/)

Prevent Pester tests from hanging indefinitely using PowerShell runspaces. Learn to handle variable scoping, module loading, TestDrive access, and stream capture challenges with timeout protection.

## Categories

*   [IT Ops](/category/it-ops/)
*   [Cloud](/category/cloud/)
*   [DevOps](/category/devops/)
*   [Home Ops](/category/home-ops/)
*   [Information Security](/category/infosec/)
*   [Software Development](/category/software-development/)

## Site

*   [Home](/)
*   [Tutorials](/tutorials/)
*   [Instructors](/author/)
*   [Advertising](/advertising/)
*   [Recommended Resources](/resources/)
*   [About Adam](/about-adam/)

Copyright 2026© ATA Learning | [Privacy Policy](/privacy/)
