Enabling HTTPS for PowerShell Remoting in Windows Server 2012 R2

powershell hero

In today’s Ask the Admin, I’ll kill two birds with one stone by showing you how to enable secure PowerShell Remoting in Azure virtual machines (VMs) and on-premises servers running Windows Server 2012 R2.

PowerShell Remoting uses encryption to secure communications between devices, regardless of whether HTTPS is deployed as the transport. But outside of a domain environment where the Kerberos authentication protocol provides a trust relationship between computers, PowerShell Remoting could be subject to man-in-the-middle attacks. To thwart these kinds of threats, Microsoft recommends using HTTPS as the transport for PowerShell Remoting when a session is initiated from a workgroup device.

Azure VMs deployed using the classic deployment model have PowerShell Remoting enabled and are securely configured by default. With that model, you could download a certificate from the management portal, install it on your device and then make a secure connection to an Azure VM.

But that’s no longer the case for VMs deployed using Azure Resource Manager (ARM), where only RDP connections are permitted by default. In this article, I’ll show you how to generate a self-signed certificate for the purposes of establishing SSL connections. While self-signed certificates are useful in lab environments, if you need HTTPS in a production environment, certificates should either be issued by your own Certification Authority (CA) or by a public CA.

Add a Rule to an Azure Network Security Group

If the Azure VM you want to manage was deployed using the Resource Manager deployment model in the management portal, you’ll need to start by adding a rule to the Network Security Group (NSG) to allow inbound connections on port 5986. If you deployed the VM using Resource Manager, but using PowerShell ARM cmdlets or a JSON template, it may be that no NSG exists, in which case you can skip this section. If you want to manage an on-premises server using a device on the Internet, you’ll need to add an inbound rule to your network-edge firewall.

Add a rule to an Azure Network Security Group (NSG) (Image Credit: Russell Smith)
Add a rule to an Azure Network Security Group (NSG) (Image Credit: Russell Smith)

If you haven’t yet installed Azure PowerShell 1.0 (or higher), read Install Azure PowerShell 1.0 Preview on Petri. Open Windows PowerShell ISE, and log in to your Microsoft account using the Login-AzureRmAccount cmdlet.

Login-AzureRmAccount

# Select a subscription

$subscriptionId = (Get-AzureRmSubscription | Out-GridView -Title 'Select Azure Subscription:' -PassThru).SubscriptionId
Select-AzureRmSubscription -SubscriptionId $subscriptionId

# Select a Resource Group

$rgName = (Get-AzureRmResourceGroup | Out-GridView -Title 'Select Azure Resource Group:' -PassThru).ResourceGroupName

Run the rest of the code above, and select the Azure subscription and Resource Group where your VM is located.

Next, I’ll define a variable with the name of the NSG that I want to modify. NSGs are most commonly used to control inbound and outbound network traffic to subnets. The Get-AzureRmNetworkSecurityGroup, Add-AzureRmNetworkSecurityRuleConfig, and Set-AzureRmNetworkSecurityGroup cmdlets are then used to add a new rule and update the NSG. You can see that the rule specified in the Add-AzureRmNetworkSecurityRuleConfig cmdlet allows inbound TCP traffic on port 5986 from any device.

# Set the NSG name

$nsgName = 'NSG1'

# Add rule to existing NSG

$nsg = Get-AzureRmNetworkSecurityGroup -Name $nsgName -ResourceGroupName $rgName
$nsg | Add-AzureRmNetworkSecurityRuleConfig -Name 'default-winrm-https' -Direction Inbound -Priority 1001 -Access Allow -SourceAddressPrefix '*'  -SourcePortRange '*' -DestinationAddressPrefix '*' -DestinationPortRange 5986 -Protocol Tcp 
$nsg | Set-AzureRmNetworkSecurityGroup

# Display custom security rules

(Get-AzureRmNetworkSecurityGroup -Name $nsgName -ResourceGroupName $rgName).SecurityRules
List the security rules in an Azure Network Security Group (Image Credit: Russell Smith)
List the security rules in an Azure Network Security Group (Image Credit: Russell Smith)

