├── Certify_the_Web ├── CertifyPost-PRTG.ps1 ├── Documentation │ └── Images │ │ ├── Certify-DeploymentMode.png │ │ ├── Certify-DeploymentTask01.png │ │ ├── Certify-DeploymentTask02.png │ │ └── Certify-DeploymentTask03.png └── README.md ├── LICENSE ├── README.md └── win-acme ├── README.md └── WACSPost-PRTG.ps1 /Certify_the_Web/CertifyPost-PRTG.ps1: -------------------------------------------------------------------------------- 1 | #requires -version 5 2 | <# 3 | .SYNOPSIS 4 | A Post-Request Script Hook for Certify The Web to install a cert for PRTG. 5 | 6 | .DESCRIPTION 7 | 8 | 9 | .PARAMETER 10 | A Post-Request Script Hook for Certify The Web to install a cert for PRTG. 11 | 12 | .INPUTS 13 | None. 14 | 15 | .OUTPUTS 16 | * PowerShell Transcript written to the directory the script is saved in. 17 | * Current PRTG certificate files will be backed up to $PRTGCertRoot\Backup_$iso8601 18 | * PEM file with private key and certificate will be written to $env:ProgramData\Certify\certes\assets\pfx 19 | * Public Certificate File, PEM Format: $PRTGCertRoot\prtg.crt 20 | * Private Key File, PEM Format: $PRTGCertRoot\prtg.key 21 | * Let's Encrypt Intermediate Root CA: $PRTGCertRoot\root.pem 22 | 23 | 24 | .NOTES 25 | Author: Andrew Zbikowski 26 | Latest version can be found at https://github.com/andyzib/LetsEncrypt-PRTG 27 | 28 | Requires PSPKI module for PFX to PEM conversion. from. 29 | Install-Module PSPKI should to the trick, see https://www.pkisolutions.com/tools/pspki for more on PSPKI. 30 | 31 | .EXAMPLE 32 | DO NOT RUN MANUALLY! Configure as a Post-request PS Script in the Certify The Web GUI. 33 | #> 34 | 35 | # Certify the Web Scipting Hooks documentation: https://docs.certifytheweb.com/docs/script-hooks.html 36 | param($result) # required to access the $result parameter 37 | 38 | ########## Configuration Section 39 | # Where the PRTG certificates live. 40 | $PRTGCertRoot = 'C:\Program Files (x86)\PRTG Network Monitor\cert' 41 | # Let's Encrypt Intermediate CA 42 | $LEIntermediateCA = 'https://letsencrypt.org/certs/lets-encrypt-r3.pem' 43 | # Restart PRTGCoreService (Required to update certificate) 44 | $RestartPRTGCoreService = $true 45 | # Restart PRTGProbeService (https://github.com/andyzib/LetsEncrypt-PRTG/issues/2) 46 | $RestartPRTGProbeService = $false 47 | # Restart PRTG 48 | ########## End of Configuration, no more changes! 49 | 50 | # PowerShell PKI module needed for certificate conversion. 51 | # https://www.pkisolutions.com/tools/pspki/ 52 | Import-Module -Name PSPKI -ErrorAction Stop 53 | 54 | # ISO 8601 Date Format. Accept no substitutes! 55 | $iso8601 = Get-Date -Format s 56 | # Colon (:) isn't a valid character in file names. 57 | $iso8601 = $iso8601.Replace(":","_") 58 | 59 | Start-Transcript -Path "$PSScriptRoot\$($iso8601)_CertifyPost-PRTG_Transcript.txt" 60 | 61 | # Verify Certify successfully renewed the cert. 62 | if ($result.IsSuccess) { 63 | # Verify the path to PRTG's Certificate Directory is configured correctly. 64 | if (Test-Path -Path $PRTGCertRoot -PathType Container) { 65 | # Verify the backup path for the current certificate exists or create it. 66 | $BackupPath = $PRTGCertRoot + "\Backup_" + $iso8601 67 | if (-Not (Test-Path -Path $BackupPath -PathType Container)) { 68 | Try { New-Item -Path $PRTGCertRoot -Name "\Backup_$iso8601" -ItemType "Directory" } 69 | Catch { Throw "Failed to create backup directory for current certificate. Aborting without making changes." } 70 | } 71 | # Backup the current certificate files. 72 | # prtg.crt 73 | Try { Copy-Item -Path "$PRTGCertRoot\prtg.crt" -Destination "$BackupPath\prtg.crt" } 74 | Catch { Throw "Failed to backup prtg.crt to $BackupPath. Aborting without making changes." } 75 | # prtg.key 76 | Try { Copy-Item -Path "$PRTGCertRoot\prtg.key" -Destination "$BackupPath\prtg.key" } 77 | Catch { Throw "Failed to backup prtg.key to $BackupPath. Aborting without making changes." } 78 | # root.pem 79 | Try { Copy-Item -Path "$PRTGCertRoot\root.pem" -Destination "$BackupPath\root.pem" } 80 | Catch { Throw "Failed to backup root.pem to $BackupPath. Aborting without making changes." } 81 | 82 | # Remove existing certificate files. 83 | # prtg.crt 84 | Try { Remove-Item -Path "$PRTGCertRoot\prtg.crt" } 85 | Catch { Throw "Failed to delete $PRTGCertRoot\prtg.crt. Aborting without making further changes." } 86 | # prtg.key 87 | Try { Remove-Item -Path "$PRTGCertRoot\prtg.key" } 88 | Catch { Throw "Failed to delete $PRTGCertRoot\prtg.key. Aborting without making further changes." } 89 | # root.pem 90 | Try { Remove-Item -Path "$PRTGCertRoot\root.pem" } 91 | Catch { Throw "Failed to delete $PRTGCertRoot\root.pem. Aborting without making further changes." } 92 | 93 | # Write the new Let's Encrypt certificate obtained by Certify. 94 | 95 | # Get the cert from the store. 96 | $CertPFX = Get-ChildItem -Path "cert:\LocalMachine\my\$($result.ManagedItem.CertificateThumbprintHash)" 97 | # Convert PFX to PEM 98 | # This Split-Path should return something like C:\ProgramData\Certify\certes\assets\pfx 99 | $CertifyAssetDir = Split-Path -Path $result.ManagedItem.CertificatePath -Parent 100 | $TempPEM = "$CertifyAssetDir\$($result.ManagedItem.CertificateThumbprintHash).pem" 101 | # Convert-PfxToPem will only save a file. 102 | $resultTEMP = Convert-PfxToPem -Certificate $CertPFX -OutputFile $TempPEM 103 | # Read in the PEM cert file in -Raw format so RegEx works. 104 | $CertPEM = Get-Content $TempPEM -Raw 105 | # Split Private Key and Certificate using RegEx. 106 | $pattern = "(?sm).*-----BEGIN PRIVATE KEY-----(.*)-----END PRIVATE KEY-----.*-----BEGIN CERTIFICATE-----(.*)-----END CERTIFICATE-----" 107 | if ($CertPEM -match $pattern) { 108 | $OutPrivateKeyPEM = "-----BEGIN PRIVATE KEY-----$($Matches[1])-----END PRIVATE KEY-----" 109 | 110 | $OutCertificatePEM = "-----BEGIN CERTIFICATE-----$($Matches[2])-----END CERTIFICATE-----" 111 | } else { 112 | Throw "Unable to match Private Key and Certificate" 113 | } 114 | # Write the prtg.cer and prtg.key files. 115 | $OutCER = "$PRTGCertRoot\prtg.crt" 116 | $resultTEMP = Set-Content -Path $OutCER -Value $OutCertificatePEM 117 | $OutKey = "$PRTGCertRoot\prtg.key" 118 | $resultTEMP = Set-Content -Path $OutKey -Value $OutPrivateKeyPEM 119 | # Write Public Cert to root.pem (https://github.com/andyzib/LetsEncrypt-PRTG/issues/3) 120 | $OutRootPem = "$PRTGCertRoot\root.pem" 121 | $resultTEMP = Set-Content -Path $OutRootPem -Value $OutCertificatePEM 122 | # Download LE Intermediate and Append to root.pem. 123 | $OutFile = Join-Path -Path $env:TEMP -ChildPath "LEIntermediate.txt" 124 | $resultTEMP = Invoke-WebRequest -Uri $LEIntermediateCA -OutFile $OutFile 125 | # Append $OutFile to root.pem 126 | Get-Content -Path $OutFile | Add-Content -Path $OutRootPem 127 | 128 | # Cleanup 129 | Remove-Item -Path $OutFile 130 | 131 | # Restart PRTG. 132 | if ($RestartPRTGCoreService) { 133 | Restart-Service -Name PRTGCoreService -Force 134 | } 135 | 136 | if ($RestartPRTGProbeService) { 137 | Restart-Service -Name RestartPRTGProbeService -Force 138 | } 139 | 140 | } 141 | } else { 142 | Throw "Certify did not renew certificate. Aboring without making changes." 143 | } 144 | Stop-Transcript -------------------------------------------------------------------------------- /Certify_the_Web/Documentation/Images/Certify-DeploymentMode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andyzib/LetsEncrypt-PRTG/590556df4b0ac4b354e52b91d49d1c6e328173bc/Certify_the_Web/Documentation/Images/Certify-DeploymentMode.png -------------------------------------------------------------------------------- /Certify_the_Web/Documentation/Images/Certify-DeploymentTask01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andyzib/LetsEncrypt-PRTG/590556df4b0ac4b354e52b91d49d1c6e328173bc/Certify_the_Web/Documentation/Images/Certify-DeploymentTask01.png -------------------------------------------------------------------------------- /Certify_the_Web/Documentation/Images/Certify-DeploymentTask02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andyzib/LetsEncrypt-PRTG/590556df4b0ac4b354e52b91d49d1c6e328173bc/Certify_the_Web/Documentation/Images/Certify-DeploymentTask02.png -------------------------------------------------------------------------------- /Certify_the_Web/Documentation/Images/Certify-DeploymentTask03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andyzib/LetsEncrypt-PRTG/590556df4b0ac4b354e52b91d49d1c6e328173bc/Certify_the_Web/Documentation/Images/Certify-DeploymentTask03.png -------------------------------------------------------------------------------- /Certify_the_Web/README.md: -------------------------------------------------------------------------------- 1 | # Certify the Web & PRTG 2 | 3 | This is a post request script to install a certificate aquired using Certify the Web into [PRTG](https://www.paessler.com/prtg). 4 | 5 | ## Deprecation / Retirement 6 | 7 | Since creating this script, Certify the Web has expanded Deployment Tasks to include "Deploy to Generic Server (multi-purpose)" which can write the needed files to PRTG's directory. Using this Deployment Task followed by a Run task to restart PRTG should be sufficient. As of Feburary 2022, I don't have plans on improving this script. If you find it useful over Certify's built in tasks, please submit a [GitHub Issue](https://github.com/andyzib/LetsEncrypt-PRTG/issues/new/choose) with your feature/enhancment requests. Thanks! 8 | 9 | ## Requirements 10 | 11 | * [PowerShell 5.1](https://docs.microsoft.com/en-us/skypeforbusiness/set-up-your-computer-for-windows-powershell/download-and-install-windows-powershell-5-1) 12 | * You should already have Windows PowerShell 5.1 if you're running Windows 10 Anniversary Update or Windows Server 2016. 13 | * [PSPKI](https://www.pkisolutions.com/tools/pspki) module that is available in the [PowerShell Gallery](https://www.powershellgallery.com/packages/PSPKI). 14 | * Tested/Verified with Certify the Web version 4.1.6.0 and 5.5.5.0. 15 | * Tested/Verified on Windows 10, Server 2012 R2, Server 2016, and Server 2019. 16 | 17 | ## Installation 18 | 19 | 1. If running on Windows 10, Install RSAT: Active Directory Certificate Services (Settings > Apps & features > Optional features) 20 | 2. Open PowerShell as an administrator and run Install-Module PSPKI 21 | 3. Save the script to a directory (Example: E:\Scripts\Certify-PRTG\Certify-PRTG.ps1) 22 | 4. Verify that $PRTGCertRoot variable matches your PRTG installation. 23 | 24 | ## Running Certify-PRTG.ps1 25 | 26 | The script must be configured as a Deployment Task in the Certify GUI. 27 | 28 | 1. Configure obtaining your certificate in Certify. See [Certify The Web - Docs](https://docs.certifytheweb.com/docs/intro) for instructions. 29 | 2. Deployment Mode: Certificate Store Only is the only option I've tested. 30 | ![Certify Deployment Mode](Documentation/Images/Certify-DeploymentMode.png) 31 | 3. Under Deployment Tasks, add a Run Powershell Script task. 32 | ![Select Run Powershell Script](Documentation/Images/Certify-DeploymentTask01.png) 33 | 4. Set Trigger to "Run On Success." 34 | ![Task General Settings](Documentation/Images/Certify-DeploymentTask02.png) 35 | 5. Use a security principal with appropriate permissions for your environment. 36 | 6. Pass Result as First Argument must be checked. 37 | 7. Request/Renew certificate as normal. If successful, cert will be added to PRTG and PRTG will be restarted. 38 | ![Task Parameters](Documentation/Images/Certify-DeploymentTask03.png) 39 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Andrew Zbikowski 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Let's Encrypt PRTG 2 | 3 | This is a collection of scripts to automatically install a Let's Encrypt certificate for [PRTG](https://www.paessler.com/prtg). 4 | 5 | ## Certify the Web 6 | 7 | Post request script to install a certificate obtained by [Certify the Web](https://certifytheweb.com/) into PRTG. [Documentation and Download](https://github.com/andyzib/LetsEncrypt-PRTG/tree/master/Certify_the_Web). 8 | 9 | ## Win-ACME 10 | 11 | Post request script to install a certificate obtained with [Win-ACME](https://www.win-acme.com/) in PRTG. [Documentation and Download](https://github.com/andyzib/LetsEncrypt-PRTG/tree/master/win-acme). 12 | -------------------------------------------------------------------------------- /win-acme/README.md: -------------------------------------------------------------------------------- 1 | # Win-ACME & PRTG 2 | 3 | This is a post request script to install a certificate acquired using Certify the Web into [PRTG](https://www.paessler.com/prtg). 4 | 5 | ## Requirements 6 | 7 | * [PowerShell 5.1](https://docs.microsoft.com/en-us/skypeforbusiness/set-up-your-computer-for-windows-powershell/download-and-install-windows-powershell-5-1) 8 | * You should already have Windows PowerShell 5.1 if you're running Windows 10 Anniversary Update or Windows Server 2016. 9 | * [Win-ACME](https://www.win-acme.com/) and associated plugins for your environment. 10 | 11 | ## Using Win-ACME 12 | 13 | This documentation doesn't cover how to use Win-ACME to acquire a certificate. Consult the [Win-ACME Manual](https://www.win-acme.com/manual/getting-started) and [Win-ACME Command Line Reference](https://www.win-acme.com/reference/cli). 14 | 15 | ## Prepare your system 16 | 17 | 1. Download Win-ACME and any plugins you need for your environment. 18 | 2. Create a directory where Win-ACME can store the PEM files for your ACME certificate. This directory should have limited permissions as the Private Key for your certificate will be stored unencrypted. 19 | 3. Create a directory where the post install script will be saved. 20 | 4. When running wacs.exe, select only the PEM files option as storage. Select the menu item or CLI parameter for no password for the private key. 21 | 5. Select run an external script or program for your install option. 22 | 6. Give the full path to the WACSPost-PRTG.ps1 script. 23 | 7. Use the following parameters: -CertCommonName {CertCommonName} -StorePath {StorePath} -StoreType {StoreType} -RestartPRTGCoreService 24 | 25 | ## About RestartPRTGCoreService 26 | 27 | This is an optional parameter that will restart PRTG Core Service after the script installs the new certificate acquired by wacs.exe. While restarting the PRTG Core Service is required for PRTG to use the new certificate, not every environment will want PRTG automatically restarted. I **strongly recommend** including -REstartPRTGCoreService. Adjust the timing of the win-acme task in Windows Task Scheduler to reflect your organization's maintenance windows to ensure your certificate doesn't expire. By default, win-acme creates a task that runs daily at 9 AM. You can run the task weekly or even once a month without concern as long as you are monitoring the certificate. You have PRTG, you should add a sensor that is monitoring PRTG's certificate expiration! 28 | 29 | ## About and RestartPRTGProbeService 30 | 31 | Restarting the PRTG Probe Service is not required in my experience, however [grumpymojo](https://github.com/grumpymojo) reported [having to do so](https://github.com/andyzib/LetsEncrypt-PRTG/issues/2) for their PRTG install. I've included the option to restart the PRTG Probe Service for anyone who finds they need it. 32 | 33 | ## Configuring the Install Script in wacs.exe interactive mode 34 | 35 | Coming Soon. 36 | 37 | ## Configuring the Install Script in wacs.exe non-interactive mode 38 | 39 | Coming Soon. 40 | -------------------------------------------------------------------------------- /win-acme/WACSPost-PRTG.ps1: -------------------------------------------------------------------------------- 1 | #requires -version 5 2 | <# 3 | .SYNOPSIS 4 | 5 | 6 | .DESCRIPTION 7 | 8 | 9 | .PARAMETER CertCommonName 10 | Mandatory. {CertCommonName} from wacs.exe - Common name (primary domain name) 11 | 12 | .PARAMETER StorePath 13 | Mandatory. {StorePath} from wacs.exe - Path or store name used by the store plugin 14 | 15 | .PARAMETER StoreType 16 | Mandatory. {StoreType} from wacs.exe - Name of the plugin (CentralSsl, CertificateStore or PemFiles) 17 | 18 | .PARAMETER RestartPRTGCoreService 19 | Optional. Switch paramater to have this script restart PRTG Core Service after installing the certificate. Restarting this service is REQUIRED for PRTG to see the new certificate. 20 | 21 | .PARAMETER RestartPRTGProbeService 22 | Optional. Switch parameter to have this script restart PRTG Probe Service after installing the certificate. Not required in the script author's experience, but other script users have reported otherwise. 23 | 24 | .PARAMETER TestRun 25 | Optional. TestRun allows the author to run tests on a system that does not have PRTG installed. :) 26 | 27 | .INPUTS 28 | None 29 | 30 | .OUTPUTS 31 | A backup directory will be created in the PRTG cert folder. The backup directory will have a backup copy of the certificate that was replaced and a log file. 32 | 33 | .NOTES 34 | Author: Andrew Zbikowski 35 | Latest version can be found at https://github.com/andyzib/LetsEncrypt-PRTG 36 | 37 | Available script parameters from wacs.exe: https://github.com/PKISharp/win-acme/wiki/Install-script 38 | * {0} or {CertCommonName} - Common name (primary domain name) 39 | * {1} or {CachePassword} - The .pfx password (generated randomly for each renewal) 40 | * {2} or {CacheFile} - Full path of the cached.pfx file 41 | * {4} or {CertFriendlyName} - Friendly name of the generated certificate 42 | * {5} or {CertThumbprint} - Thumbprint of the generated certificate 43 | * {7} or {RenewalId} - Id of the renewal 44 | * {3} or {6} or {StorePath} - Path or store name used by the store plugin 45 | * {StoreType} - Name of the plugin (CentralSsl, CertificateStore or PemFiles) 46 | 47 | .EXAMPLE 48 | WACSPost-PRTG.ps1 -CertCommonName {CertCommonName} -StorePath {StorePath} -StoreType {StoreType} -RestartPRTGCoreService 49 | #> 50 | 51 | #region Parameters 52 | [CmdletBinding()] 53 | 54 | # Advanced Parameters: https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_functions_advanced_parameters?view=powershell-6 55 | # Be sure to edit this to meet the validatations required as the allows and validate lines below may conflict with each other. 56 | Param ( 57 | [Parameter(Mandatory=$true, 58 | ValueFromPipeline=$false, 59 | Position=0, 60 | HelpMessage="Common name of the cert created by wacs.exe")] 61 | [ValidateNotNullOrEmpty()] 62 | [string]$CertCommonName, 63 | # Don't forget a comma between parameters. 64 | [Parameter(Mandatory=$true, 65 | ValueFromPipeline=$false, 66 | Position=1, 67 | HelpMessage="Path to the PEM files created by wacs.exe.")] 68 | [ValidateNotNullOrEmpty()] # Specifies that the parameter value cannot be $null and cannot be an empty string "". 69 | [ValidateScript({Test-Path -Path $_ -PathType Container})] # Specifies a script that is used to validate a parameter or variable value. 70 | [string]$StorePath, 71 | # Don't forget a comma between parameters. 72 | [Parameter(Mandatory=$true, 73 | ValueFromPipeline=$false, 74 | Position=3, 75 | HelpMessage="StoreType from wacs.exe. There is only one correct answer.")] 76 | [ValidateNotNullOrEmpty()] # Specifies that the parameter value cannot be $null and cannot be an empty string "". 77 | [ValidateSet("PemFiles")] # Specifies a set of valid values for a parameter or variable. 78 | [string]$StoreType, 79 | # Don't forget a comma between parameters. 80 | [Parameter(Mandatory=$false, 81 | ValueFromPipeline=$false, 82 | Position=4, 83 | HelpMessage="Restart the PRTG Core service (required for PRTG to use the new cert).")] 84 | [switch]$RestartPRTGCoreService, 85 | # Don't forget a comma between parameters. 86 | [Parameter(Mandatory=$false, 87 | ValueFromPipeline=$false, 88 | Position=5, 89 | HelpMessage="Restart the PRTG Probe service.")] 90 | [switch]$RestartPRTGProbeService, 91 | # Don't forget a comma between parameters. 92 | [Parameter(Mandatory=$false, 93 | ValueFromPipeline=$false, 94 | Position=6, 95 | HelpMessage="Perform a test run that skips some checks and makes no changes.")] 96 | [switch]$TestRun # FIXME: This is so I can develop and test on a computer that doesn't have PRTG installed. Change to a WhatIf when things are working. :) 97 | ) 98 | #endregion Parameters 99 | 100 | #region Declarations 101 | 102 | # ISO 8601 Date Format. Accept no substitutes! 103 | $iso8601 = Get-Date -Format s 104 | # Colon (:) isn't a valid character in file names. 105 | $iso8601 = $iso8601.Replace(":","_") 106 | # Just YYYY-MM-DD 107 | #$datestamp = $iso8601.Substring(0,10) 108 | 109 | if ($TestRun) { 110 | # For a TestRun, we don't need PRTG installed so just set the expected variable. 111 | $PRTGServerEXE = 'C:\Program Files (x86)\PRTG Network Monitor\64 bit\PRTG Server.exe' 112 | } else { 113 | $PRTGServerEXE = $(Get-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Services\PRTGCoreService" -Name ImagePath).ImagePath 114 | $PRTGServerEXE = $PRTGServerEXE.Trim('"') 115 | } 116 | 117 | $PRTGInstallDir = $PRTGServerEXE | Split-Path | Split-Path 118 | $PRTGCertDir = Join-Path -Path $PRTGInstallDir -ChildPath "cert" 119 | 120 | # Verify PRTG directory. 121 | if ( (Test-Path -Path $PRTGInstallDir -PathType Container) ) { 122 | Write-Host "PRTG installation directory found at $PRTGInstallDir." -ForegroundColor Green 123 | } else { 124 | # Behave deifferently on a test run. Might be testing on a PC without PRTG installed. 125 | if (-Not $TestRun) { 126 | Throw "PRTG installation directory NOT FOUND at $PRTGInstallDir." 127 | } else { 128 | Write-Host "PRTG installation directory NOT FOUND at $PRTGInstallDir." -ForegroundColor Red 129 | } 130 | } 131 | 132 | # Verify PRTG cert directory. 133 | if ( (Test-Path -Path $PRTGCertDir -PathType Container) ) { 134 | Write-Host "PRTG certificate directory found at $PRTGCertDir." -ForegroundColor Green 135 | } else { 136 | # Behave deifferently on a test run. Might be testing on a PC without PRTG installed. 137 | if (-Not $TestRun) { 138 | Throw "PRTG certificate directory NOT FOUND at $PRTGCertDir." 139 | } else { 140 | Write-Host "PRTG certificate directory NOT FOUND at $PRTGCertDir." -ForegroundColor Red 141 | } 142 | } 143 | 144 | #endregion Declarations 145 | 146 | #region Functions 147 | 148 | Function New-BackupDirectory { 149 | <# 150 | Creates the backup directory and also verify that the script can write to the PRTG cert folder. 151 | If this function fails, the script will exit without making changes and no log file will be created. (wacs.exe will write to Windows Event Log) 152 | #> 153 | Param ( 154 | [Parameter(Mandatory=$true)] 155 | [string]$PRTGCertDir, 156 | [Parameter(Mandatory=$true)] 157 | [string]$iso8601 158 | ) 159 | $DirName = "Backup_$iso8601" 160 | $Dir = New-Item -Path $PRTGCertDir -Name $DirName -Type Directory -ErrorAction Continue 161 | if ($null -eq $Dir) { 162 | # Failed to create backup directory. Not good. 163 | Return $null 164 | } else { 165 | Return $Dir.FullName 166 | } 167 | } 168 | #end Function New-BackupDirectory 169 | Function Backup-PRTGCert { 170 | <# 171 | Creates a backup of the PRTG certificate currently in use. Only creats a copy, no changes to the existing cert. 172 | #> 173 | Param ( 174 | [Parameter(Mandatory=$true)] 175 | [string]$SourceDir, 176 | [Parameter(Mandatory=$true)] 177 | [string]$DestinationDir 178 | ) 179 | 180 | $Files = @( 181 | # prtg.crt 182 | [pscustomobject]@{ 183 | Source = Join-Path -Path $SourceDir -ChildPath 'prtg.crt' 184 | Destination = Join-Path -Path $DestinationDir -ChildPath 'prtg.crt' 185 | }, 186 | # prtg.key 187 | [pscustomobject]@{ 188 | Source = Join-Path -Path $SourceDir -ChildPath 'prtg.key' 189 | Destination = Join-Path -Path $DestinationDir -ChildPath 'prtg.key' 190 | }, 191 | # root.pem 192 | [pscustomobject]@{ 193 | Source = Join-Path -Path $SourceDir -ChildPath 'root.pem' 194 | Destination = Join-Path -Path $DestinationDir -ChildPath 'root.pem' 195 | } 196 | ) 197 | 198 | foreach ($file in $Files) { 199 | Write-Host "Backing up $($file.Source) to $($file.Destination)..." 200 | Copy-Item -Path $file.Source -Destination $file.Destination 201 | if ( -Not (Test-Path -Path $file.Destination) ) { 202 | Write-Host "Unknown error when backing up $($file.Destination)." -ForegroundColor Red 203 | Write-Host "Unable to continue due to backup error. Exiting." -ForegroundColor Red 204 | Stop-Transcript 205 | Exit 206 | } 207 | Write-Host "Done!" -ForegroundColor Green 208 | Write-Host "" 209 | } 210 | 211 | # If we've gotten this far, the backup didn't fail so move to the next step. 212 | Return $true 213 | 214 | } 215 | #end Function Backup-PRTGCert 216 | Function Remove-OldPRTGCert { 217 | <# 218 | Removes the current PRTG cert from the cert directory. Returns $false if removal failed. 219 | #> 220 | Param ( 221 | [Parameter(Mandatory=$true)] 222 | [string]$PRTGCertDir 223 | ) 224 | $Files = @( 225 | $(Join-Path -Path $PRTGCertDir -ChildPath 'prtg.crt'), 226 | $(Join-Path -Path $PRTGCertDir -ChildPath 'prtg.key'), 227 | $(Join-Path -Path $PRTGCertDir -ChildPath 'root.pem') 228 | ) 229 | 230 | $result = $true # Only change to false if a file exists after removal attempt. 231 | foreach ($file in $Files) { 232 | Write-Host "Deleting $file" 233 | Remove-Item -Path $file 234 | if (Test-Path -Path $file -PathType Leaf) { 235 | $result = $false 236 | } 237 | } 238 | Return $result # Result will be $true if files were successfuly deleted, false if delete failed. 239 | } 240 | #end Remove-OldPRTGCert 241 | Function Install-NewPRTGCert { 242 | <# 243 | Copies the files from wacs.exe to the PRTG cert directory. Renames the wacs.exe files to what PRTG expects. 244 | #> 245 | Param ( 246 | [Parameter(Mandatory=$true)] 247 | [string]$CertCommonName, 248 | [Parameter(Mandatory=$true)] 249 | [string]$StorePath, 250 | [Parameter(Mandatory=$true)] 251 | [string]$PRTGCertDir 252 | ) 253 | # Setup an array of custom objects to make this easier. 254 | $Files = @( 255 | # prtg.crt 256 | [pscustomobject]@{ 257 | Source = Join-Path -Path $StorePath -ChildPath "$($CertCommonName)-crt.pem" 258 | Destination = Join-Path -Path $PRTGCertDir -ChildPath 'prtg.crt' 259 | }, 260 | # prtg.key 261 | [pscustomobject]@{ 262 | Source = Join-Path -Path $StorePath -ChildPath "$($CertCommonName)-key.pem" 263 | Destination = Join-Path -Path $PRTGCertDir -ChildPath 'prtg.key' 264 | }, 265 | # root.pem 266 | [pscustomobject]@{ 267 | Source = Join-Path -Path $StorePath -ChildPath "$($CertCOmmonName)-chain-only.pem" 268 | Destination = Join-Path -Path $PRTGCertDir -ChildPath 'root.pem' 269 | } 270 | ) 271 | # Copy from $StorePath to $PRTGCertDir. 272 | $return = $true 273 | foreach ($file in $Files) { 274 | Write-Host "Copying $($file.Source) to $($file.Destination )... " 275 | Copy-Item -Path $file.Source -Destination $file.Destination 276 | if ( -Not (Test-Path -Path $file.Destination -PathType Leaf ) ) { 277 | $return = $false # File failed to copy, restore backup and fail. 278 | Write-Host "Failed." -ForegroundColor Red 279 | } else { 280 | Write-Host "Done." -ForegroundColor Green 281 | } 282 | Write-Host "" 283 | } 284 | Return $return 285 | } 286 | #end Install-NewPRTGCert 287 | Function Restore-PRTGCert { 288 | <# 289 | If something failed, this function is called to restore the certificate from the backup. 290 | #> 291 | Param ( 292 | [Parameter(Mandatory=$true)] 293 | [string]$Backup, 294 | [Parameter(Mandatory=$true)] 295 | [string]$PRTGCertDir 296 | ) 297 | $BackupDir = Join-Path -Path $PRTGCertDir -ChildPath $PRTGCertDir 298 | if ( -Not (Test-Path -Path $BackupDir -PathType Container) ) { 299 | $Message = "Backup directory $BackupDir was not found. Manual intervention to restore a valid certificate is required." 300 | Write-Host $Message -ForegroundColor Red 301 | Stop-Transcript 302 | Throw $Message 303 | } 304 | 305 | # Setup Files Array to make this easier. 306 | $Files = @( 307 | # prtg.crt 308 | [pscustomobject]@{ 309 | Source = Join-Path -Path $BackupDir -ChildPath 'prtg.crt' 310 | Destination = Join-Path -Path $PRTGCertDir -ChildPath 'prtg.crt' 311 | }, 312 | # prtg.key 313 | [pscustomobject]@{ 314 | Source = Join-Path -Path $BackupDir -ChildPath 'prtg.key' 315 | Destination = Join-Path -Path $PRTGCertDir -ChildPath 'prtg.key' 316 | }, 317 | # root.pem 318 | [pscustomobject]@{ 319 | Source = Join-Path -Path $BackupDir -ChildPath 'root.pem' 320 | Destination = Join-Path -Path $PRTGCertDir -ChildPath 'root.pem' 321 | } 322 | ) 323 | 324 | # Copy from BackupDir to CertDir 325 | $return = $true # Change to false if a copy fails. 326 | foreach ($file in $Files) { 327 | Copy-Item -Path $file.Source -Destination $file.Destination 328 | if ( -Not (Test-Path -Path $file.Destination -PathType Leaf) ) { 329 | $return = $false 330 | } 331 | } 332 | Return $return 333 | } 334 | #end Restore-PRTGCert 335 | 336 | #region Function Invoke-ISO8601Cleanup 337 | Function Invoke-ISO8601Cleanup { 338 | <# 339 | .SYNOPSIS 340 | Cleans up files/directories that use YYYY-MM-DD naming. 341 | 342 | .DESCRIPTION 343 | Cleans up files/directories that use YYYY-MM-DD naming. 344 | 345 | .PARAMETER KeepNum 346 | The number of matching files/directories to keep on disk. 347 | 348 | .PARAMETER BaseDir 349 | Directory containing files/directories. 350 | 351 | .EXAMPLE 352 | Keep only the last 7 matching directoreis: Invoke-ISO8601Cleanup KeepNum 7 -BaseDir servershareLogs 353 | #> 354 | Param( 355 | [Parameter(Mandatory=$true, 356 | ValueFromPipeline=$false, 357 | HelpMessage="Keep this number of backups on disk.")] 358 | [ValidateNotNullOrEmpty()] # Specifies that the parameter value cannot be $null and cannot be an empty string . 359 | [ValidateScript({$_ -gt 0})] # Specifies a script that is used to validate a parameter or variable value. 360 | [int]$KeepNum, 361 | # Don't forget a comma between parameters. 362 | [Parameter(Mandatory=$true, 363 | ValueFromPipeline=$false, 364 | HelpMessage="Directory containing backups.")] 365 | [ValidateNotNullOrEmpty()] 366 | [ValidateScript({ Test-Path -Path $_ -PathType Container })] # Specifies a script that is used to validate a parameter or variable value. 367 | [string]$BaseDir, 368 | # Don't forget a comma between parameters. 369 | [Parameter(Mandatory=$false, 370 | ValueFromPipeline=$false, 371 | HelpMessage="Filter for Get-ChildItem to use for selecting files/directories to consider for delete.")] 372 | [string]$Filter="Backup_20*", 373 | # Don't forget a comma between parameters. 374 | [Parameter(Mandatory=$false, 375 | ValueFromPipeline=$false, 376 | HelpMessage="Enable WhatIf Mode.")] 377 | [switch]$WhatIf 378 | ) 379 | 380 | # Only keep the number of backups specified by $KeepNum. 381 | if ( $(Get-ChildItem -Path $BaseDir).Count -gt $KeepNum ) { 382 | Write-Host Performing log directory cleanup. 383 | $DeleteFolders = Get-ChildItem -Path $BaseDir -Filter $Filter | Sort-Object -Descending | Select-Object -Skip $KeepNum 384 | Foreach ($DelFolder in $DeleteFolders) { 385 | if ($WhatIf) { 386 | Remove-Item -LiteralPath $DelFolder.FullName -Recurse -Force -WhatIf 387 | } else { 388 | Remove-Item -LiteralPath $DelFolder.FullName -Recurse -Force 389 | } 390 | } 391 | } else { 392 | Write-Host "$KeepNum or fewer backups stored on disk. Nothing to delete." 393 | } 394 | } 395 | #endregion Function Invoke-ISO8601Cleanup 396 | 397 | #endregion Functions 398 | 399 | 400 | 401 | <# Pseudocode 402 | 403 | 1. Create the backup directory, exit script on fail: New-BackupDirectory 404 | 2. Start the transcript, writing to the backup directory. 405 | 3. Backup exiting cert files. 406 | 4. Delete existing cert files. 407 | 5. Install new cert files. 408 | 6. Restart-PRTGServices. 409 | 410 | If step 3, 4, or 5 fails back out cleanly. Restoring original files if required. 411 | 412 | End Pseudocode #> 413 | 414 | 415 | # 1. Create the backup directory, exit script on fail: New-BackupDirectory 416 | $BackupDir = New-BackupDirectory -PRTGCertDir $PRTGCertDir -iso8601 $iso8601 417 | if ($null -eq $BackupDir) { 418 | Write-Host "Unable to create backup directory for current PRTG certificate. Script cannot safely continue." -ForegroundColor Red 419 | Throw "Creating backup directory failed. Exiting without making changes." 420 | } 421 | 422 | # 2. Start the transcript, writing to the backup directory. 423 | $TranscriptFile = Join-Path -Path $BackupDir -ChildPath "$($MyInvocation.MyCommand.Name)_LOG.txt" 424 | Start-Transcript -Path $TranscriptFile 425 | Write-Host "" 426 | 427 | # 3. Backup exiting cert files. 428 | Write-Host "Backing up current certificate files to $BackupDir." -ForegroundColor Cyan 429 | Write-Host "" 430 | $BackupResult = Backup-PRTGCert -SourceDir $PRTGCertDir -DestinationDir $BackupDir 431 | if ($BackupResult -eq $false) { 432 | Write-Host "Backup of the current PRTG certificate failed. Script cannot safely continue." -ForegroundColor Red 433 | Write-Host "No changes were made to curent PRTG certificate." -ForegroundColor Yellow 434 | Throw "Backup of current certificate failed. Exiting without making changes." 435 | } 436 | Write-Host "Backup of current certificate files complete." -ForegroundColor Green 437 | Write-Host "" 438 | 439 | # 4. Delete existing cert files. 440 | Write-Host "Deleting the old certificate files." -ForegroundColor Cyan 441 | Write-Host "" 442 | $Deleted = Remove-OldPRTGCert -PRTGCertDir $PRTGCertDir 443 | 444 | if ($Deleted -eq $false) { 445 | $Restored = Restore-PRTGCert -Backup $iso8601 -PRTGCertDir $PRTGCertDir 446 | if ($Restored) { 447 | Write-Host "Removing old certificate failed, but changes were successfully reverted." -ForegroundColor Yellow 448 | } else { 449 | Write-Host "Removing old certificate failed, and changes could not be reverted. Manual intervention is required!" -ForegroundColor Red 450 | } 451 | Write-Host "" 452 | Stop-Transcript 453 | Exit 454 | } else { 455 | Write-Host "" 456 | Write-Host "Old certificate files were successfully removed." -ForegroundColor Green 457 | Write-Host "" 458 | } 459 | 460 | # 5. Install new cert files. 461 | Write-Host "Installing new certificate." -ForegroundColor Cyan 462 | Write-Host "" 463 | $Installed = Install-NewPRTGCert -CertCommonName $CertCommonName -StorePath $StorePath -PRTGCertDir $PRTGCertDir 464 | if ($Installed -eq $false) { 465 | $Restored = Restore-PRTGCert -Backup $iso8601 -PRTGCertDir $PRTGCertDir 466 | if ($Restored) { 467 | Write-Host "New certificate installation failed, but changes were successfully reverted." -ForegroundColor Yellow 468 | } else { 469 | Write-Host "New certificate installation failed, and changes could not be reverted. Manual intervention is required!" -ForegroundColor Red 470 | } 471 | Stop-Transcript 472 | Exit 473 | } else { 474 | Write-Host "New certificate installation was successful!" -ForegroundColor Green 475 | Write-Host "" 476 | } 477 | 478 | # 6. Restart-PRTGServices. 479 | 480 | if ($RestartPRTGCoreService) { 481 | Write-Host "Restarting PRTGCoreService." 482 | Restart-Service -Name PRTGCoreService -Force 483 | Write-Host "Done!" -ForegroundColor Green 484 | Write-Host "" 485 | } 486 | 487 | if ($RestartPRTGProbeService) { 488 | Write-Host "Restarting RestartPRTGProbeService." 489 | Restart-Service -Name PRTGProbeService -Force 490 | Write-Host "Done!" -ForegroundColor Green 491 | Write-Host "" 492 | } 493 | 494 | Write-Host "Certificate post-install script completed. Review logs for errors, happy monitoring!" -ForegroundColor Cyan 495 | Write-Host "" 496 | 497 | Write-Host "Cleaning up old backup directories." -ForegroundColor Cyan 498 | Invoke-ISO8601Cleanup -KeepNum 3 -BaseDir $PRTGCertDir 499 | 500 | Stop-Transcript 501 | --------------------------------------------------------------------------------