Remote Server Administration with PowerShell

Your RDP Client is going to get straight up jealous

Intro

In the past remote Window’s administration has been limited to RDP, Citrix, DRAC/KVM, VNC, or the command line using PsExec. Developing scripts to remotely connect to hosts and gather data utilizing these legacy technologies became very cumbersome. One of the best things about PowerShell is the ability to manage hosts remotely quickly and efficiently. In a previous post I wrote about Local Server Administration with PowerShell which included quick glances at cmdlets for managing Windows features, firewall administration, network connectivity diagnostic tools, HTTP and HTTPS diagnostic cmdlets, and scheduled job management. The same cmdlets are available remotely; allowing us to configure servers without the need to RDP and executing PowerShell commands locally.

First we need to understand Windows Remote Management (WinRM) as PowerShell uses this technology to remotely connect to servers and other infrastructure. WinRM provides a common way for systems to access and exchange management information across an IT infrastructure. WinRM was introduced with the Windows Vista/Server 2008 kernel and can retroactively be installed in earlier versions by installing Windows Management Framework 2.0.

Security Implications with WinRM

Before implementing PowerShell Remoting it is important to understand the security implications. WinRM, by default, will use the HTTP protocol on port 5985 to communicate with a remote host. Microsoft Negotiate Authentication will decide to authenticate using either Kerberos and NTLM. The preference will be Kerberos, which will be used in the same AD domains or domains with trust relationships.

If you analyze packet captures during the WinRM process over HTTP, and follow the entire TCP stream, you will see that the content is encrypted with the content type of HTTP-SPNEGO-session-encrypted for NTLM v2 and HTTP-Kerberos-session-encrypted for Kerberos. Here is an example of a WinRM session using Kerberos as the local and remote computers were on the same domain:

WinRM-Kerberos-Capture

The actual password is never sent over the wire and the only way that a password could be decoded is if the Security Accounts Manager (SAM) on the remote machine had already been compromised. The hash that is stored in the Security Accounts Manager is never actually sent and cannot be decoded. Even if there was someone snooping packets on the network they wouldn’t see anything in clear text and therefore an exploit is extremely unlikely. With this data we conclude that WinRM is encrypted by default and that configuring WinRM for HTTPS would be an added layer of security.

Configuring PowerShell Remoting

Configuring PowerShell Remoting was originally difficult and time consuming because administrators had to manually create the WinRM listener, set the service to automatically start, enable PowerShell session configurations, and troubleshooting authentication. Now, all of this is configured out of the box; primarily because the majority of new management tools application require this to properly function. Additionally, several cmdlets have a ComputerName parameter that is supported to identify which computer you want to run the command on. Here is how you can enable PowerShell remoting on previous versions of Windows.

Configure PowerShell Remoting with HTTP

Starting in PowerShell 2.0 you can enable PowerShell remoting with a single command:

Enable-PSRemoting

The Enable-PSRemoting cmdlet runs the Set-WSManQuickConfig cmdlet, which performs the following tasks:

  • Starts the WinRM service.
  • Sets the startup type on the WinRM service to Automatic.
  • Creates a listener to accept requests on any IP address.
  • Enables a firewall exception for WS-Management communications.
  • Registers the Microsoft.PowerShell and Microsoft.PowerShell.Workflow session configurations, if it they are not already registered.
  • Registers the Microsoft.PowerShell32 session configuration on 64-bit computers, if it is not already registered.
  • Enables all session configurations.
  • Changes the security descriptor of all session configurations to allow remote access.
  • Restarts the WinRM service to make the preceding changes effective.

Configuring PowerShell Remoting with HTTPS

If you would like this additional layer of security, the process is slightly more complex than the Enable-PSRemoting command. The first thing you need to do is obtain an SSL cert for the remote server or workstation. You can script out the issuing of certificates by using Certificate Enrollment Web Service and Certificate Enrollment Policy Web Service in Windows Server 2012 R2. I would also recommend deploying a duplicate of the Web Server certificate template and modifying the settings to meet your organization’s needs. Here is an example of how you can obtain these certificates using PowerShell.

