Public/Install-CertificateAutomation.ps1
function Install-CertificateAutomation { [CmdletBinding()] param ( [Parameter(Mandatory, Position = 1)] [string] $Domain, [Parameter(Position = 2)] [string] $Contact, [Parameter(Position = 3)] [string] $DnsPlugin, [Parameter(Position = 4)] [hashtable] $PluginArgs, [Parameter(Position = 5)] [string] $ScriptDirectory = "C:\scripts" ) begin { if (!([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")) { throw "This command requires elevation. Please run as administrator." } Write-Output "Setting Execution Policy to RemoteSigned for the current process" Set-ExecutionPolicy RemoteSigned -Scope Process if (!(Get-Module Posh-ACME)) { Install-Module Posh-ACME -Repository PSGallery -Verbose } } process { try { Write-Output "Testing certificate request against staging server" Set-PAServer LE_STAGE New-PACertificate -force $domain -AcceptTOS -Contact $contact -DnsPlugin $DnsPlugin -PluginArgs $PluginArgs -Install -ErrorAction Stop -Verbose Write-Output "Successfully installed certificate from staging server" Write-Output "Removing test certificate" $stagingCert = Get-PACertificate Get-ChildItem Cert:\LocalMachine\My | Where-Object Thumbprint -eq $stagingCert.Thumbprint | Remove-Item Write-Output "Installing production certificate" Set-PAServer LE_PROD New-PACertificate -force $domain -AcceptTOS -Contact $contact -DnsPlugin $DnsPlugin -PluginArgs $PluginArgs -Install -ErrorAction Stop -Verbose Write-Output "Certificate installed to Cert:\LocalMachine\My" Write-Output "Binding certificate to Mobile Server" Get-PACertificate | Set-MobileServerCertificate -ErrorAction Stop -Verbose Write-Output "Setting up automatic certificate renewal script in $ScriptDirectory" $scriptPath = Join-Path $ScriptDirectory "renew-certificate.ps1" $logPath = Join-Path $ScriptDirectory "log.txt" if (!(Test-Path $ScriptDirectory)) { New-Item $ScriptDirectory -ItemType Directory | Out-Null } $scriptBlock = { param([string]$LogPath) function WriteLog { Param ([string]$message) Add-Content -Path $LogPath -Value "$(Get-Date) - $message" } try { $thumbprint = (Get-PACertificate).Thumbprint $cert = Submit-Renewal -WarningAction Stop -ErrorAction Stop $cert | Set-MobileServerCertificate WriteLog "New certificate installed with thumbprint $($cert.Thumbprint)" WriteLog "Removing old certificate with thumbprint $thumbprint" Get-ChildItem Cert:\LocalMachine\My | Where-Object Thumbprint -eq $thumbprint | Remove-Item } catch { WriteLog $_.Exception.Message throw } } Set-Content -Path $scriptPath -Value $scriptBlock Write-Output "Registering a new scheduled task to run the renewal script daily" $taskName = 'Posh-ACME Certificate Renewal' $action = New-ScheduledTaskAction -Execute "powershell.exe" -Argument "-NoProfile -File ""$scriptPath"" $logPath" $trigger = New-ScheduledTaskTrigger -Daily -At 2am Unregister-ScheduledTask -TaskName $taskName -Confirm:$false -ErrorAction SilentlyContinue $credential = Get-Credential -Message "Enter your password to setup the Scheduled Task" -UserName ([System.Security.Principal.WindowsIdentity]::GetCurrent().Name) $taskParams = @{ Action = $action Trigger = $trigger TaskName = $taskName RunLevel = "Highest" User = $credential.UserName Password = ($credential.Password | ConvertTo-UnsecureString) } Register-ScheduledTask @taskParams | Out-Null $taskParams = $null # Edits Windows hosts file so that on the local machine, the $domain address always routes to the local machine Write-Output "Adding $domain to the local hosts file" $params = @{ Path = "$($env:SystemRoot)\System32\drivers\etc\hosts" Value = "`r`n127.0.0.1 $domain" } Add-Content @params # Launch the default web browser to the mobile server's HTTPS page $mobileServer = Get-MobileServerInfo $url = "https://$($domain):$($mobileServer.HttpsPort)" Write-Output "Finished! Opening a web browser to $url" Start-Process $url } catch { throw } } <# .SYNOPSIS Uses Posh-ACME to request a Let's Encrypt certificate and configure Mobile Server to use it .DESCRIPTION Uses Posh-ACME to request a Let's Encrypt certificate and configure Mobile Server to use it, then creates a Scheduled Task to run daily, and execute a renewal script which will handle certificate renewal when the certificate becomes eligible for renewal - typically 60 days after issue. When the certificate is renewed, it will be installed into the Windows certificate store and the old certificate will be removed from the certificate store. The Milestone XProtect Mobile Server service will be restarted so that it automatically uses the renewed certificates going forward. .PARAMETER Domain The domain for which you will request a Let's Encrypt certificate. See Get-Help New-PACertificate for more info. .PARAMETER Contact The email address associated with this domain for the purpose of renewal notifications. See Get-Help New-PACertificate for more info. .PARAMETER DnsPlugin The DnsPlugin to use for handling DNS challenges. See Get-Help New-PACertificate for more info. .PARAMETER PluginArgs A hashtable with the necessary parameters for the chosen DnsPlugin. See Get-Help New-PACertificate for more info. .PARAMETER ScriptDirectory The path where the renew-certificate.ps1 script will be saved, and the log.txt file will be written to. A scheduled task named Posh-ACME Certificate Renewal will be created to run the renew-certificate.ps1 script daily, and this script will append information to log.txt in the same path. .EXAMPLE $InstallParams = @{ Domain = test.example.com Contact = admin@example.com DnsPlugin = Dynu PluginArgs = @{DynuClientID='xxxx';DynuSecret='xxxx'} ScriptDirectory = "C:\scripts" } Install-CertificateAutomation @InstallParams Requests a Let's Encrypt certificate for test.example.com, uses Dynu DNS to handle the ACME-protocol DNS challenge, binds the certificate to the Mobile Server's HTTPS port using 'netsh http add|update sslcert', restarts the Mobile Server service, creates a .PS1 certificate renewal script in C:\scripts\ and a scheduled task to call this script daily at 2AM, logging the result to C:\scripts\log.txt. #> } |