Script to Import and Bind a Certificate to the Default Web Site

by Jeremy Saunders on January 4, 2015

SSL CertificateThis Powershell script will import and bind a certificate to the Default Web Site. I use this script for Citrix StoreFront and Director deployments, but it’s written to be very flexible and versatile so can be used for other tasks.

The original idea came from scripts written by Thomas Albaek and Jerome Quief for Citrix StoreFront.

The way I’ve written this script it will actually remove any existing certificate bindings first, which is really handy for pushing out updated certificates as I found that other scripts written to do this task would fail when run more than once.

Review the documentation within the script to understand the syntax.

Here is the Install-Certificate.ps1 (1210 downloads) script:

<#
  This script will import and bind a certificate to the Default
  Web Site for use with Citrix StoreFront, etc.

  The original idea came from scripts written by Thomas Albaek and
  Jerome Quief:
  - http://www.albaek.org/automatic-installation-of-citrix-storefront-2-6/
  - https://jeromequief.wordpress.com/2014/06/11/storefront-2-5-unattended-install-and-config/

  I have enhanced and modernized it. You can either pass parameters
  to it, or hardcode them. The nice thing about this script is that
  it can also be used to remove/update certificates.

  Syntax examples:

    Using hardcoded variables:
      Install-Certificate.ps1

    Passing parameters:
      Install-Certificate.ps1 -PFXPath:".\star_jhouseconsulting_com.pfx" -PFXPassword:"notT3LL1ngu" -CertSubject:"CN=*.jhouseconsulting.com"

    The ExcludeLocalServerCert is optional, and is forced to $True
    if left off. You really never want this set to false, especially
    if using a wildcard certificate. It's there mainly for flexibility.

    If the password contains a $ sign, you must escape it with the `
    character.

  Script Name: Install-Certificate.ps1
  Release 1.0
  Written by Jeremy@jhouseconsulting.com 21st December 2014

  Note: This script has been tested thoroughly on Windows 2012R2
        (IIS 8.5). Due to the cmdlets used I cannot guarantee full
        backward compatibility.

  A log file will either be written to %windir%\Temp or to the
  %LogPath% Task Sequence variable if running from an SCCM\MDT
  Task.

#>

#-------------------------------------------------------------
param([String]$PFXPath,[String]$PFXPassword,[String]$CertSubject,[switch]$ExcludeLocalServerCert)

# Set Powershell Compatibility Mode
Set-StrictMode -Version 2.0

$ScriptPath = {Split-Path $MyInvocation.ScriptName}

if ([String]::IsNullOrEmpty($PFXPath)) {
  $PFXPath = $(&$ScriptPath) + "\star_jhouseconsulting_com.pfx"
}

if ([String]::IsNullOrEmpty($PFXPassword)) {
  $PFXPassword = "notT3LL1ngu"
}

if ([String]::IsNullOrEmpty($CertSubject)) {
  $CertSubject = "CN=*.jhouseconsulting.com"
}

if (!($ExcludeLocalServerCert.IsPresent)) {
  $ExcludeLocalServerCert = $True
}

# Set to the Web Site
$sitename = "Default Web Site"

# Set to the Port number
$port = 443

#-------------------------------------------------------------

Function IsTaskSequence() {
  # This code was taken from a discussion on the CodePlex PowerShell
  # App Deployment Toolkit site. It was posted by mmashwani.
  Try {
      [__ComObject]$SMSTSEnvironment = New-Object -ComObject Microsoft.SMS.TSEnvironment -ErrorAction 'SilentlyContinue' -ErrorVariable SMSTSEnvironmentErr
  }
  Catch {
  }
  If ($SMSTSEnvironmentErr) {
    Write-Verbose "Unable to load ComObject [Microsoft.SMS.TSEnvironment]. Therefore, script is not currently running from an MDT or SCCM Task Sequence."
    Return $false
  }
  ElseIf ($null -ne $SMSTSEnvironment) {
    Write-Verbose "Successfully loaded ComObject [Microsoft.SMS.TSEnvironment]. Therefore, script is currently running from an MDT or SCCM Task Sequence."
    Return $true
  }
}

#-------------------------------------------------------------

$invalidChars = [io.path]::GetInvalidFileNamechars()
$datestampforfilename = ((Get-Date -format s).ToString() -replace "[$invalidChars]","-")

# Get the script path
$ScriptPath = {Split-Path $MyInvocation.ScriptName}
$ScriptName = [System.IO.Path]::GetFilenameWithoutExtension($MyInvocation.MyCommand.Path.ToString())
$Logfile = "$ScriptName-$($datestampforfilename).txt"
$logPath = "$($env:windir)\Temp"

If (IsTaskSequence) {
  $tsenv = New-Object -COMObject Microsoft.SMS.TSEnvironment
  $logPath = $tsenv.Value("LogPath")

  $UserDomain = [System.Text.Encoding]::ASCII.GetString([System.Convert]::FromBase64String($tsenv.Value("UserDomain")))
  $UserID = [System.Text.Encoding]::ASCII.GetString([System.Convert]::FromBase64String($tsenv.Value("UserID")))
  $UserPassword = [System.Text.Encoding]::ASCII.GetString([System.Convert]::FromBase64String($tsenv.Value("UserPassword")))
}

$logfile = "$logPath\$Logfile"

# Start the logging
Start-Transcript $logFile
Write-Output "Logging to $logFile"

#-------------------------------------------------------------

Write-Output "Start Certificate Installation"

Write-Output "Loading the Web Administration Module"
try{
    Import-Module webadministration
}
catch{
    Write-Output "Failed to load the Web Administration Module"
}

Write-Output "Deleting existing certificate from Store"
try{
    $cert = Get-ChildItem cert:\LocalMachine\MY | Where-Object {$_.subject -like "$CertSubject*" -AND $_.Subject -notmatch "CN=$env:COMPUTERNAME"}
    $thumbprint = $cert.Thumbprint.ToString()
    If (Test-Path "cert:\localmachine\my\$thumbprint") {
      Remove-Item -Path cert:\localmachine\my\$thumbprint -DeleteKey
    }
}
catch{
    Write-Output "Unable to delete existing certificate from store"
}

Write-Output "Running certutil to import certificate into Store"
try{
    $ImportError = certutil.exe -f -importpfx -p $PFXPassword $PFXPath
}
catch{
    Write-Output "certutil failed to import certificate: $ImportError"
}

Write-Output "Locating the cert in the Store"
try{
    If ($ExcludeLocalServerCert) {
      $cert = Get-ChildItem cert:\LocalMachine\MY | Where-Object {$_.subject -like "$CertSubject*" -AND $_.Subject -notmatch "CN=$env:COMPUTERNAME"}
    } Else {
      $cert = Get-ChildItem cert:\LocalMachine\My | Where-Object {$_.subject -like "$CertSubject*"}
    }
    $thumbprint = $cert.Thumbprint.ToString()
    Write-Output $cert
}
catch{
    Write-Output "Unable to locate cert in certificate store"
}

Write-Output "Removing any existing binding from the site and SSLBindings store"
try{
  # Remove existing binding form site
  if ($null -ne (Get-WebBinding -Name $sitename | where-object {$_.protocol -eq "https"})) {
    $RemoveWebBinding = Remove-WebBinding -Name $sitename -Port $Port -Protocol "https"
    Write-Output $RemoveWebBinding
  }
  # Remove existing binding in SSLBindings store
  If (Test-Path "IIS:\SslBindings\0.0.0.0!$port") {
    $RemoveSSLBinding = Remove-Item -path "IIS:\SSLBindings\0.0.0.0!$port"
    Write-Output $RemoveSSLBinding
  }
}
catch{
    Write-Output "Unable to remove existing binding"
}

Write-Output "Bind your certificate to IIS HTTPS listener"
try{
  $NewWebBinding = New-WebBinding -Name $sitename -Port $Port -Protocol "https"
  Write-Output $NewWebBinding
  $AddSSLCertToWebBinding = (Get-WebBinding $sitename -Port $Port -Protocol "https").AddSslCertificate($thumbprint, "MY")
  Write-Output $AddSSLCertToWebBinding
}
catch{
    Write-Output "Unable to bind cert"
}

Write-Output "Completed Certificate Installation"

# Stop logging
Stop-Transcript

Enjoy!

Jeremy Saunders

Jeremy Saunders

Technical Architect | DevOps Evangelist | Software Developer | Microsoft, NVIDIA, Citrix and Desktop Virtualisation (VDI) Specialist/Expert | Rapper | Improvisor | Comedian | Property Investor | Kayaking enthusiast at J House Consulting
Jeremy Saunders is the Problem Terminator. He is a highly respected IT Professional with over 35 years’ experience in the industry. Using his exceptional design and problem solving skills with precise methodologies applied at both technical and business levels he is always focused on achieving the best business outcomes. He worked as an independent consultant until September 2017, when he took up a full time role at BHP, one of the largest and most innovative global mining companies. With a diverse skill set, high ethical standards, and attention to detail, coupled with a friendly nature and great sense of humour, Jeremy aligns to industry and vendor best practices, which puts him amongst the leaders of his field. He is intensely passionate about solving technology problems for his organisation, their customers and the tech community, to improve the user experience, reliability and operational support. Views and IP shared on this site belong to Jeremy.
Jeremy Saunders
Jeremy Saunders

Previous post:

Next post: