Remote Access

All PowerShell cmdlets dealing with WMI (Windows Management Instrumentation) support remote access. In this article, you’ll learn about the prerequisites to connect to remote computers and run any ...

All PowerShell cmdlets dealing with WMI (Windows Management Instrumentation) support remote access. In this article, you’ll learn about the prerequisites to connect to remote computers and run any WMI operation remotely.

Protocols: DCOM and WinRM

WMI supports two protocols to connect to remote computers: DCOM (Distributed COM) and WinRM (Windows Remote Management).

DCOM is the older technology and was introduced with Windows NT. While it is enabled by default on many servers for historic reasons, it is considered out-dated.

WinRM is a webservice-based modern protocol and was introduced in PowerShell 2. It is more reliable, safer, and faster. Unlike DCOM, it uses only one dedicated firewall port.

Connecting via DCOM

Since DCOM is considered obsolete, it is not recommended to use this protocol. If you still use the old cmdlet Get-WmiObject, it is your only choice, though. Use the code below to try and connect to a remote machine:

# the computer name or IP address you want to connect to:
$computer = '192.168.2.109'

# use a user account that has Administrator permissions on
# the target computer:
$credential = Get-Credential 

# try and retrieve BIOS information:
Get-WmiObject -Class Win32_BIOS -ComputerName $computer -Credential $credential

When the code succeeds, you receive BIOS information from the remote machine:

SMBIOSBIOSVersion : 4KCN45WW
Manufacturer      : LENOVO
Name              : 4KCN45WW
SerialNumber      : PF0LTV33
Version           : LENOVO - 1

If you get an error, it indicates that some of the prerequisites are missing:

  • Access Denied: the user account that you used has no Administrator privileges on the target machine.
  • RPC Server Unavailable: the target computer could not be reached. It is turned off, you misspelled its name, or the firewall blocked access.

Adding Firewall Exception

For DCOM, the Remote Administration Exception must be enabled in the target computer firewall. The easiest manual way is to use an old and obsolete command. Run it on the target computer with Administrator privileges:

netsh firewall set service remoteadmin enable

Once the command executed, try again the code above.

Using CimCmdlets

Get-WmiObject is considered obsolete and was replaced by the CIM cmdlets in PowerShell 3. It is recommended to use Get-CimInstance instead of Get-WmiObject.

One of the most fundamental differences between these two is the default network protocol they use: Get-WmiObject always uses DCOM whereas Get-CimInstance uses the modern WinRM protocol by default but can also be configured to fall back to DCOM.

To repeat above example with Get-CimInstance, try this code:

# the computer name or IP address you want to connect to:
$computer = '192.168.2.109'

# use a user account that has Administrator permissions on
# the target computer:
$credential = Get-Credential 

# create a CIM session that uses DCOM:
$options = New-CimSessionOption -Protocol Dcom
$session = New-CimSession -ComputerName $computer -SessionOption $options -Credential $credential

# try and retrieve BIOS information:
Get-CimInstance -ClassName Win32_BIOS -CimSession $session

# remove session after use
Remove-CimSession -CimSession $session

The result looks almost the same. CIM Cmdlets always automatically add the property PSComputerName, though.

Connecting via WinRM

Whenever possible, you should avoid using the obsolete protocol DCOM, and use the modern protocol WinRM instead. This is why the CIM cmdlets use this protocol by default.

Testing WinRM Connectivity

The first test should be whether the target machine is already set up for WinRM. Test-WsMan does exactly that and tests the connection much like the old-fashioned ping command: it checks whether WinRM echoes your request. For this, no permissions are required, so it is just checking the physical connectivity from you to the target computer.

Run this code:

# the computer name or IP address you want to connect to:
$computer = '192.168.2.109'

# test whether the physical connection to the target is
# possible (no permissions required):
Test-WSMan -ComputerName $computer

You should receive a result similar to this:

wsmid           : http://schemas.dmtf.org/wbem/wsman/identity/1/wsmanidentity.xsd
ProtocolVersion : http://schemas.dmtf.org/wbem/wsman/1/wsman.xsd
ProductVendor   : Microsoft Corporation
ProductVersion  : OS: 0.0.0 SP: 0.0 Stack: 3.0

If you receive an error message instead, then most likely the target machine is not yet set up to receive WinRM remoting requests. The next section explains how you enable WinRM on the target machine.

Enabling PowerShell Remoting

On servers, WinRM remoting is enabled by default. On clients, it is not. To manually enable WinRM remoting, run this command with Administrator privileges:

