Automatic HTTPS WinRM Listener creation
Automatic HTTPS WinRM Listener creation

In this post I will show you how to create a HTTPS WinRM listener that is using a certificate issued by your own MS PKI.

The requirements are the following:
– An Active Directory domain
– An Enterprise MS PKI
– Powershell 5

The first step is to create a certificate template that will be used to create the WinRM listener certificates on your machines.

Open a mmc console, load the snap-in Certificate template, duplicate the Web server cert template and configure the new template as shown below:

In the following example, I have selected a computer object as a target of my certificate deployment. In your env, you can use your own AD group with the following permissions:

Now you can open your CA mmc snap-in and publish this new template:

In that example, I have used the default Domain Policy. It is only as an example. As a best pratice, you will have to create a new GPO with these settings:

After running the command gpupdate /force on the target computer, you will receive the new WinRM cert based on the template we have created above:

So now, we have :
– created the cert template
– publish the template on the Active Directory domain
– auto enroll a computer = deploy the new certificate based on the WinRM cert template

Script syntax
PS:\> winrmScript.ps1 -WinRMCertTemplateName “WinRM Listener”

param (
	[string]$WinRMCertTemplateName
)

function set_WinRMHTTPSListener($tp,$cn){
	$selector_set = @{
		Address = "*"
		Transport = "HTTPS"
	}
	$value_set = @{
		CertificateThumbprint = $tp
		Hostname = $cn
	}
	
	New-WSManInstance -ResourceURI "winrm/config/Listener" -SelectorSet $selector_set -ValueSet $value_set
}

function get_InstalledCertificateInfo($loc) {
	#build an array with the certificate thumbprint, CN and the template used to generate the cert if available
	$array = gci Cert:\$loc | `
		# get thumbprint
		select Thumbprint , `
		# extract the CN
		@{n="CN";e={($_.Subject).split("=")[1]}} , `
		# get the cert Template Name (Template=TEMPLATE NAME(OID) )
		@{n="IssuedfromTemplate";e={[regex]::match( ($_.extensions.Format(0) | ? { $_ -match "Template" } ).split(",")[0] , '^(Template\=)(\w.*)(\([0-9.]*(\)))$' ).Groups[2].value}}
	return $array
}

function get_WinRMListeners(){
	return (gci -Path WSMan:\localhost\Listener -recurse)
}

function test_WinRMListeners($Wlist,$WPropName,$WlistType){
	return ($Wlist |? {$_.PSChildName -eq $WPropName -and $_.Value -eq $WlistType}).PSParentPath
}

$certLocation = "LocalMachine\My"
$allOK = $false
$WinRMcertFound = $false

# check if the Corporate WinRM Listener certificate has been deployed successfully
do {

	if (get_InstalledCertificateInfo $certLocation | ? {$_.IssuedfromTemplate -eq $WinRMCertTemplateName}){
		$WinRMCorporateCertThumbprint = ((get_InstalledCertificateInfo $certLocation | ? {$_.IssuedfromTemplate -eq $WinRMCertTemplateName}).Thumbprint).replace(" ","").toupper()
		$WinRMCorporateCertCN = (get_InstalledCertificateInfo $certLocation | ? {$_.IssuedfromTemplate -eq $WinRMCertTemplateName}).CN
		write-host "WinRM Certificate found (thumbprint = $WinRMCorporateCertThumbprint)" -ForegroundColor Green
		$WinRMcertFound = $true
	}
	else {
		write-host -ForegroundColor Cyan "The certificate has not been deployed on this machine. A group policy update will be performed."
		write-host -ForegroundColor Cyan "In case of problem ask assistance to your PKI admin or CTRL-C to stop`n"
		write-host -ForegroundColor Magenta "Running GPUPDATE /FORCE..."
		gpupdate /force
	}
	
} until ($WinRMcertFound -eq $true)

