Implementing SSL with PowerShell for existing SharePoint 2013 farm – Part 4

In this 4th part of the series I will describe how to set to your SSL certificate for your SharePoint web application and provider-hosted SharePoint add-ins. In the previous part I described how to change your SharePoint web application to use SSL and HTTPS properly. That also resulted in a IIS site with bindings for the HTTPS protocol. This hasn’t been done yet for the provider-hosted SharePoint add-ins. They also have IIS sites that need HTTPS bindings. And, of course, your SSL certificate.

Configuration file

I have added Binding information to the existing configuration file. These bindings describe per web application what bindings are needed.

First, I have added a Bindings attribute for every Server element to indicate that bindings must be configured for that server when the complete script is running.

Second, I have added a Type attribute for every WebApplication element to indicate if it’s a SharePoint IIS site that we’re dealing with or an Add-in IIS site. We need some different logic here. The SharePoint Add-in sites do not have the HTTPS binding yet, the SharePoint sites do.

Last, but not least I have added one or more IISBinding elements for every WebApplication element. Each IISBinding element indicates what SSL certificate needs to be set for what hostheader.

<configuration>
 
    <certificates>
        <certificate store="Root" name="AddTrustExternalCARoot" filename="AddTrustExternalCARoot.crt" />
        <certificate store="CA" name="TrustedSecureCertificateAuthority5" filename="TrustedSecureCertificateAuthority5.crt" />
        <certificate store="CA" name="USERTrustRSAAddTrustCA" filename="USERTrustRSAAddTrustCA.crt" />
        <certificate store="My" name="Wildcard Company" filename="wildcard_company_com.pfx" password="VerySecretOfCourse" />
        <certificate store="My" name="Wildcard SPApps" filename="wildcard_spapps_company_com.pfx" password="VerySecretOfCourse" />
    </certificates>
 
    <servers>
        <server name="NL1601" certificatelocation="d$\Install\Certificates" bindings="TRUE">
            <certificate name="AddTrustExternalCARoot" />
            <certificate name="TrustedSecureCertificateAuthority5" />
            <certificate name="USERTrustRSAAddTrustCA" />
            <certificate name="Wildcard Company" />
            <certificate name="Wildcard SPApps" />
        </server>
        <server name="NL1602" certificatelocation="d$\Install\Certificates" bindings="TRUE">
            <certificate name="AddTrustExternalCARoot" />
            <certificate name="TrustedSecureCertificateAuthority5" />
            <certificate name="USERTrustRSAAddTrustCA" />
            <certificate name="Wildcard Company" />
            <certificate name="Wildcard SPApps" />
        </server>
        <server name="NL1611" certificatelocation="d$\Install\Certificates" >
            <certificate name="AddTrustExternalCARoot" />
            <certificate name="TrustedSecureCertificateAuthority5" />
            <certificate name="USERTrustRSAAddTrustCA" />
            <certificate name="Wildcard Company" />
            <certificate name="Wildcard SPApps" />
        </server>
        <server name="NL1612" certificatelocation="d$\Install\Certificates" >
            <certificate name="AddTrustExternalCARoot" />
            <certificate name="TrustedSecureCertificateAuthority5" />
            <certificate name="USERTrustRSAAddTrustCA" />
            <certificate name="Wildcard Company" />
            <certificate name="Wildcard SPApps" />
        </server>
        <server name="NL1701" certificatelocation="d$\Install\Certificates" bindings="TRUE">
            <certificate name="AddTrustExternalCARoot" />
            <certificate name="TrustedSecureCertificateAuthority5" />
            <certificate name="USERTrustRSAAddTrustCA" />
            <certificate name="Wildcard Company" />
            <certificate name="Wildcard SPApps" />
        </server>
        <server name="NL1702" certificatelocation="d$\Install\Certificates" bindings="TRUE">
            <certificate name="AddTrustExternalCARoot" />
            <certificate name="TrustedSecureCertificateAuthority5" />
            <certificate name="USERTrustRSAAddTrustCA" />
            <certificate name="Wildcard Company" />
            <certificate name="Wildcard SPApps" />
        </server>
    </servers>

    <WebApplications>
        <WebApplication name="AutoFollow" type="App" >
            <IISBinding hostheader="autofollow.company.com" certificate="Wildcard Company" sni="false" />
        </WebApplication>
        <WebApplication name="MaventionMake" type="App" >
            <IISBinding hostheader="make.company.com" certificate="Wildcard Company" sni="false" />
        </WebApplication>
        <WebApplication name="MySites" type="SharePoint" currentUrl="http://mysites.company.com" newDefaultUrl="https://mysites.company.com" hasHostHeader="TRUE">
            <IISBinding hostheader="mysites.company.com" certificate="Wildcard Company" sni="false" />
        </WebApplication>
        <WebApplication name="Hosting Web App" type="SharePoint" currentUrl="http://hostingwebapp.company.com" newDefaultUrl="https://hostingwebapp.company.com" hasHostHeader="FALSE" >
            <IISBinding hostheader="" certificate="Wildcard SPApps" sni="false" />
            <IISBinding hostheader="appcatalog.company.com" certificate="Wildcard Company" sni="true" />
            <IISBinding hostheader="search.company.com" certificate="Wildcard Company" sni="true" />
            <IISBinding hostheader="publicationcenter.company.com" certificate="Wildcard Company" sni="true" />
            <IISBinding hostheader="projectcenter.company.com" certificate="Wildcard Company" sni="true" />
            <IISBinding hostheader="makeadmin.company.com" certificate="Wildcard Company" sni="true" />
        </WebApplication>
    </WebApplications>