PS C:\> $uri = “https://yourca.domain.tld/Policy/service.svc”

PS C:\> $fqdn = “remotehost.domain.tld”

PS C:\> $subject = “[email protected], C=US, S=Ohio, L=Dayton, O=DataYard, OU=TechOps,CN=$fqdn”

PS C:\> $netbios = “remotehost”

PS C:\> $creds = Get-Credential

PS C:\> Get-Certificate -Template “WinRM” -Url $uri -DNSName $fqdn,$netbios -CertStoreLocation cert:\LocalMachine\My `

-SubjectName $fqdn `

-Credential $creds

You can issue these commands from the remote host in the same domain. If the certificate request is issued, then the returned certificate is installed in the machine’s personal certificate store. If the request is made pending, then the request is installed in the machine’s request store. Once you have the certificate installed you will need to obtain the certificate thumbprint for the issued certificate and copy it to your clipboard:

PS C:\> $thumbprint = (gci cert:\LocalMachine\My | Where {$_.Subject -eq $subject}).Thumbprint

PS C:\> $thumbprint | clip

In order to create the HTTPS WinRM listener you will have to use a command prompt and not PowerShell as you will receive an error “Error: Invalid use of command line. Type “winrm -?” for help”. In the following example we also delete the default HTTP listener:

C:\> winrm create winrm/config/Listener?Address=*+Transport=HTTPS  @{Hostname=”remotehost.domain.tld”;CertificateThumbprint=”E1ED1974E00A9789DFDD417A690F2C1480FD224A”}

C:\> winrm delete winrm/config/Listener?Address=*+Transport=HTTP

Now that WinRM is actively listening on 5986 we need to modify the firewall rules in PowerShell to reflect our changes. We strongly suggest restricting the remote addresses able to access this service for an additional layer of security:

PS C:\> Get-NetFirewallRule | Where {$_.Name -match “WINRM-HTTP-In-TCP”} | Disable-NetFirewallRule

PS C:\> New-NetFirewallRule -Name “WINRM-HTTPS-In-TCP” -DisplayName “Windows Remote Management (HTTPS-In)” `

-Direction Inbound -Action Allow -Protocol TCP -LocalPort 5986 -RemoteAddress 10.0.0.0/24

Utilizing PowerShell Remoting

Now that we have PowerShell Remoting configured there are several options to remotely connect. These options include the ComputerName parameter, Invoke-Command, PSSession capabilities, and implicit remoting.

ComputerName Parameter

You can run commands on a remote computer by using the ComputerName parameter. Although many cmdlets now offer a ComputerName parameter, there are a limited number of cmdlets with this parameter available. When you use this parameter Windows PowerShell creates a temporary connection that is used for the command and is then closed. Here is an example regarding memory consumption on a remote host:

PS C:\> Get-Process -ComputerName remotehost.domain.tld | sort WorkingSet | select -last 5

Handles  NPM(K)    PM(K)      WS(K) VM(M)   CPU(s)     Id ProcessName

——-  ——    —–      —– —–   ——     — ———–

   4658      93   100208     185996   669            3612 OUTLOOK

   1358     130   178744     214380   631           11912 VpxClient

   5917     104   136148     220148   772            5480 OUTLOOK

    463      32   268112     231492   780           10780 javaw

   7114     108   175692     269376   878            3528 OUTLOOK

Here are all of the commands with the ComputerName parameter available:

PS C:\> Get-Command | ForEach { if ($_.Parameters.Keys -Contains “ComputerName”){$_}} | select name

Name

—-

Add-Computer

Clear-EventLog

Connect-PSSession

Enter-PSSession

Get-EventLog

Get-HotFix

Get-Process

Get-PSSession

Get-Service

Get-WmiObject

Invoke-Command

Invoke-WmiMethod

Limit-EventLog

New-EventLog

New-PSSession

Receive-Job

Receive-PSSession

Register-WmiEvent

Remove-Computer

Remove-EventLog

Remove-PSSession

Remove-WmiObject

Rename-Computer

Restart-Computer

Set-Service

Set-WmiInstance

Show-EventLog

Stop-Computer

Test-Connection

Write-EventLog

I primarily utilize the ComputerName parameter for quickly identifying issues with servers in our local domain or dev environments. Since invoking a cmdlet only creates a temporary connection, any time you need to perform several action items on a remote host I would recommend to utilize PowerShell Sessions (PSSessions).

Invoke-Command

Invoke-Command is perhaps the most versatile way to remotely connect to hosts with PowerShell. You can use this command to execute scriptblocks locally, remotely, through a remote session with or without arguments, or through a connection endpoint URI. Invoke-command can also be used to invoke the same scriptblock on several different servers; we can also identify a filepath to predefined scripts to run remotely. You can also use Invoke-Command on a local computer to evaluate or run a string in a script block as a command. Windows PowerShell converts the script block to a command and runs the command immediately in the current scope, instead of just echoing the string at the command line.The Invoke-Command cmdlet runs commands on a local or remote computer and returns all output from the commands, including errors.

Here is an example of grabbing the configured DNS servers on all interfaces for several servers in your domain:

PS C:\> $server1 = “server1.domain.tld”

PS C:\> $server2 = “server2.domain.tld”

PS C:\> $server3 = “server3.domain.tld”

PS C:\> Invoke-Command -ScriptBlock { Get-DNSClientServerAddress } `