do {
	# get WinRM listeners
	$winRMListeners = get_WinRMListeners
	
	# if existing WinRM listener(s) already exist(s)...
	if ($winRMListeners) {
		# ...if exists, delete HTTP WinRM Listeners
		$WinRMHTTPListener_Name = test_WinRMListeners $winRMListeners "Transport" "HTTP"
		if ($WinRMHTTPListener_Name) {
			write-host -ForegroundColor Yellow "WinRM HTTP Listener configured: Deleting..."
			Remove-Item $WinRMHTTPListener_Name -force -recurse
		}
		
		# ... if HTTPS WinRM listeners exist check to be sure it uses the correct certificate and the correct TCP port 5986
		$WinRMHTTPSSLListener_Name = test_WinRMListeners $winRMListeners "Transport" "HTTPS"
		$WinRMHTTPSSLListener_Port = test_WinRMListeners $winRMListeners "Port" "5986"
		if ($WinRMHTTPSSLListener_Name -and $WinRMHTTPSSLListener_Port) {
			# get WinRM Listener cert thumbprint
			$WinRMListenerCertThumbprint = ((gci $WinRMHTTPSSLListener_Name\CertificateThumbprint).value).replace(" ","").toupper()
			
			if ($WinRMListenerCertThumbprint -ne $WinRMCorporateCertThumbprint) {
				write-host -ForegroundColor Yellow "WinRM HTTPS Listener already configured but with a non-Corporate certificate: Deleting..."
				remove-item $WinRMHTTPSSLListener_Name -force -recurse
				$allOK = $false
			}
			else {
				write-host -ForegroundColor Green "WinRM Listener configuration OK (corporate certificate + listening port)"
				$allOK = $true
			}
		}
		else {
			# The WinRM Listener found is not properly configured
			# Reset the WinRM configuration
			$process = 'cmd.exe'
			$arguments = '/c winrm invoke restore winrm/config @{}'
			start-process $process -ArgumentList $arguments -Wait
			$allOK = $false
		}
	}
	
	if ($allOK -eq $false){
		# create a HTTPS WinRM Listener with the Corporate WinRM certificate
		set_WinRMHTTPSListener $WinRMCorporateCertThumbprint $WinRMCorporateCertCN
		get_WinRMListeners |? {$_.PSChildName -eq "Transport" -and $_.Value -eq "HTTPS"}
	}
} until ($allOK -eq $true)


Before launching the script, we can create a simple HTTP WinRM listener. This listener will be automatically deleted by the script. The script will do the same on the target machine if the WinRM listener has been configured with HTTPS but with a certificate not issued using our template.

Let’s test :
– create the HTTP WinRM listener and check it has been created as expected

PS C:\temp> ls


Directory: C:\temp


Mode LastWriteTime Length Name
---- ------------- ------ ----
-a---- 6/3/2018 10:47 AM 4209 set_winrmlistener.ps1


PS C:\temp> winrm qc
WinRM service is already running on this machine.
WinRM is not set up to allow remote access to this machine for management.
The following changes must be made:

Create a WinRM listener on HTTP://* to accept WS-Man requests to any IP on this machine.

Make these changes [y/n]? y

WinRM has been updated for remote management.

Created a WinRM listener on HTTP://* to accept WS-Man requests to any IP on this machine.
PS C:\temp> winrm enumerate winrm/config/Listener
Listener
Address = *
Transport = HTTP
Port = 5985
Hostname
Enabled = true
URLPrefix = wsman
CertificateThumbprint
ListeningOn = 127.0.0.1, 192.168.122.74, ::1, fe80::5efe:192.168.122.74%6, fe80::ffff:ffff:fffe%4, fe80::6093:d6fe:b7c3:31fc%5

PS C:\temp> gci -path WSMan:\localhost\Listener


WSManConfig: Microsoft.WSMan.Management\WSMan::localhost\Listener

Type Keys Name
---- ---- ----
Container {Transport=HTTP, Address=*} Listener_1682839512


PS C:\temp> gci -path WSMan:\localhost\Listener\Listener_1682839512


WSManConfig: Microsoft.WSMan.Management\WSMan::localhost\Listener\Listener_1682839512

Type Name SourceOfValue Value
---- ---- ------------- -----
System.String Address *
System.String Transport HTTP
System.String Port 5985
System.String Hostname
System.String Enabled true
System.String URLPrefix wsman
System.String CertificateThumbprint
System.String ListeningOn_1770022257 127.0.0.1
System.String ListeningOn_448445659 192.168.122.74
System.String ListeningOn_1414502903 ::1
System.String ListeningOn_648997042 fe80::5efe:192.168.122.74%6
System.String ListeningOn_2018006527 fe80::ffff:ffff:fffe%4
System.String ListeningOn_489914956 fe80::6093:d6fe:b7c3:31fc%5

We are now ready to run the script on the target computer to create the HTTPS WinRM listener:

As you see in the screenshot above, the HTTP listener has been deleted and the new HTTPS listener has been created using the certificate issued by the PKI.

Do not hesitate to leave a comment or ask if you have any questions.

<>

My Powershell script categories

Automatic HTTPS WinRM Listener creation

Leave a Reply

Your email address will not be published.