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:
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:
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.