For confirmation, I use the Get-AzureRmNetworkSecurityGroup cmdlet once more to display the custom security rules for the NSG. You should see the new rule in the output.

Generate and Install a Self-Signed Certificate

Now I need to RDP to the server and install a certificate. This process is the same for local and Azure-based VMs. Open a PowerShell prompt with local administrator privileges on the server and run the code block below.

Create a self-signed certificate using PowerShell (Image Credit: Russell Smith)
Create a self-signed certificate using PowerShell (Image Credit: Russell Smith)
# Run on the remote server with admin privileges

mkdir C:\temp
$Cert = New-SelfSignedCertificate -CertstoreLocation Cert:\LocalMachine\My -DnsName adVM.ad.contoso.com
Export-Certificate -Cert $Cert -FilePath C:\temp\cert

Here I use the New-SelfSignedCertificate cmdlet (PowerShell 4.0 and later) to generate and install the certificate on the server. Note that the -DnsName parameter should contain the name that will be resolved by DNS when initiating a remote connection to the server. This usually means specifying the server’s Fully Qualified Domain Name (FQDN). Export-Certificate is then used to export the certificate as a file to the temp directory. You’ll need to copy this file and import it on the device where you’ll initiate PowerShell Remoting sessions to the server.

Create a WinRM HTTPS Listener

Now that we have a certificate, all that’s left to do is use the New-Item cmdlet to create an HTTPS listener and the New-NetFirewallRule cmdlet to open port 5986 in Windows Firewall to allow inbound connections for PowerShell Remoting.

Create a WinRM HTTPS listener and new Windows Firewall rule (Image Credit: Russell Smith)
Create a WinRM HTTPS listener and new Windows Firewall rule (Image Credit: Russell Smith)
# Set up WinRM HTTPS listener

New-Item -Path WSMan:\LocalHost\Listener -Transport HTTPS -Address * -CertificateThumbPrint $Cert.Thumbprint –Force
New-NetFirewallRule -DisplayName 'Windows Remote Management (HTTPS-In)' -Name 'Windows Remote Management (HTTPS-In)' -Profile Any -LocalPort 5986 -Protocol TCP

Remove HTTP Listeners

If you want to ensure that only HTTPS can be used for Windows Remote Management (WinRM), you should remove any existing HTTP listeners. You can use the winrm command to enumerate the listeners configured on the device. Here I use Get-ChildItem cmdlet to enumerate and remove all HTTP listeners. You might also want to remove firewall or NSG rules created for WinRM HTTP if they exist.

# Remove HTTP listener (optional)

Winrm enumerate winrm/config/listener
Get-ChildItem WSMan:\Localhost\listener | Where -Property Keys -eq 'Transport=HTTP' | Remove-Item -Recurse

Import the Certificate and Establish a Remote Connection

Now that the server is configured, use the Import-Certificate cmdlet to copy and import the certificate file (cert) generated in the steps above to the device where you’ll initiate PowerShell Remoting sessions. You’ll need to run a PowerShell prompt with local administrator privileges to import the certificate and make it available for all users of the device.

Import the self-signed certificate on the management PC (Image Credit: Russell Smith)
Import the self-signed certificate on the management PC (Image Credit: Russell Smith)
# Copy the cert file to the local PC and run commands below with admin privileges

Import-Certificate -Filepath 'C:\temp\cert' -CertStoreLocation 'Cert:\LocalMachine\Root'

# Skip Certification Authority (CA) check

$so = New-PsSessionOption –SkipCACheck

# Establish a POSH Remoting session

Enter-PSSession -Computername adVM.ad.contoso.com -Credential (Get-Credential) -UseSSL -SessionOption $so

Finally, use the Enter-PSSession cmdlet to establish a remote connection to the server. I skip the CA check because the imported certificate is self-signed and as such isn’t trusted by the device because it wasn’t issued by a known CA. Note also the use of the -UseSSL parameter to force the use of HTTPS.

Establish a PowerShell Remoting session over HTTPS (Image Credit: Russell Smith)
Establish a PowerShell Remoting session over HTTPS (Image Credit: Russell Smith)

If the PowerShell Remoting session is established successfully, you should see the prompt change in the console window to indicate that you are now connected to a session running on the remote server.