</configuration>

The tricky part here is our hostheader-less SharePoint web application. Host headers are set at the level of site collections. Hence, the term Host Named Site Collections. Initially, you will not find any binding for each host named site collection in IIS. The challenge here is we need 2 SSL certificates, one for the main domain COMPANY.COM and one for the App domain SPAPPS.COMPANY.COM. Remember, that SharePoint creates an App site with the URL that looks like app-<guid>.spapps.company.com. This site is part of the hostheaderless web application. Since IIS 8.0 (part of Windows Server 2012) you can set more than 1 SSL certificate on one IIS site (SSL Scalability). This is done using Server Name Indication (SNI). More in-depth information can be read here.

Let’s do some scripting.

The Script

When looping through the Server elements, I determine if Bindings need to be set.

[bool]$setBindings = [System.Convert]::ToBoolean($server.Bindings)

When that is the case, I use the remote session again to get the certificate info I need later.

Invoke-Command -Session $session -ScriptBlock {
        
    #Get Certificate ThumbPrints
    $thumbPrintWildCardSPApps = Get-ChildItem -Path Cert:\LocalMachine\My | ? { $_.subject.StartsWith("CN=*.spapps.company.com") } | Select-Object -ExpandProperty Thumbprint
    $thumbPrintWildCardCompany = Get-ChildItem -Path Cert:\LocalMachine\My | ? { $_.subject.StartsWith("CN=*.company.com") } | Select-Object -ExpandProperty Thumbprint
    $guid = [guid]::NewGuid().ToString("B")
}

Each certificate has a Thumbprint property. The thumbprint is a hash value computed over the complete certificate. I use this value to set the SSL certificate on a binding. I’ll show you in a bit.

Then we loop through the web applications.

