Public/Set-IISCertificateOld.ps1

function Set-IISCertificateOld {
    [CmdletBinding()]
    param(
        [Parameter(Position=0,ValueFromPipelineByPropertyName)]
        [Alias('Thumbprint')]
        [string]$CertThumbprint,
        [Parameter(Position=1,ValueFromPipelineByPropertyName)]
        [string]$PfxFile,
        [Parameter(Position=2,ValueFromPipelineByPropertyName)]
        [securestring]$PfxPass,
        [string]$SiteName='Default Web Site',
        [uint32]$Port=443,
        [string]$IPAddress='*',
        [string]$HostHeader,
        [switch]$RequireSNI,
        [switch]$RemoveOldCert
    )

    Begin {
        # make sure the WebAdministration module is available
        if (!(Get-Module -ListAvailable WebAdministration -Verbose:$false)) {
            try { throw "The WebAdministration module is required to use this function." }
            catch { $PSCmdlet.ThrowTerminatingError($_) }
        } else {
            Import-Module WebAdministration -Verbose:$false
        }
    }

    Process {

        # surface individual errors without terminating the whole pipeline
        trap { $PSCmdlet.WriteError($PSItem); return }

        $CertThumbprint = Confirm-CertInstall @PSBoundParameters

        # HostHeader with SSL is only supported on IIS versions with SNI support which
        # is IIS 8+. So throw an error if they specified one and we can't use it.
        $SupportSNI = ((Get-ItemProperty HKLM:\SOFTWARE\Microsoft\InetStp).MajorVersion -ge 8)
        if (-not ([string]::IsNullOrWhiteSpace($HostHeader))) {
            if (-not $SupportSNI) {
                throw "Host Headers for SSL/TLS bindings are only supported on IIS 8.0 or higher."
            }
        } elseif ($RequireSNI) {
            # HostHeader is empty but they used -RequireSNI, so throw an error
            throw "RequireSNI specified, but HostHeader is missing."
        }

        # verify the site exists
        if (-not (Get-WebSite -Name $SiteName)) {
            throw "Site $SiteName not found."
        }

        $sslFlags = 0
        if ($RequireSNI) { $sslFlags = 1 }

        # check for an existing site binding
        $bindMatch = "$($IPAddress):$($Port):$($HostHeader)"
        $binding = Get-WebBinding -Name $SiteName -Protocol 'https' | Where-Object {
            $_.bindingInformation -eq $bindMatch
        }
        if ($binding) {

            # we've got a match on the binding string, but now we have to make sure the sslFlags value
            # also matches. But sslFlags only exists on IIS 8+, so don't bother checking on earlier versions
            if ($SupportSNI) {

                # update the value if it doesn't match
                if ($binding.sslFlags -ne $sslFlags) {
                    Write-Verbose "Updating sslFlags on binding $bindMatch"
                    Set-WebBinding -Name $SiteName -BindingInformation $bindMatch -PropertyName sslFlags -Value $sslFlags
                } else {
                    Write-Verbose "IIS Binding already exists for $bindMatch"
                }

            } else {
                Write-Verbose "IIS Binding already exists for $bindMatch"
            }

        } else {
            # no match

            # build the param splat we'll use with New-WebBinding
            $newBindingParams = @{
                Name = $SiteName
                Protocol = 'https'
                IPAddress = $IPAddress
                Port = $Port
            }
            if ($HostHeader) {
                $newBindingParams.HostHeader = $HostHeader
            }
            if ($SupportSNI) {
                $newBindingParams.SslFlags = $sslFlags
            }

            Write-Verbose "Adding new site binding for $bindMatch"
            New-WebBinding @newBindingParams
        }

        # get a reference to the cert
        $cert = Get-ChildItem Cert:\LocalMachine\My | Where-Object { $_.Thumbprint -eq $CertThumbprint }

        # get the current ssl binding list
        $sslBindings = Get-ChildItem IIS:\SslBindings | Where-Object { $_.Sites.Value -eq $SiteName }

        # Matching the SSL binding is weird because it changes depending on whether you have the
        # SNI required flag enabled on the web binding or not. If yes, the IP is stripped so the string
        # starts with '!' and ends with '!<hostname>'. If not, the IP is included (0.0.0.0 for *) but the
        # hostname and it's '!' are stripped. So:
        #
        # *:443: + No SNI = 0.0.0.0!443
        # 10.10.10.10:443: + No SNI = 10.10.10.10!443
        # *:443:test.example.com + No SNI = 0.0.0.0!443
        # 10.10.10.10:443:test.example.com + No SNI = 10.10.10.10!443
        # *:443: + SNI = (not possible)
        # 10.10.10.10:443: + SNI = (not possible)
        # *:443:test.example.com + SNI = !443!test.example.com
        # 10.10.10.10:443:test.example.com + SNI = !443!test.example.com
        #
        # This means it's possible to have multiple web bindings that basically share an SSL binding
        # so if you change the thumbprint on one, it changes both.

        $sslMatch = $bindMatch.Replace(':','!').Replace('*','0.0.0.0')
        if ($sslMatch[-1] -eq '!') {
            # remove the ending '!'
            $sslMatch = $sslMatch.Substring(0,$sslMatch.Length-1)
        } elseif ($RequireSNI) {
            # remove the IP in front
            $sslMatch = $sslMatch.Substring($sslMatch.IndexOf('!'))
        } else {
            # remove the '!<hostname>' at the end
            $sslMatch = $sslMatch.Substring(0,$sslMatch.LastIndexOf('!'))
        }
        Write-Verbose "Checking SSL binding $sslMatch"

        # check if ssl binding already exists
        if ($binding = $sslBindings | Where-Object { $_.PSChildName -eq $sslMatch }) {

            # save the old thumbprint for potential deleting later
            $oldThumb = $binding.Thumbprint

            if ($oldThumb -eq $CertThumbprint) {
                Write-Verbose "SSL binding already exists for $sslMatch"
            } else {
                Write-Verbose "Removing old thumbprint from $sslMatch SSL binding"
                # Could never get Set-Item to work directly, always kept throwing param errors
                # So instead, we'll delete and re-create
                Get-Item IIS:\SslBindings\$sslMatch | Remove-Item
                $addNew = $true
            }

        } else { $addNew = $true }

        if ($addNew) {

            Write-Verbose "Adding certificate thumbprint $CertThumbprint"
            if ($SupportSNI) {
                $cert | New-Item IIS:\SslBindings\$sslMatch -SslFlags $sslFlags | Out-Null
            } else {
                $cert | New-Item IIS:\SslBindings\$sslMatch | Out-Null
            }

            # remove the old cert if specified
            if ($RemoveOldCert) { Remove-OldCert $oldThumb }
        }

    }

}