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