$config.Configuration.WebApplications.WebApplication | % {
 
    $iisSite = $_
    Write-Host $iisSite.name - type: $iisSite.type -NoNewline
 
    $webExists = Invoke-Command -Session $session -ScriptBlock {
        param( $iisSiteName )
         
        $website = Get-WebSite -Name $iisSiteName
        return ($website -ne $null)
         
    } -ArgumentList $iisSite.name
 
     
    if( !$webExists )
    {
        Write-Host -f Yellow "[Skipped. Not present]"
    }
    else
    {
 
        Write-Host -f Green "[Exists]"
         
        $iisSite.IISBinding | % {
         
            $bindingConfig = $_
            Write-Host $bindingConfig.hostHeader
             
            if( $iisSite.type -eq "SharePoint")
            {
                #SharePoint Server
                Invoke-Command -Session $session -ScriptBlock {
                    param( $iisSiteName, $hostHeader, $certificate, $sni )
                 
                    $activeThumbPrint = $thumbPrintWildCardCompany
                    if( $certificate -ne "Wildcard Company" )
                    {
                        $activeThumbPrint = $thumbPrintWildCardSPApps
                    }
 
                    if( !$hostHeader )
                    {
                        # Main binding always exists, so add SSL Certificate only
                        netsh http add sslcert ipport="0.0.0.0:443" certhash="$activeThumbPrint" appid="$guid" certstorename=MY verifyclientcertrevocation=disable
 
                    }
                    else
                    {
 
                        $sniFlag = 1
                        if( $sni -ne "true")
                        {
                            $sniFlag = 0
                        }
 
                        $binding = Get-WebBinding -Name $iisSiteName -protocol https -port 443 -HostHeader $hostHeader
                        if( $binding -eq $null )
                        {
                            New-WebBinding -Name $iisSiteName -Protocol https -Port 443 -IPAddress "*" -HostHeader $hostHeader -SslFlags $sniFlag
                        }
                        else
                        {
                            Set-WebBinding -Name $iisSiteName -BindingInformation $binding.BindingInformation -PropertyName sslFlags -Value $sniFlag
                        }
                        netsh http add sslcert hostnameport="$($hostHeader):443" certhash="$activeThumbPrint" appid="$guid" certstorename=MY verifyclientcertrevocation=disable
 
                    }
                     
                } -ArgumentList $iisSite.name, $bindingConfig.hostheader, $bindingConfig.certificate, $bindingConfig.sni
            }
            else
            {
                #AppServer
                Invoke-Command -Session $session -ScriptBlock {
                    param( $iisSiteName, $hostHeader, $certificate )
                 
                    $activeThumbPrint = $thumbPrintWildCardCompany
                    if( $certificate -ne "Wildcard Company" )
                    {
                        $activeThumbPrint = $thumbPrintWildCardSPApps
                    }
 
                    New-WebBinding -Name $iisSiteName -Protocol https -Port 443 -IPAddress "*" -HostHeader $hostHeader -SslFlags 0
                     
                    netsh http add sslcert hostnameport="$($hostHeader):443" certhash="$activeThumbPrint" appid="$guid" certstorename=MY verifyclientcertrevocation=disable
                     
                    Remove-WebBinding -Name $iisSiteName -Protocol http -Port 80 -IPAddress "*" -HostHeader $hostHeader -Confirm:$false
                     
                } -ArgumentList $iisSite.name, $bindingConfig.hostheader, $bindingConfig.certificate
 
            }
        }
    }
}

Keep in mind that above snippet runs on each server in the configuration file. I check if the web application exists on the server (lines 6-12). If it’s not, I skip it and move on to the next. If the web site does exist, I do some processing based on the Type (SharePoint or Add-In).

SharePoint sites

For the SharePoint sites I once again use a remote session to get the SSL certificate thumbprint (lines 35-39). Then I check the value of the hostheader attribute. If it’s empty we are dealing with the main/default HTTPS binding. I use the command line tool NETSH to add a SSL certificate to a binding. (more info on TechNet https://technet.microsoft.com/en-us/library/cc725882(v=ws.10).aspx#BKMK_2 ). For this binding, I select the certificate SPAPPS. The App Domain hostheader is using this main/default HTTPS binding.

Back to the script… When the hostheader attribute is not empty for the SharePoint site, then I create a new Web Binding for that host (named site collection). Then I add the other SSL certificate (Company) to this binding. (lines 59-70). To be able to set another SSL certificate I have to set the SNI flag.

SharePoint Add-In sites

For the Add-In IIS sites it’s less complex (starting at line 81). It is still on HTTP/80, so I create a new binding for the HTTPS/443 and set the SSL certificate (COMPANY). Then I remove the HTTP/80 binding.

Running the Script

The bindings for the Hosting Web App IIS site are:

image

Let’s kick it off.

image

And the bindings are now:

image

 

Summary

Setting your SSL certificate to new or existing bindings in IIS can easily be done with PowerShell. This is quite handy when you need to configure multiple servers.

In the next part I will focus on some re-configuration of SharePoint services, such as Search and User Profiles. 

Share

One thought on “Implementing SSL with PowerShell for existing SharePoint 2013 farm – Part 4

  1. Pingback: Implementing SSL with PowerShell for existing SharePoint 2013 farm – Part 1 | SharePoint: Working on it…

Leave a Reply

Your email address will not be published. Required fields are marked *