>> -ComputerName $server1,$server2,$server3

>>

InterfaceAlias               Interface Address ServerAddresses                                PSComputerName

                             Index     Family

————–               ——— ——- —————                                ————–

Ethernet                            12 IPv4    {192.168.1.101, 192.168.1.102}                 server1.domain.tld

Ethernet                            12 IPv4    {192.168.1.101, 192.168.1.102}                 server2.domain.tld

Ethernet                            12 IPv4    {192.168.1.101, 192.168.1.102}                 server3.domain.tld

PSSession Capabilities

A PSSession is a persistent PowerShell Session on a local or remote computer. Use a PSSession to run multiple commands that share data, such as a function or the value of a variable. Commands are executed inside of a PSSession using the Invoke-Command. Through PowerShell session options and New-PSSession parameters we can connect to a remote machine to perform the vast majority of our actions, and we can develop very elaborate scripts by connecting to and storing persistent PowerShell sessions.

Invoke-Command and PSSessions

Let’s start with the basics of creating a new session, storing it in a variable, and then issuing a command:

PS C:\> $session = New-PSSession remotehost.domain.tld

PS C:\> Invoke-Command -Session $session -ScriptBlock { (Get-NetAdapter).Name }

vEthernet (Internal Ethernet Port Windows Phone Emulator Internal Switch)

vEthernet (Realtek PCIe GBE Family Controller – Virtual Switch)

Production Interface (95)

That is a very basic example of capturing data from a remote host. Here is a little bit more of an advanced example of how we can capture all IPs from a remote host, and create A records on our domain controller (a different remote host) for all the server IPs:

PS C:\> $creds = Get-Credential

PS C:\> $remote_fqdn = “remotehost.domain.tld”

PS C:\> $dc = “domaincontroller.domain.tld”

PS C:\> $session1 = New-PSSession $remote_fqdn -Credential $creds

PS C:\> $session2 = New-PSSession $dc -Credential $creds

PS C:\> $ips = Invoke-Command -Session $session1 -ScriptBlock {

(Get-NetIPAddress | Where {$_.InterfaceAlias -notmatch “Loopback”}).IPAddress

}

PS C:\> $ips

10.0.1.42

10.0.0.42

172.16.2.1

192.168.1.108

192.168.1.105

PS C:\> Invoke-Command -session $session2 -ArgumentList $remote_fqdn,$ips -scriptblock {

param ($remote_fqdn, $ips)

$name = $remote_fqdn.split(“.”)[0]

ForEach ($ip in $ips){

#Add-DnsServerResourceRecordA -ZoneName domain.tld. -Name $name -IPv4Address $ip

$ip

}

}

Enter-PSSession

