Public/Install-CertificateAutomation.ps1
function Install-CertificateAutomation { [CmdletBinding(SupportsShouldProcess, ConfirmImpact="Medium")] 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" Set-ExecutionPolicy RemoteSigned -WhatIf:$WhatIfPreference if (!(Get-Module Posh-ACME)) { Install-Module Posh-ACME -Repository PSGallery -Verbose:$VerbosePreference -WhatIf:$WhatIfPreference } } process { try { if ($PSCmdlet.ShouldProcess("Install test certificate")) { 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:$VerbosePreference 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 } if ($PSCmdlet.ShouldProcess("Install production certificate")) { Write-Output "Installing production certificate" Set-PAServer LE_PROD New-PACertificate -force $domain -AcceptTOS -Contact $contact -DnsPlugin $DnsPlugin -PluginArgs $PluginArgs -Install -ErrorAction Stop -Verbose:$VerbosePreference Write-Output "Certificate installed to Cert:\LocalMachine\My" } if ($PSCmdlet.ShouldProcess("Bind new certificate to Mobile Server")) { Write-Output "Binding certificate to Mobile Server" Get-PACertificate | Set-MobileServerCertificate -ErrorAction Stop -Verbose:$VerbosePreference } 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 -WhatIf:$WhatIfPreference | 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 -WhatIf:$WhatIfPreference if ($PSCmdlet.ShouldProcess("Register-ScheduledTask to execute $scriptPath")) { 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 -WhatIf:$WhatIfPreference $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 } 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 -WhatIf:$WhatIfPreference $mobileServer = Get-MobileServerInfo $url = "https://$($domain):$($mobileServer.HttpsPort)" if ($PSCmdlet.ShouldProcess("Open web browser to $url")) { 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. #> } |