Skip to content

RDS 2012 RDSH Certificate Deployment Script

Technical Article

Replace the self-signed RDP listener certificate on a Windows Server 2012 / 2012 R2 Remote Desktop Session Host with a trusted PFX. The script imports the PFX into the local machine store, captures the thumbprint, and binds it to the RDP-Tcp listener via the Win32_TSGeneralSetting WMI / CIM class so users stop seeing certificate warnings on connect.

Categories
MicrosoftRds 2012
Tags
Pfx CertificateRdsRds CertificateRds Certificate DeploymentRdsh CertificateRdsh Rdp CertificatePowershellWmi
RDS 2012 RDSH Certificate Deployment Script

This script is for administrators of Windows Server 2012 / 2012 R2 Remote Desktop Session Host servers who want to replace the default self-signed RDP listener certificate with a trusted one. After running, the RDP-Tcp listener presents the supplied certificate, and clients connecting to the host stop seeing the "The identity of the remote computer cannot be verified" warning.

What the script does

  • Validates the PFX path before doing anything mutative.
  • Imports the PFX into Cert:\LocalMachine\My and captures the thumbprint.
  • Updates Win32_TSGeneralSetting.SSLCertificateSHA1Hash for the RDP-Tcp listener via the ROOT\CIMV2\TerminalServices namespace.
  • Optionally restarts the Terminal Services service so the new binding is picked up immediately.
  • Designed for standalone RDSH hosts. RDSH joined to a Connection Broker should use Set-RDCertificate -Role RDSessionHost via the broker, shown further down.

PowerShell

#requires -RunAsAdministrator
#requires -Modules PKI, CimCmdlets
<#
.SYNOPSIS
    Imports a PFX into the local machine store on an RDSH server and binds
    it to the RDP-Tcp listener.

.DESCRIPTION
    On a standalone Remote Desktop Session Host, the listener certificate
    is configured via the SSLCertificateSHA1Hash property of the
    Win32_TSGeneralSetting WMI class in ROOT\CIMV2\TerminalServices.

    This script:
      1. Imports the supplied PFX into Cert:\LocalMachine\My.
      2. Captures the resulting thumbprint.
      3. Updates SSLCertificateSHA1Hash on the RDP-Tcp listener via CIM.
      4. Optionally restarts TermService so the change is live.

.PARAMETER PfxPath
    Path to the PFX file (local path recommended, e.g. C:\Cert\rdsh.pfx).

.PARAMETER PfxPassword
    The PFX password as a SecureString.

.PARAMETER RestartService
    When set, restart TermService at the end to force the new certificate
    to be picked up by the listener immediately. Disconnects active RDP
    sessions, so leave off during business hours.

.EXAMPLE
    $pw = Read-Host 'PFX password' -AsSecureString
    Set-RdshListenerCertificate `
        -PfxPath     'C:\Cert\rdsh01.contoso.com.pfx' `
        -PfxPassword $pw `
        -RestartService
#>
function Set-RdshListenerCertificate {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory)]
        [string]$PfxPath,

        [Parameter(Mandatory)]
        [SecureString]$PfxPassword,

        [switch]$RestartService
    )

    # 1. Sanity check the PFX path.
    if (-not (Test-Path -LiteralPath $PfxPath -PathType Leaf)) {
        throw "PFX not found at '$PfxPath'."
    }

    # 2. Import into Cert:\LocalMachine\My and capture the thumbprint.
    Write-Host "Importing $PfxPath into Cert:\LocalMachine\My ..." -ForegroundColor Cyan
    $imported = Import-PfxCertificate `
        -FilePath          $PfxPath `
        -Password          $PfxPassword `
        -CertStoreLocation Cert:\LocalMachine\My `
        -ErrorAction Stop

    $thumbprint = $imported.Thumbprint
    Write-Host ("Imported certificate. Thumbprint: {0}" -f $thumbprint) -ForegroundColor Green

    # 3. Locate the RDP-Tcp listener via the CIM TerminalServices namespace.
    $namespace = 'ROOT\CIMV2\TerminalServices'
    $listener  = Get-CimInstance `
        -Namespace $namespace `
        -ClassName Win32_TSGeneralSetting `
        -Filter    "TerminalName='RDP-Tcp'" `
        -ErrorAction Stop

    if (-not $listener) {
        throw "Could not find the RDP-Tcp listener in $namespace. Is the Remote Desktop Session Host role installed?"
    }

    # 4. Bind the new thumbprint to the listener.
    Write-Host "Binding thumbprint to RDP-Tcp listener ..." -ForegroundColor Cyan
    Set-CimInstance `
        -InputObject $listener `
        -Property    @{ SSLCertificateSHA1Hash = $thumbprint } `
        -ErrorAction Stop

    Write-Host "RDP-Tcp listener now bound to $thumbprint" -ForegroundColor Green

    # 5. Restart TermService so the change is live, if requested.
    if ($RestartService) {
        Write-Warning 'Restarting TermService will disconnect active RDP sessions.'
        Restart-Service -Name 'TermService' -Force -ErrorAction Stop
        Write-Host 'TermService restarted.' -ForegroundColor Green
    }
    else {
        Write-Host 'Skipping service restart. New connections will pick up the new certificate; existing ones keep the previous binding.' -ForegroundColor Yellow
    }
}

How to use it

# Run on the RDSH host, elevated:
$pw = Read-Host 'PFX password' -AsSecureString

. .\Set-RdshListenerCertificate.ps1
Set-RdshListenerCertificate `
    -PfxPath     'C:\Cert\rdsh01.contoso.com.pfx' `
    -PfxPassword $pw `
    -RestartService `
    -Verbose

Modern alternative: Set-RDCertificate via the Connection Broker

If the RDSH host is joined to an RDS 2012 deployment (managed by an RD Connection Broker), the canonical method is to apply the certificate from the broker rather than touching the listener directly. This keeps Server Manager and the deployment view in sync:

#requires -RunAsAdministrator
#requires -Modules RemoteDesktop
$pw = Read-Host 'PFX password' -AsSecureString

Set-RDCertificate `
    -Role             RDSessionHost `
    -ImportPath       'C:\Cert\rdsh01.contoso.com.pfx' `
    -Password         $pw `
    -ConnectionBroker 'rdcb01.contoso.com' `
    -Force

Use the direct WMI approach in this article only on a standalone RDSH that is not joined to a Connection Broker deployment.

Security notes

  • Use a SecureString for the password. Never inline the PFX password in a script committed to source control. Read-Host -AsSecureString or a secrets vault both keep it out of Get-History.
  • Trim the certificate's private key permissions. After import, the private key ACL should allow NETWORK SERVICE read access; remove any inherited Authenticated Users entry on the key file under C:\ProgramData\Microsoft\Crypto\RSA\MachineKeys.
  • Plan the restart. Restarting TermService disconnects everyone. Either run the change during a maintenance window with -RestartService, or skip the switch and let new sessions pick up the binding while existing ones drain.
  • Delete the PFX after deployment. Once imported into the local machine store the file is no longer needed on disk.
  • Rollback is just a re-bind. If a renewal goes wrong, re-run the script with the previous PFX (or set SSLCertificateSHA1Hash back to the old thumbprint) to restore the previous binding.

Original 2014 script

The original 2014 download was hosted on TechNet Gallery (now retired). It prompted for the PFX path, password, and thumbprint and then bound the listener via wmic / WMI. The function above reproduces that behaviour using Import-PfxCertificate (so the thumbprint is captured automatically rather than re-typed) and Set-CimInstance against the same Win32_TSGeneralSetting class in ROOT\CIMV2\TerminalServices.