The Install-RDPCert function can be used to replace a computer's self-signed RDP certificate with a trusted certificate, either from a third-party certificate authority, or from an organization's internal certificate authority. The certificate must be in PFX format and protected with a password.

This script was inspired by Ryan Mangan's RDS 2012 Session Host Certificate Configuration script - https://gallery.technet.microsoft.com/RDS-2012-Session-Host-fbb54ff9.

I improved upon Ryan's script by allowing you to push the certificate to multiple remote computer simultaneously, as opposed to having to run the script locally on each RDSH server.

You need to supply the following information to the script:

 

Enjoy!

 

Tom Murphy

http://blog.tmurphy.org

PowerShell
Edit|Remove
#Requires -Version 4.0 
#Requires -Modules PKI 
 
<# 
.SYNOPSIS 
   Replaces RDP certificate on local or remote computers 
 
.DESCRIPTION 
   This function replaces the certificate used by Remote Desktop Protocol on computers, including remote computers. 
 
.EXAMPLE 
   $SecurePass = ConvertTo-SecureString -String "P4$$w0rd9" -AsPlainText -Force 
   Install-RDPCertificate -ComputerName RDSHServer1 -FilePath D:\WildcardCert.pfx -Password $SecurePass 
 
   This example shows how to push a certificate located on the local machine to a remote computer. 
 
.EXAMPLE 
   $SecurePass = ConvertTo-SecureString -String "P4$$w0rd9" -AsPlainText -Force 
   Install-RDPCertificate -ComputerName RDSHServer1,RDSHServer2,RDSHServer3 -FilePath D:\WildcardCert.pfx -Password $SecurePass 
 
   This example shows how to push a certificated located on the local machine to multiple remote computers. 
 
.EXAMPLE 
   $SecurePass = ConvertTo-SecureString -String "P4$$w0rd9" -AsPlainText -Force 
   Get-RDServer -Role RDS-RD-SERVER | Install-RDPCertificate -FilePath D:\WildcardCert.pfx -Password $SecurePass 
 
   This example uses the Get-RDServer cmdlet to get a list of all RD Session Host servers in an RDS deployment, and then apply a certificate to them. 
 
.NOTES 
   Created by Tom Murphy  
   Last modified 4/15/2016  
   http://blog.tmurphy.org 
  
   Inspired by Ryan Mangan's RDS 2012 Session Host Certificate Configuration script.  
   https://gallery.technet.microsoft.com/RDS-2012-Session-Host-fbb54ff9  
  
.LINK  
   http://blog.tmurphy.org 