Enable-PSRemoting -SkipNetworkProfileCheck -Force

Note: Do this on the target machine (the one you want to connect to). The client machine (the one where you are running PowerShell code) does not need to have remoting turned on.

When you do this for the first time, the command reports in detail the settings it configured. If it ran before, you see no return values.

Run Disable-PSRemoting to disable PowerShell remoting. This cmdlet will not revert all the settings done by Enable-PSRemoting because part of these settings may be required by other services. Instead, Disable-PSRemoting disables connections and effectively prevent connections.

Once PowerShell remoting is enabled, use Test-WsMan (see above) to test connectivity.

Test-WsMan is an anonymous test. When it succeeds, you know that PowerShell remoting is enabled and all firewall exceptions are in place. Permissions are not subject to this test, so let’s test these next (see below)

Testing Authentication

Once WinRM remoting is enabled, anyone with Administrator privileges on that machine can run code remotely. There are two ways to authenticate: via Kerberos (default) and via Negotiate.

If all permissions are in place, you might get away with running this minimal code to access WMI information remotely:

# the computer name or IP address you want to connect to:
$computer = '192.168.2.109'

# try and retrieve BIOS information:
Get-CimInstance -ClassName Win32_BIOS -ComputerName $computer

It requires:

  • that WinRM was enabled on the target computer
  • that Negotiate authentication is enabled
  • that your current user account has Administrator privileges on the target machine

In most cases, this code will fail because it is using a IP address. The default WinRM authentication uses Kerberos which requires a domain controller and works only with computer names, not IP addresses.

Either replace the IP address with a computer name that can be resolved by your domain DNS, or enable Negotiate authentication (see below).

Enabling Negotiate Authentication

Here are some good reasons why you might want to enable Negotiate authentication:

  • Peer-to-Peer Network: maybe you are not working in a network domain and want to test-drive remote access in a lab, or at home in your private network. Kerberos requires a network domain.
  • Cross-Domain: maybe the target computer belongs to a different domain, and there are no trust relationships. Kerberos requires trust relationships.
  • IP Address: or maybe you must use IP addresses. Kerberos requires computer names.

Why is Negotiate disabled by default at all? Because unlike Kerberos or certificates, it cannot guarantee that a target computer really is who it pretends to be. Any attacker with physical access to your servers could unplug a server, plug in a compromised system, and take over its IP address. So use a bit of common sense.

To enable Negotiate authentication, on the computer that initiates the connection, run the code below with Administrator privileges.

Note: do this on the client machine (the one where your PowerShell code runs). This is a client setting.

# WinRM service must run at least temporarily to 
# make the change
Start-Service -Name WinRM

# define the IP addresses you trust
# this can be "*" for all, or just a subset
# in the example, all addresses starting with "192.168." are trusted:
Set-Item -Path WSMan:\localhost\Client\TrustedHosts -Value '192.168.*' -Force

# to add more addresses or ranges, use -Concatenate
Set-Item -Path WSMan:\localhost\Client\TrustedHosts -Value '10.12.100.*' -Force -Concatenate

# double-check the setting:
Get-Item -Path WSMan:\localhost\Client\TrustedHosts

# you may stop the WinRM service after the change but should do so
# only if it did not run before (may be used by other parties)
Stop-Service -Name WinRM

To revert the default settings and disable Negotiate authentication, run this command: Clear-Item -Path WSMan:\localhost\Client\TrustedHosts.

Testing Permissions And Using CimSessions

While you can use Get-CimInstance with the parameter -ComputerName to directly connect to remote machines, it is not the smartest way.

A more professional way separates the network connection from subsequent calls. When you create a dedicated CimSession, you can specify any user account, and reuse the session for as many subsequent commands as you like.

So let’s just do this: first, create a Cim Session network connection with all the bells and whistles you require. Then, re-use this connection with as many Cim Cmdlets as you want. Finally: remove the session when it is no longer needed.

Use the code below to test whether your user account has the appropriate permissions to contact and retrieve information from the target system:

# the computer name or IP address you want to connect to
# if you are in a domain and want to use the default safe Kerberos
# authentication, use a computer name instead of an IP address:
$computer = '192.168.2.109'

# use a user account that has Administrator permissions on
# the target computer:
$credential = Get-Credential 

# create a CIM session:
$session = New-CimSession -ComputerName $computer -Credential $credential

# try and retrieve BIOS information. This makes use of 
# the session you created before:
Get-CimInstance -ClassName Win32_BIOS -CimSession $session

# remove session after use
Remove-CimSession -CimSession $session