How to Remotely Run PowerShell Code – The Basics

To start off my Solo Sysadmin Series, I figured one of the best places to start is with is how to remotely run PowerShell code. There are several ways to accomplish this, but I figured I’d share the method I use most often. In this post, I’ll be providing a simple script that can be modified without too much issue, and then break down its parts and give examples for uses. If you are a solo sysadmin and want some simple tools to help you work more efficiently, then follow along.

One thing I notice quite a bit of on Reddit’s r/sysadmin, is that there are a decent number of solo system administrators (Solo Sysadmin’s) out there running alone or in very small teams. I can’t imagine not having the ability to pass some responsibilities over to other teammates or being responsible for a significantly wide range of functions.

I’ve been fortunately to be a part of teams that are on the larger side of “Small teams” and have never had to be solo. However, I know that it can be a challenge keeping up, and so I wanted to make some tools for those lone rangers out there fighting the good fight all alone. I hope these tools help you save time and your hair!

DISCLAIMER

Please understand that the content herein is for informational purposes only. This existence and contents shall not create an obligation, liability or suggest a consultancy relationship. In further, such shall be considered as is without any express or implied warranties including, but not limited to express and implied warranties of merchantability, fitness for a particular purpose and non-infringement. There is no commitment about the content within the services that the specific functions of the services or its reliability, applicability or ability to meet your needs, whether unique or standard. Please be sure to test this process fully before deploying in ANY production capacity, and ensure you understand that you are doing so at your own risk.

Table of Contents

Sample Use Cases
Script Breakdown
Remote Script Template
Conclusion


Sample Use Cases

I felt it was probably a good idea to start off with some example. Main reason being that there are several reasons you might want to remotely run PowerShell on your other Windows Servers or devices. Here are some of the ones that come to mind, those this list is nowhere near exhaustive…

  1. Start/Stop Services
  2. Change Local Account Passwords
  3. Change Network settings
  4. Apply security configurations
  5. Change Configuration files
  6. Install/Uninstall Software
  7. The list goes on…

Once you get the hang of running PowerShell remotely, you will undoubtedly discover many more reasons to run PowerShell remotely. PowerShell remoting is one of the most powerful parts of PowerShell and allows it to become a force multiplier in any kind of configuration or deployment.


Script Breakdown

The script for this example has three main parts, two of which are contained within the loop to iterate through the servers:

  1. Declared Variables
  2. Script Block Containing Our Code to be run remotely.
  3. Try-Catch to attempt change and process errors.

This is pretty standard, so I won’t spend time breaking down the structure as much as the reasons.

Declared Variables

# Specify the OU distinguished name
$ou = "OU=Servers,DC=domain,DC=com"  

# Desired DNS server addresses
$desiredDNSServers = "192.168.0.2", "192.168.0.3"

# Retrieve all servers from the specified OU
$servers = Get-ADComputer -Filter * -SearchBase $ou -Properties Name, WhenCreated 

In this first part, we’re declaring our variables outside of any loops, so they persist through the entire script. First, we are declaring what OU we want to pull servers from in AD, (our SearchBase). Then, we are declaring what IP’s we want to use for our DNS Servers. Finally, we are gathering all the servers from the OU we identified earlier and saving them to a variable for the loop later.

Something to keep in mind, is that in this example, I dynamically grabbing the servers in a specific OU to run this script against. However, you could also use an array of server names, or import a CSV of server names as well. Those might look something like this:

# Array Example: 
$servers = @("Server1","Server2","Server3")

#CSV Import Example: Ensure you have the Name & DNSHostName as column headers for your CSV import file 
$servers = Import-csv -Path "C:\Path\To\CSV.file.csv"

Script Block

$scriptBlock = {

        # Get the DNS domain suffix search order
        $domainSuffixes = (Get-DnsClientGlobalSetting).SuffixSearchList

        # Get network interfaces and filter based on domain suffix search order
        $networkInterfaces = Get-NetIPConfiguration | Where-Object { $_.NetProfile.Name -in $domainSuffixes }

        # Assign the interfaces to a variable as an integer
        [int]$InterfaceIndex = ($networkInterfaces).InterfaceIndex

        # Set the DNS Client Server Address
        Set-DnsClientServerAddress -ServerAddresses $desiredDNSServers -InterfaceIndex $InterfaceIndex

        # output the current computer and the presently assigned DNS Addresses
        Write-Host $ENV:COMPUTERNAME -ForegroundColor Cyan
        Get-DnsClientServerAddress -AddressFamily IPv4 | Select-Object -ExpandProperty ServerAddresses
    }