#> 
function Install-RDPCertificate 
{ 
    [CmdletBinding( 
        SupportsShouldProcess=$true, 
        ConfirmImpact="Medium" 
    )] 
     
    Param 
    ( 
        # One or more computers that the certificate should be installed to. Can accept pipeline input. 
        [Parameter(Mandatory=$false,  
                   ValueFromPipeline=$true, 
                   ValueFromPipelineByPropertyName=$true,  
                   ValueFromRemainingArguments=$false,  
                   Position=0, 
                   ParameterSetName='ParamSet1')] 
        [ValidateNotNullOrEmpty()] 
        [Alias("Computer")] 
        [Alias("Server")] 
        [string[]]$ComputerName = $env:COMPUTERNAME, 
 
        # Path to a certificate exported in PFX format. The exported certificate must be secured with a password. 
        [Parameter(Mandatory=$true, 
                   ValueFromPipeline=$false, 
                   ValueFromRemainingArguments=$false, 
                   Position=1, 
                   ParameterSetName='ParamSet1')] 
        [ValidateNotNullOrEmpty()] 
        [ValidateScript({ 
                if ($_ -like "*.pfx"){ 
                    $true 
                } else { 
                    Throw "Certificate must be in *.PFX format!" 
                }})] 
        [Alias("CertificatePath")] 
        [Alias("PFX")] 
        [string]$FilePath, 
 
        # Password used to unlock the certificate PFX. Must be formatted as a SecureString. 
        [Parameter(Mandatory=$true, 
                   ValueFromPipeline=$false, 
                   ValueFromRemainingArguments=$false, 
                   Position=2, 
                   ParameterSetName='ParamSet1')] 
        [ValidateNotNullOrEmpty()] 
        [System.Security.SecureString]$Password 
    ) # end param block 
 
    Begin 
    { 
        # Import modules 
        Import-Module PKI -Verbose:$false 
 
        # Check to ensure certificate exists at target path 
        if (-not (Test-Path $FilePath)){ 
            # File does not exist 
            throw "Certificate not found at target location $FilePath" 
        } # end if 
 
        # Get the thumbprint from the certificate 
        try 
        { 
            $Thumbprint = (Get-PfxData -FilePath $FilePath -Password $Password).EndEntityCertificates.Thumbprint 
            Write-Verbose "Certificate thumbprint: $Thumbprint" 
        } 
        catch [System.Exception] 
        { 
            # Cannot get thumbprint from certificate, password is likely invalid 
            throw "Access denied to certificate - ensure the password is correct" 
        } # end try/catch 
 
        # Create array to hold output object 
        $Output = @() 
    } # end Begin block 
    Process 
    { 
        foreach($Computer in $ComputerName){ 
            # Set -WhatIf information 
            if($PSCmdlet.ShouldProcess("$Computer","Apply certificate '$(Split-Path -Path $FilePath -Leaf)' to RDP listener")){ 
                # Ensure target computer can be reached 
                if (-not (Test-Connection $Computer -Count 1 -Quiet)){ 
                    # Computer cannot be pinged 
                    Write-Warning "[$Computer] - Cannot ping target computer" 
                    Continue 
                } # end if 
 
                # Create hashtable to hold output object properties and add default properties 
                $hashtable = @{} 
                $hashtable.ComputerName = $Computer 
 
                # Get WMI object of Win32_TSGeneralSetting 
                try 
                { 
                    $WMIObject = Get-WmiObject -Class "Win32_TSGeneralSetting" -Namespace root\cimv2\terminalservices -Filter "TerminalName='RDP-tcp'" -ComputerName $Computer -Authentication 6 
                    if ($WMIObject){ 
                        if ($WMIObject.SSLCertificateSHA1Hash -eq $Thumbprint){ 
                            # Thumbprint already matches, no need to continue 
                            Write-Verbose "[$Computer] - The certificate thumbprint already matches the new certificate" 
                            Continue 
                        } # end if 
                    } else { 
                        # WMI query did not return any results 
                        Write-Warning "[$Computer] - Could not query WMI class Win32_TSGeneralSetting" 
                        Continue 
                    } # end if 
                } 
                catch 
                { 
                    # Error connecting to WMI 
                    Write-Warning "[$Computer] - Unable to connect to WMI`: $($Error[0])" 
                    Continue 
                } # end try/catch 
 
                Write-Verbose "[$Computer] - Original thumbprint`: $($WMIObject.SSLCertificateSHA1Hash)" 
                $hashtable.OriginalThumbprint = $WMIObject.SSLCertificateSHA1Hash 
 
                # Get $env:SystemRoot path from remote computer and build remote path variables 
                $RemoteSystemRoot = Invoke-Command -ComputerName $Computer -ScriptBlock {$env:SystemRoot.replace(":","$")} 
                $RemoteFilePath = "\\" + $Computer + "\" + $RemoteSystemRoot + "\Temp" 
                $RemoteCert = ($RemoteSystemRoot.replace("$",":")) + "\Temp\" + (Split-Path $FilePath -Leaf) 
         
                # Push certificate to computer 
                Write-Verbose "[$Computer] - Pushing certificate to $RemoteCert" 
                Copy-Item -Path $FilePath -Destination $RemoteFilePath 
         
                # Import certificate on remote machine 
                Write-Verbose "[$Computer] - Importing certificate to LocalMachine store" 
                try 
                { 
                    $SessionOptions = New-PSSessionOption -OperationTimeout 60000 -IdleTimeout 60000 -OpenTimeout 60000 
                    Invoke-Command -ComputerName $Computer -ScriptBlock {param($FilePath,$Password) Import-PfxCertificate -FilePath $FilePath -Password $Password -CertStoreLocation Cert:\LocalMachine\My} -ArgumentList $RemoteCert,$Password -SessionOption $SessionOptions -ErrorAction Continue | Out-Null 
                    Write-Verbose "[$Computer] - Import successful" 
                } 
                catch 
                { 
                    # Unable to import the certificate 
                    Write-Warning "[$Computer] - Unable to import certificate into store`: $($Error[0])" 
                    Continue 
                } # end try/catch 
 
                # Set the certificate and permissions 
                Write-Verbose "[$Computer] - Applying the certificate to computer" 
                try 
                { 
                    # Apply the certificate to WMI 
                    $WMIObject.SSLCertificateSHA1Hash = $Thumbprint 
                    $WMIObject.Put() | Out-Null 
                    Write-Verbose "[$Computer] - Certificate applied successfully" 
                 
                    # Set the appropriate permissions to the private key so RDP may access it 
                    $ScriptBlock = { 
                        $FilePath = "C:\ProgramData\Microsoft\Crypto\RSA\MachineKeys" 
                        $File = Get-ChildItem $FilePath | Sort-Object LastWriteTime -Descending | Select-Object -First 1 
                        # Specify account 
                        $Account = "NT AUTHORITY\NETWORK SERVICE" 
                        # Get current ACL on the private key 
                        $ACL = Get-Acl -Path $File.FullName 
                        # Set new rule 
                        $rule = New-Object System.Security.AccessControl.FileSystemAccessRule("$Account""Read""Allow") 
                        # Add rule to the ACL 
                        $ACL.AddAccessRule($rule) 
                        # Set new ACL to the private key 
                        Set-Acl -Path $File.FullName -AclObject $ACL 
                    } # end $ScriptBlock 
                    Invoke-Command -ComputerName $Computer -ScriptBlock $ScriptBlock -ArgumentList $FilePath -SessionOption $SessionOptions 
                    Write-Verbose "[$Computer] - Set private key permissions successfully" 
                } 
                catch 
                { 
                    # Unable to apply the certificate 
                    Write-Warning "[$Computer] - Unable to apply certificate`: $($Error[0])" 
                    Continue 
                } # end try/catch 
                Write-Verbose "[$Computer] - Certificate applied successfully" 
 
                # Delete pfx certificate file from remote machine 
                try 
                { 
                    Remove-Item -Path ($RemoteFilePath + '\' + (Split-Path $FilePath -Leaf)) -Force 
                    Write-Verbose "[$Computer] - Deleted certificate file successfully" 
                } 
                catch 
                { 
                    Write-Warning "[$Computer] - Unable to delete PFX file from remote machine" 
                } # end try/catch 
 
                # Everything was successful, add properties to hashtable 
                $hashtable.NewThumbprint = $Thumbprint 
 
                # Create new object and add to object array 
                $Object = New-Object -TypeName psobject -Property $hashtable 
                $Output +$Object 
            } # end $PSCmdlet.ShouldProcess 
        } # end foreach 
    } # end Process block 
    End 
    { 
        # Write output object to the pipeline 
        Write-Output $Output | Select-Object ComputerName, OriginalThumbprint, NewThumbprint 
    } # end End block 
} #end function