Update-IISCertificate.psm1

#Requires -Modules Microsoft.PowerShell.Core
#Requires -Modules Microsoft.PowerShell.Utility
#Requires -Modules Microsoft.PowerShell.Management

Function Get-File {

    [CmdletBinding()]
    param (
        [string]$InitialDirectory,
        [string]$Filter
    )

    [System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms") | Out-Null
    $OpenFileDialog = New-Object System.Windows.Forms.OpenFileDialog

    if ($InitialDirectory) {
        $OpenFileDialog.InitialDirectory = $InitialDirectory
    }
    if ($Filter) {
        $OpenFileDialog.Filter = "$Filter (*.$Filter)|*.$Filter"
    } else {
        $OpenFileDialog.Filter = 'All Files (*.*)|*.*'
    }
    [void]$OpenFileDialog.ShowDialog()
    return $OpenFileDialog.FileName
}


Function Update-IISCertificate {
    <#
    .SYNOPSIS
        This function allows you to update IIS certificates across multiple servers
 
    .DESCRIPTION
        This commandlet will allow you to update the SSL certificate and IIS binding on an
        array of servers that you give it. Currently you have to provide a pfx file and password
        to use this command. In the future it might support auto generation of pfx files
 
    .PARAMETER FQDN
        Specify the FQDN also known as Common Name (CN) of the SSL certificate
 
    .PARAMETER Servers
        Put the name(s) of the server(s) you want to update.
        This can be a comma separated list for multiple servers
 
    .INPUTS
        There are no inputs for this function
 
    .OUTPUTS
        There are no outputs for this function
 
    .EXAMPLE
        Update-IISCertificate -FQDN *.domain.com -servers server
 
        Updates IIS sites that are using the *.domain.com certificate common name
 
    .EXAMPLE
        Update-IISCertificate -FQDN *.domain.com -servers server,server2,server3
 
        Updates IIS sites that are using the *.domain.com certificate common name
        for server, server2, and server3
 
    .NOTES
        Author: Joe Fabrie
        Last Edit: 2023-11-08
        Version 1.0 - Initial version
 
    #>


    [CmdletBinding(SupportShouldProcess)]
    param (

    [Parameter(Mandatory=$true,
        HelpMessage="FQDN of cert")]
        [string]$FQDN,

    [Parameter(ParameterSetName='Server list',
        HelpMessage="Use one server name")]
        [string[]]$servers,

    [Parameter(ParameterSetName='CSV server list',
        DontShow,
        HelpMessage="You will be prompted to select csv file")]
        [switch]$CSVservers
    )

    #Prompt for PFX password
    $password = Read-Host -AsSecureString -Prompt "Enter PFX Password"


    #Prompts you to select the pfx file
    $certname = Get-File -Filter pfx

    # Convert plain text password to a secure string
    #$SecurePassword = ConvertTo-SecureString -String $Password -AsPlainText -Force

    #If using CSV parameter import values
    if($CSVservers) {
        $servers = Import-Csv -Path (Get-File -Filter csv)
    $servers
    }

    #Get just the file name from $certname parameter
    $cert = Split-Path $Certname -Leaf

    #Copy PFX to all servers
    $servers | foreach-Object {
        try {
            copy-item -Path $certname -Destination "\\$_\c`$" }
        catch { "$_"}
    }

    #Establish a connection to all of those servers:
    try {$session = New-PsSession -ComputerName $servers}
    catch{"$_"}

    #Import certificate script block that is ran with Invoke-Command at end of script
    $importCertificatesCommand = ({

        $newCert = Import-PfxCertificate `
          -FilePath "c:\$($using:cert)"  `
          -CertStoreLocation "Cert:\LocalMachine\My" `
          -password $using:Password

        Import-Module Webadministration

        $sites = Get-ChildItem -Path IIS:\Sites

        foreach ($site in $sites)
        {
            foreach ($binding in $site.Bindings.Collection)
            {
                if ($binding.protocol -eq 'https')
                {
                    $search = "Cert:\LocalMachine\My\$($binding.certificateHash)"
                    $certs = Get-ChildItem -path $search -Recurse
                    $hostname = hostname

                    if (($certs.count -gt 0) -and
                        ($certs[0].Subject.StartsWith($FQDN)))
                    {
                        Write-Output "Updating $hostname, site: `"$($site.name)`", binding: `"$($binding.bindingInformation)`", current cert: `"$($certs[0].Subject)`", Expiry Date: `"$($certs[0].NotAfter)`""

                        $binding.AddSslCertificate($newCert.Thumbprint, "my")
                    }
                }
            }
        }
    })

    Invoke-Command -session $session -scriptblock $importCertificatesCommand
    Invoke-Command -Session $session { remove-item -path "c:\$($using:cert)" }
    Get-PSSession | Remove-PSSession
}