Here is the script block for the DNS server change example I mentioned. This section is what will get passed through to the remote server as our script. Think of this as a ‘mini script within a script’. If we were on the local machine, this is what our script would be all by itself. If you were converting an existing script to something you could run remotely, this is where you would place it.

Try-Catch Block

try {
        # create a new PowerShell session to the remote server
        $session = New-PSSession -ComputerName $s.DNSHostName -ErrorAction Stop

        # Execute the script block on the remote server within the PSSession
        Invoke-Command -Session $session -ScriptBlock $scriptBlock -ErrorAction Stop

        # Close the PSSession
        Remove-PSSession $session
    }
    Catch {
        # Write an error for failed connections so you know which ones did not run
        Write-Host "Could not connect to: " $s.Name -ForegroundColor Red
    }

In this case, the Try-Catch block is meant to actually perform the actions and catch the exception if one occurs:

  1. Create a new PowerShell Remote Session.
  2. Run the script block using the session that was created.
  3. Remove the PowerShell session once complete.

There will likely be some cases where you can’t successfully connect to a server, or the remote command may fail. The Catch block is meant to let you know what server failed, so you can understand where you might need to manually intervene.


Remote Script Template

As usual, you can always find the latest version of this code at my GItHub page here:

Powershell/Solo SysAdmin Scripts/Run-PwshRemoteCode.ps1

However, in case you found this via a search engine, and don’t feel like any extra clicks, here is the original template I created for this function.

# Specify the OU distinguished name
$ou = "OU=Servers,DC=domain,DC=com"  

# Retrieve all servers from the specified OU
$servers = Get-ADComputer -Filter * -SearchBase $ou -Properties Name, WhenCreated 

Foreach ($s in $servers) {
    
    $scriptBlock = {
        # Enter Code to be run remotely between these script block brackets


    }

    try {
        # create a new PowerShell session to the remote server
        $session = New-PSSession -ComputerName $s.DNSHostName -ErrorAction Stop

        # Execute the script block on the remote server within the PSSession
        Invoke-Command -Session $session -ScriptBlock $scriptBlock -ErrorAction Stop

        # Close the PSSession
        Remove-PSSession $session
    }
    Catch {
        # Write an error for failed connections so you know which ones did not run
        Write-Host "Could not connect to: " $s.Name -ForegroundColor Red
    }
} 

This template serves as the basis for any simple scripts where you might want to remotely run the code. Keep in mind that you could move the script block section outside of the loop if there wasn’t anything that was changing with your code or needed pulled dynamically within the loop. It would run more efficiently that way, though I included it because you may or may not know whether or not it needs to be in the loop. This way you don’t have to worry about it.

Either way, this is the starting template for running code remotely, and is a good way to start. I hope this saves you some time when looking for a script example for running PowerShell code remotely on several servers or computers.


Conclusion

There you have it! The first of (hopefully) many posts that help out those Solo SysAdmin’s out there. I can imagine it is tough having to run the show all alone, and everything you can do to save time helps. I know for me, when I was starting out, I relied heavily on the work of others to learn and grow and get things done, and that motivated me to give back and do the same thing. As a result, I hope this find someone who needs it and gets them past whatever challenge they are working on.

There you have it! I hope you found this interesting. Let me know what you think! I’m open to suggestions about changes you would make. Alternatively, if you have any topics specifically you need help with, let me know! I’m by no means an expert, but I like a good challenge! Hit me up on Twitter @SeeSmittyIT to let me know what you thought of this post. Or if you are avoiding the bird site, I’m also posted up on Mastodon @[email protected]. Thanks for reading!

Smitty

Curtis Smith works in IT with a primary focus on Mobile Device Management, M365 Apps, and Azure AD. He has certifications from CompTIA and Microsoft, and writes as a hobby.

View all posts by Smitty →