Windows/Apache Web Server

Autorenewing SSL Certifcates

Introduction

I have been using Certbot and Let's Encrypt for my SSL Certificates. For all the time I have been using these, I still have not found a way of installing the certificates without stopping the server. What I would do is manually run a PowerShell script to shut down the server's Apache service, run Certbot to obtain the certifcates and restart the Apache service.

New-Variable -Name serviceName -Value 'Apache2.4' -Option Constant
Write-Output "Stopping Apache"
Stop-Service -name $serviceName
$service = Get-Service -Name $serviceName
$service.WaitForStatus('Stopped')
Write-Output "Apache is stopped"
# Line below works and waits for certbot to finsh before restarting Apache!
# Uncomment any 1 of the 3 following lines - they all work
# certbot.exe renew 
# Invoke-expression -command "certbot.exe renew"
Start-Process -FilePath "certbot.exe" -ArgumentList "renew" -wait
Write-Output "Apache is restarting"
Start-Service -Name $serviceName
$service.WaitForStatus('Running')
Write-Output "Apache has restarted"
Exit

The above PowerShell script is what I was using

On 22 January 2025, Let's Encrypt announced that from 4 June 2025 they will no longer be sending expiration notification emails to remind people that their certificates will need to be renewed soon.

In February 2025, I decided to write a PowerShell script to run once a week via Windows Task Scheduler and check when the certificates will expire and if there is 30 days to go to that, renew the certifcates, and log what the script did.


The Script

New-Variable -Name apacheService -Value 'Apache2.4' -Option Constant
$rewnewCerts = cmd.exe /c "C:\Certbot\bin\certbot certificates"
Write-Output $renew-certs-task
"Certbot Renew Log" | Out-File "$PSScriptRoot\renew-certs-task.log"
"" | Out-File "$PSScriptRoot\renew-certs-task.log" -append
$now = Get-Date
"renew-certs-task.ps1 was run at $now" | Out-File "$PSScriptRoot\renew-certs-task.log" -append
"" | Out-File "$PSScriptRoot\renew-certs-task.log" -append
"The Certbot Certifcates string is:" | Out-File "$PSScriptRoot\renew-certs-task.log" -append
"" | Out-File "$PSScriptRoot\renew-certs-task.log" -append
$rewnewCerts | Out-File "$PSScriptRoot\renew-certs-task.log" -append
foreach ($line in $rewnewCerts) {
	$line | Select-String -Pattern "VALID"
	$posn = $line.IndexOf("VALID")
	if ($posn -gt -1) {
		$newLine = $line.toString()
		"The position of VALID in the string is $posn" | Out-File "$PSScriptRoot\renew-certs-task.log" -append
		"" | Out-File "$PSScriptRoot\renew-certs-task.log" -append
		$days = $newLine.Substring($posn + 7, 2)
		"The number of days before certificates expire is $days" | Out-File "$PSScriptRoot\renew-certs-task.log" -append
		break
	}
}
If ($days -gt 30) {
	"" | Out-File "$PSScriptRoot\renew-certs-task.log" -append
	"The number of days before the certificates expire is greater than 30 days, nothing done, exiting program" | Out-File "$PSScriptRoot\renew-certs-task.log" -append
	exit
}
If ($days -le 30) {
	"" | Out-File "$PSScriptRoot\renew-certs-task.log" -append
	"Trying to get new certificates" | Out-File "$PSScriptRoot\renew-certs-task.log" -append
	# Stop Apache server
	Write-Output "Stopping $($apacheService) service"
	Stop-Service -Name $apacheService
	$service = Get-Service -Name $apacheService
	$service.WaitForStatus('Stopped')
	# Run Certbot to update the certificates
	cmd.exe /c "C:\Certbot\bin\certbot renew"
	# Start Apache server
	Write-Output "Restarting Apache service"
	Start-Service -Name $apacheService
	$service.WaitForStatus('Running')
	Write-Output "$apacheService has restarted"
	# Check Certbot Certificates again
	$rewnewCerts = cmd.exe /c "C:\Certbot\bin\certbot certificates"
	"" | Out-File "$PSScriptRoot\renew-certs-task.log" -append
	"The new Certbot Certifcates string is:" | Out-File "$PSScriptRoot\renew-certs-task.log" -append
	"" | Out-File "$PSScriptRoot\renew-certs-task.log" -append
	$rewnewCerts | Out-File "$PSScriptRoot\renew-certs-task.log" -append
	foreach ($line in $rewnewCerts) {
		$line | Select-String -Pattern "VALID"
		$posn = $line.IndexOf("VALID")
		if ($posn -gt -1) {
			$newLine = $line.toString()
			"" | Out-File "$PSScriptRoot\renew-certs-task.log" -append
			$days = $newLine.Substring($posn + 7, 2)
			"The new certificates will expire on $newLine" | Out-File "$PSScriptRoot\renew-certs-task.log" -append
			break
		}
	}
}
exit

What it Does

The command certbot certifcates is run and the lines of text returned from that is searched for the word VALID and its position noted. The number of days the certificates have left can then be found. This is because that string contains the number of days the certificates are valid for among:

- - - - - - - - - -
Found the following certs:
Certificate Name: brisray.com
Serial Number: xxxxxxxxxxx
Key Type: ECDSA
Domains: brisray.com bristolgunners.org hmsgambia.org ihor4x4.com www.brisray.com www.bristolgunners.org www.hmsgambia.org www.ihor4x4.com
Expiry Date: 2025-03-16 00:19:10+00:00 (VALID: 35 days)
Certificate Path: xxxxxxxxxx\fullchain.pem
Private Key Path: xxxxxxxxxxx\privkey.pem
- - - - - - - - - -

If the number is less than or equal to thirty, the Apache service is stopped, the certificates renewed and the Apache service started again. The certbot certifcates command is run again and the number of days left for the new certificates written to the log file.


Sources and Resources

Certbot
Certbot Documentation
Certbot Latest Releases
Certbot on Windows: Automation is Possible - Matt Zaske's experience with Certbot with Apache on Windows
Electronic Frontier Foundation
HTTPS Checker - Checks a HTTPS site for insecure content. The online checker is not free but the downloadable one is
Ionos SSL Certificate Checker - checks that the certificates are installed properly
JitBit SSL Check - Checks a HTTPS site for insecure content
Let's Encrypt
Let's Encypt Community Forum
Let's Encrypt no longer sending expiration notification emails
Missing Padlock SSL Checker - Checks a HTTPS site for insecure content
Qualys SSL Server Test - checks that the certificates are installed properly
SSL Certificate Formats (Tutorials Teacher)