How To Run Commands or PowerShell Scripts on Remote Computers Using Invoke-Command

So, if you’ve ever tried to run remote commands in PowerShell and kept hitting roadblocks, it’s probably totally frustrating. The Invoke-Command cmdlet is actually super handy for executing scripts or commands remotely, but it only works if the whole WinRM (Windows Remote Management) setup is configured correctly. Usually, people run into issues because WinRM isn’t enabled on the target machine, or firewalls are blocking incoming requests. Setting this up right can feel like a game of whack-a-mole, especially if you’re not familiar with the exact commands or the path to change network profiles or firewall rules. This guide covers how to enable WinRM properly, ensure your network is set up for remote management, and then how to actually run commands on remote systems without pulling out all your hair.

How to Enable PowerShell Remoting (WinRM) on Windows

Enabling WinRM Service — a must-do for remote commands to work

The main reason Invoke-Command refuses to connect? WinRM isn’t enabled or isn’t set up with the correct network rules. First things first, you need the WinRM service to be running and configured. To check if WinRM is already running and listening, run this in PowerShell on the target machine:

Get-Service -Name "*WinRM*" | fl WinRM, State Get-Item wsman:\localhost\Listener 

If it’s not configured, no worries. On the target machine, run:

winrm quickconfig

This command does a few things: starts the WinRM service, sets it to auto-start, creates default listener, and adds firewall exceptions. Sometimes, just running Enable-PSRemoting -Force inside PowerShell as Admin does the trick, especially if you’re trying to do it remotely or via scripts. Keep in mind, on some setups, you might need to tweak your network profile to Private or Domain, because PowerShell remote won’t work smoothly over Public networks by default.

Quick note: On some machines, you might run into issues with network profiles. It’s kind of weird, but changing network from Public to Private via Settings > Network > Ethernet or Wi-Fi > Network Profile often solves the permission hiccups. If remote still doesn’t connect, you might want to tweak the firewall rules or explicitly allow WinRM inbound traffic:

Set-NetFirewallRule -Name "WINRM-HTTP-In-TCP" –RemoteAddress Any

This line allows incoming WinRM traffic regardless of network address — useful if you’re dealing with multiple subnet segments or wonky network setups. Remember, because of course, Windows has to make it harder than necessary sometimes.

Trusted Hosts — when working across workgroups or IPs

If your systems aren’t in a domain, or you’re connecting via IP, you’ll need to add those remote machines to your trusted hosts list. This is a bit of a “security by convenience” thing, but it’s what’s needed. Run:

Set-Item wsman:\localhost\Client\TrustedHosts -Value "192.168.1.201"

Or, if you’re feeling reckless, just allow all:

Set-Item wsman:\localhost\Client\TrustedHosts -Value "*"

Don’t forget, you’ll need to restart the WinRM service afterward:

Restart-Service WinRM
Note: When adding multiple hosts, separate their IPs or names with commas inside quotes, like « 192.168.1.201, 192.168.1.202 ».

Using Invoke-Commands to Run Commands on Remote Computers

Executing a single command — straightforward but needs correct setup

Once WinRM is set up and your network rules are sorted, using Invoke-Command is just a matter of specifying the target machine and what you want to run. For example:

Invoke-Command -ComputerName dc01 -ScriptBlock { Get-Service wuauclt }

This will ask the remote machine named dc01 to tell you whether the Windows Update Service is running. Expect to see the service status printed out — on one setup it worked flawlessly, on another, there might be some authentication hiccups, so double-check your permissions and network rules.

Nifty tip: If you want to run the command under a different user account, just add -Credential and supply the creds:

$cred = Get-Credential Invoke-Command -ComputerName dc01 -Credential $cred -ScriptBlock { Get-NetAdapter }

This usually helps if your current user isn’t part of Remote Management or Admin groups on the target machine. Just be aware — a lot of permissions are involved, and sometimes PowerShell security policies block certain remote commands.

Running multiple commands or scripts remotely — yeah, you can do that

Want to do more than one thing at once? Just separate commands with a semicolon inside the ScriptBlock:

Invoke-Command -ComputerName dc01 -ScriptBlock { Get-TimeZone; Set-TimeZone -Name "Central Europe Standard Time" }

And if you’ve got a script saved somewhere, like C:\PS\CheckStatus.ps1, you can run it remotely with:

Invoke-Command -ComputerName dc01 -FilePath "C:\PS\CheckStatus.ps1"

This way, you don’t need to copy scripts manually to remote systems, and the execution policy mismatch is kinda bypassed. Handy for batch jobs or admin scripts you run regularly.

Heads up: If your scripts access remote resources (like network shares), you might run into the dreaded « double-hop » problem. It’s a Windows security thing — Google it for workarounds like Kerberos delegation, or use CredSSP, because otherwise, credentials won’t pass through to third-party resources.

Running commands on multiple computers at once

Parallel commands for a bunch of servers

If you’re managing a fleet of servers, running commands serially would be a pain. Luckily, Invoke-Command allows parallel execution. Just list your servers separated by commas:

Invoke-Command server1, server2, server3 -ScriptBlock { get-date }

You can also store the names in an array for reuse:

$servers = @("server1", "server2", "server3") Invoke-Command -ComputerName $servers -ScriptBlock { get-date }
Or, you can feed a list from a text file, making automation easier:

Invoke-Command -ComputerName (Get-Content c:\ps\servers.txt) -ScriptBlock { Restart-Service spooler }

If you’re pulling host names from Active Directory, the Get-ADComputer cmdlet helps filter for certain systems:

$computers = (Get-ADComputer -Filter 'OperatingSystem -like "*Windows server*" -and Enabled -eq "true"').Name Invoke-Command -ComputerName $computers -ScriptBlock { Get-EventLog -LogName System -Newest 10 } -ErrorAction SilentlyContinue

And if one or more targets is offline or busy? The -ErrorAction SilentlyContinue parameter keeps the script running smoothly. Plus, to see which machine responded, use the PSComputerName variable inside your ScriptBlock:

$results = Invoke-Command -ComputerName $computers -ScriptBlock { get-date } $results | Select-Object PSComputerName, * 

PowerShell Remoting Persistent Connections — save time and hassle

Create a session that sticks around

If you’re doing multiple commands on the same remote machine, it makes sense to keep a session alive instead of reconnecting every time. Here’s how you create and use a persistent session:

$session = New-PSSession -ComputerName Server01, Server02 Invoke-Command -Session $session -ScriptBlock { Get-ComputerInfo | select OsLastBootUpTime }

Once you’re done, close that session with:

Remove-PSSession $session

This setup can seriously save time when managing multiple servers, especially if you run a bunch of commands in a script or scheduled job. Just keep in mind that sessions can hang around in memory, so close them when they’re no longer needed. Also, some setups might need a little extra security tweaking or policy changes to allow persistent sessions.

Honestly, getting PowerShell remote management up and running can feel like beating a big boss at first, but once you’ve got it, it’s pretty darn powerful. Just remember to check network and firewall rules, and give yourself some patience for the quirks.