Persistent PowerShell sessions are great for scripting purposes, but troubleshooting on the fly and utilizing Invoke-Command and ScriptBlocks is not a practical solution. However, with the Enter-PSSession command you can connect interactively to a PowerShell session on any properly configured remote system. The Enter-PSSession cmdlet starts an interactive session with a remote computer. During the session, the commands that you execute run on the remote computer, just as though you had a local PowerShell sesssion on it (one thing to note is that you can have only one interactive session at a time). Here is an example of connecting to a remote computer:

PS C:\> Enter-PSSession remotehost.domain.tld -Credential (Get-Credential)

In the past we had to launch an RDP client, enter in credentials, wait on the user profile and startup programs to load, and launch a PowerShell session. These actions took much longer than Enter-PSSession. The Enter-PSSession cmdlet is my personal favorite for PowerShell remoting because it is a subsitute for remote desktop and decreases the administrative time to connect to remote hosts.

Implicit Remoting

PowerShell remoting gets even better as you can take it one step further and create PowerShell modules from your PowerShell sessions you create. For example, I created modules for managing our corporate exchange servers (Exchange 2013):

PS C:\> $creds = Get-Credential

PS C:\> $uri = “https://exchange.domain.tld/PowerShell”

PS C:\> $exchange = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionURI $uri -Authentication Basic `

-Credential $creds -SessionOption (New-PSSessionOption -NoMachineProfile -SkipCACheck -SkipCNCheck -SkipRevocationCheck)

PS C:\> Export-PSSession -Session $exchange -CommandName * -OutputModule DataYardExchange -AllowClobber

Managing remote infrastructure becomes significantly easier as we build custom modules for our environment. Implicit remoting and PowerShell modules will not store your password(s) and will prompt you accordingly upon importing the module. Here is an example of modifying DataYard’s corporate exchange environment with the module we recently created:

PS C:\> Import-Module DataYardExchange

PS C:\> Set-Mailbox eric.wright -EmailAddresses @{add=”[email protected]”}

PS C:\> (Get-Mailbox eric.wright).EmailAddresses

smtp:[email protected]

smtp:[email protected]

Another way to take advantage of implicit remoting is by utilizing the cmdlet Import-PSSession. Rather than creating a module we can create a PSSession and then use the Import-PSSession cmdlet to implicitly remote to the server:

PS C:\> $creds = Get-Credential

PS C:\> $uri = “https://exchange.domain.tld/PowerShell”

PS C:\> $exchange = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionURI $uri -Authentication Basic `

-Credential $creds -SessionOption (New-PSSessionOption -NoMachineProfile -SkipCACheck -SkipCNCheck -SkipRevocationCheck)

PS C:\> Import-PSSession $exchange

PowerShell Web Access

The last feature to discuss is PowerShell Web Access (WindowsPowerShellWebAccess). This allows you to execute PowerShell commands through an encrypted website from any web browser. PowerShell Web Access can be configured using the following commands:

PS C:\> Install-WindowsFeature -Name WindowsPowerShellWebAccess -IncludeManagementTools

PS C:\> Install-PswaWebApplication -webApplicationName pswa -UseTestCertificate

PS C:\> Add-PswaAuthorizationRule -UserName domain\user -ComputerName localhost -ConfigurationName Microsoft.PowerShell

PS C:\> start iexplore.exe https://localhost/pswa

In your favorite web browser (Chrome, Firefox, Safari, or IE) you can utilize PowerShell Web Access from anywhere in the world. This is a screenshot of PowerShell Web Access from Safari on an iPhone:

 IMG_6112

I strongly suggest to implement additional layers of security by restricting the IP space that is able to access this feature as well as utilizing additional authentication schemes such as Windows Authentication, Basic Authentication, and/or client certificate verification at the IIS level.

Conclusion

There are many ways to connect to and manage remote systems, PowerShell becoming the most consistent way to manage Window’s infrastructure. As you can see the functionality of persistent PSSessions and modules can become very elaborate. Remote management techniques for Windows’ infrastructure has been constantly improving and I look forward to the incremental improvements.

Recommended Posts