├── Install-ADCSOfflineCA.ps1 ├── Install-ADCSSubordinateCA.ps1 ├── LICENSE ├── README.md └── images ├── 0_PKI.png ├── 10_PKI.png ├── 11_PKI.png ├── 12_PKI.png ├── 13_PKI.png ├── 14_PKI.png ├── 15_PKI.png ├── 16_PKI.png ├── 17_PKI.png ├── 18_PKI.png ├── 19_PKI.png ├── 1_PKI.png ├── 20_PKI.png ├── 21_PKI.png ├── 22_PKI.png ├── 23_PKI.png ├── 24_PKI.png ├── 25_PKI.png ├── 26_PKI.png ├── 27_PKI.png ├── 28_PKI.png ├── 29_PKI.png ├── 2_PKI.png ├── 30_PKI.png ├── 31_PKI.png ├── 32_PKI.png ├── 33_PKI.png ├── 34_PKI.png ├── 35_PKI.png ├── 36_PKI.png ├── 37_PKI.png ├── 38_PKI.png ├── 39_PKI.png ├── 3_PKI.png ├── 40_PKI.png ├── 41_PKI.png ├── 42_PKI.png ├── 43_PKI.png ├── 44_PKI.png ├── 45_PKI.png ├── 46_PKI.png ├── 47_PKI.png ├── 4_PKI.png ├── 5_PKI.png ├── 6_PKI.png ├── 7_PKI.png ├── 8_PKI.png └── 9_PKI.png /Install-ADCSOfflineCA.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Installs the Root (Offline) Certificate Authority role and configures it. 4 | 5 | .DESCRIPTION 6 | Script developed for to install a Root (Offline) Certificate Authority. 7 | The configuration uses certutil.exe to modify the CA settings. 8 | Tested on Windows Server 2012R2 and Server 2016. 9 | Never use a network connection an a Root (Offline) Certificate Authority 10 | 11 | .EXAMPLE 12 | Install-ADCSOfflineCA.ps1 -Company DEMO -DomainURL pki.demo.com -ConfigNC 'CN=Configuration,DC=demo,DC=lab' 13 | 14 | This will install the install and configure the Certificate Authority Service with the CA name "DEMO-Root-CA". 15 | It will create the PKI folder in the default location ("$env:SystemDrive\PKI"). 16 | The PKI folder contains the Database and paths for AIA and CRL. 17 | 18 | .EXAMPLE 19 | Install-ADCSOfflineCA.ps1 -Company Contoso -DomainURL pki.contoso.com -ConfigNC 'CN=Configuration,DC=contoso,DC=com' -LocalPKIPath E:\CALocation 20 | 21 | This will install the install and configure the Certificate Authority Service with the CA name "Contoso-Root-CA". 22 | It will create a folder named PKI in CALocation on the disk E:\. 23 | The PKI folder contains the Database and paths for AIA and CRL. 24 | 25 | 26 | .NOTES 27 | Created on: 2016-05-11 09:15 28 | Created by: Philip Haglund 29 | Organization: Gonjer.com 30 | Filename: Install-ADCSOfflineCA.ps1 31 | Version: 0.4 32 | Requirements: Powershell 4.0 (Module: ServerManager) 33 | Changelog: 2016-05-11 09:15 - Creation of script 34 | 2016-09-19 16:20 - Removed LDAP paths CRL:"\n10:ldap:///CN=%7%8,CN=%2,CN=CDP,CN=Public Key Services,CN=Services,%6%10" AIA:"\n2:ldap:///CN=%7,CN=AIA,CN=Public Key Services,CN=Services,%6%11" 35 | 2016-10-07 15:21 - Minor bugfixes and corrections based on PSSharper. 36 | 2017-01-10 15:34 - Correction of typos. Clean up help text. 37 | .LINK 38 | https://www.gonjer.com 39 | #> 40 | #requires -Version 4.0 41 | [cmdletbinding()] 42 | param ( 43 | 44 | # Company name that will belong in the Certificate Authority Name. 45 | # Example: 'Contoso' will be "Contoso-ROOT-CA" 46 | [Parameter( 47 | Mandatory = $true, 48 | HelpMessage = "Company name that will belong in the Certificate Authority Name.`nExample: 'Contoso'`n'Contoso' will be 'Contoso-ROOT-CA'" 49 | )] 50 | [string]$Company, 51 | 52 | # URL for CRL and AIA publishing from the Subordinate CA. 53 | # Example: 'pki.contoso.com' 54 | [Parameter( 55 | Mandatory = $true, 56 | HelpMessage = "Domain URL for CRL and AIA publishing.`nExample: 'pki.contoso.com'" 57 | )] 58 | [string]$DomainURL, 59 | 60 | # Active Directory Configuration Naming Context where the Subordinate Certificate Authority will be placed. 61 | # Example: 'CN=Configuration,DC=contoso,DC=com' 62 | [Parameter( 63 | Mandatory = $true, 64 | HelpMessage = "Active Directory Configuration Naming Context where the Subordinate Certificate Authority will be placed.`nExample: 'CN=Configuration,DC=contoso,DC=com'" 65 | )] 66 | [string]$ConfigNC, 67 | 68 | # Local file path to store Certificate Database and Logs. Also used for creating Web directory and fileshare location. 69 | # Example: 'C:\PKI' 70 | [Alias('LocalPath')] 71 | [string]$LocalPKIPath = "$env:SystemDrive\PKI" 72 | 73 | ) 74 | begin 75 | { 76 | function Confirm-ToContinue 77 | { 78 | $caption = 'Manual Step' 79 | $message = 'Are you done with the manual step?' 80 | [int]$defaultChoice = 1 81 | $yes = New-Object -TypeName System.Management.Automation.Host.ChoiceDescription -ArgumentList '&Yes', 'Done with manual step.' 82 | $no = New-Object -TypeName System.Management.Automation.Host.ChoiceDescription -ArgumentList '&No', 'Not done, continue to prompt.' 83 | $cancel = New-Object -TypeName System.Management.Automation.Host.ChoiceDescription -ArgumentList '&Cancel', 'Not done, exit script.' 84 | $options = [Management.Automation.Host.ChoiceDescription[]]($yes, $no, $cancel) 85 | 86 | $do = $true 87 | do 88 | { 89 | $choice = $Host.ui.PromptForChoice($caption,$message, $options,$defaultChoice) 90 | switch ($choice) 91 | { 92 | 0 93 | { 94 | $do = $false 95 | } 96 | 1 97 | { 98 | 'Issued: Not done, continue to prompt.' 99 | $do = $true 100 | } 101 | Default 102 | { 103 | 'Cancel, exiting script.' 104 | $do = $false 105 | break 106 | } 107 | } 108 | } 109 | while ($do) 110 | } 111 | 112 | # Clear all errors 113 | $Error.Clear() 114 | } 115 | process 116 | { 117 | if ($PSCmdlet.ShouldProcess("$($Company)($($DomainURL)) - $($LocalPKIPath)",'Configure Offline CA')) 118 | { 119 | #region Create directories 120 | $certdb = New-Item -Path "$($LocalPKIPath)\Database\CertDB" -ItemType Directory -Force 121 | $certlog = New-Item -Path "$($LocalPKIPath)\Database\CertLog" -ItemType Directory -Force 122 | $null = New-Item -Path "$($LocalPKIPath)\Web" -ItemType Directory -Force 123 | $crlpath = New-Item -Path "$($LocalPKIPath)\Web\CRL" -ItemType Directory -Force 124 | $aiapath = New-Item -Path "$($LocalPKIPath)\Web\AIA" -ItemType Directory -Force 125 | #endregion Create directories 126 | 127 | #region Install Windowsfeature ADCS-Cert-Authority 128 | try 129 | { 130 | $null = Install-WindowsFeature -Name ADCS-Cert-Authority -IncludeManagementTools 131 | } 132 | catch 133 | { 134 | Write-Warning -Message 'Unable to install Windows Feature ADCS-Cert-Authority' 135 | Write-Warning -Message "$($_.Exception.Message)" 136 | return 137 | } 138 | #endregion Install Windowsfeature ADCS-Cert-Authority 139 | 140 | #region Generate a CAPolicy.inf file for "$($env:windir)\CAPolicy.inf" 141 | $capolicy = '[Version] 142 | Signature="$Windows NT$" 143 | 144 | [PolicyStatementExtension] 145 | Policies=InternalUseOnly 146 | 147 | [Certsrv_Server] 148 | RenewalKeyLength=4096 149 | RenewalValidityPeriod=Years 150 | RenewalValidityPeriodUnits=10 151 | CRLPeriod=Years 152 | CRLPeriodUnits=1 153 | CRLDeltaPeriod=Days 154 | CRLDeltaPeriodUnits=0 155 | 156 | [BasicConstraintsExtension] 157 | PathLength=1 158 | Critical=Yes 159 | 160 | [CRLDistributionPoint] 161 | Empty=True 162 | 163 | [AuthorityInformationAccess] 164 | Empty=True' 165 | 166 | $capolicy | Out-File -FilePath "$($env:windir)\capolicy.inf" -Encoding default -Force 167 | #region Generate a CAPolicy.inf file for "$($env:windir)\CAPolicy.inf" 168 | 169 | #region Install-AdcsCertificationAuthority 170 | try 171 | { 172 | $null = Install-AdcsCertificationAuthority -CAType StandaloneRootCA ` 173 | -CACommonName "$Company-ROOT-CA" ` 174 | -CADistinguishedNameSuffix "OU=PKI,O=$Company,C=SE" ` 175 | -KeyLength 4096 ` 176 | -HashAlgorithmName SHA256 ` 177 | -CryptoProviderName 'RSA#Microsoft Software Key Storage Provider' ` 178 | -DatabaseDirectory $($certdb.FullName) ` 179 | -LogDirectory $($certlog.FullName) ` 180 | -ValidityPeriod Years ` 181 | -ValidityPeriodUnits 10 ` 182 | -OverwriteExistingKey ` 183 | -WarningAction SilentlyContinue ` 184 | -ErrorAction Stop ` 185 | } 186 | catch 187 | { 188 | Write-Warning -Message 'Unable to Install-AdcsCertificationAuthority -CAType StandaloneRootCA' 189 | Write-Warning -Message "$($_.Exception.Message)" 190 | Write-Output -InputObject 'Manual install AdcsCertificationAuthority with the following properties:' 191 | Write-Output -InputObject " 192 | -CAType StandaloneRootCA 193 | -CACommonName '$Company-ROOT-CA' 194 | -CADistinguishedNameSuffix 'OU=PKI,O=$Company,C=SE' 195 | -KeyLength 4096 196 | -HashAlgorithmName SHA256 197 | -CryptoProviderName 'RSA#Microsoft Software Key Storage Provider' 198 | -DatabaseDirectory '$($certdb.FullName)' 199 | -LogDirectory '$($certlog.FullName)' 200 | -ValidityPeriod Years 201 | -ValidityPeriodUnits 10" 202 | Confirm-ToContinue 203 | } 204 | #endregion Install-AdcsCertificationAuthority 205 | 206 | 207 | # Declare Configuration and Domain NCs 208 | $null = & "$env:windir\system32\certutil.exe" -setreg ca\DSConfigDN $ConfigNC 209 | 210 | # Set Validity Period for Issued Certificates (Subordinate) to 5 years 211 | $null = & "$env:windir\system32\certutil.exe" -setreg ca\ValidityPeriodUnits 5 212 | $null = & "$env:windir\system32\certutil.exe" -setreg ca\ValidityPeriod 'Years' 213 | 214 | # Define CRL Publication Intervals 215 | $null = & "$env:windir\system32\certutil.exe" -setreg ca\CRLPeriodUnits 52 216 | $null = & "$env:windir\system32\certutil.exe" -setreg ca\CRLPeriod 'Weeks' 217 | $null = & "$env:windir\system32\certutil.exe" -setreg ca\CRLDeltaPeriodUnits 0 218 | $null = & "$env:windir\system32\certutil.exe" -setreg ca\CRLDeltaPeriod 'Days' 219 | $null = & "$env:windir\system32\certutil.exe" -setreg ca\CRLOverlapPeriodUnits 12 220 | $null = & "$env:windir\system32\certutil.exe" -setreg ca\CRLOverlapPeriod 'Hours' 221 | 222 | # Enable CA Audit 223 | $null = & "$env:windir\system32\certutil.exe" -setreg CA\AuditFilter 127 224 | 225 | #region Remove any CRL Distribution Points 226 | $crllist = Get-CACrlDistributionPoint -ErrorAction SilentlyContinue 227 | foreach ($crl in $crllist) 228 | { 229 | $null = Remove-CACrlDistributionPoint -Uri $crl.uri -Force -ErrorAction SilentlyContinue 230 | } 231 | #endregion Remove any CRL Distribution Points 232 | 233 | 234 | #region Remove any certificates Authority Information Access 235 | $aialist = Get-CAAuthorityInformationAccess -ErrorAction SilentlyContinue 236 | foreach ($aia in $aialist) 237 | { 238 | $null = Remove-CAAuthorityInformationAccess -Uri $aia.uri -Force -ErrorAction SilentlyContinue 239 | } 240 | #endregion Remove any Authority Information Access certificates 241 | 242 | # Set New correct URLs for CRL and AIA 243 | $null = & "$env:windir\system32\certutil.exe" -setreg CA\CRLPublicationURLs "1:$($crlpath.FullName)\%3%8.crl\n2:http://$($DomainURL)/CRL/%3%8.crl" 244 | $null = & "$env:windir\system32\certutil.exe" -setreg CA\CACertPublicationURLs "1:$($aiapath.FullName)\%3%4.crt\n2:http://$($DomainURL)/AIA/%3%4.crt" 245 | #& "$env:windir\system32\certutil.exe" -getreg ca\CRLDeltaPeriodUnits 246 | 247 | 248 | Start-Sleep -Seconds 3 249 | try 250 | { 251 | $null = Stop-Service -Name 'certsvc' -Force -ErrorAction Stop -WarningAction SilentlyContinue 252 | } 253 | catch 254 | { 255 | Write-Warning -Message 'The Certificate Services (certsvc) did not stop correctly.' 256 | Write-Output -InputObject 'Please stop the service manually.' 257 | Confirm-ToContinue 258 | } 259 | try 260 | { 261 | $null = Start-Service -Name 'certsvc' -Confirm:$false -WarningAction SilentlyContinue -ErrorAction Stop 262 | } 263 | catch 264 | { 265 | Write-Warning -Message 'The Certificate Services (certsvc) did not start correctly.' 266 | Write-Output -InputObject 'Please start the service manually.' 267 | Confirm-ToContinue 268 | } 269 | 270 | #region Copy all AIA (Certificates) from the original store to the new AIA-Path 271 | try 272 | { 273 | Copy-Item -Path "$($env:windir)\system32\certsrv\certenroll\*.crt" -Destination $aiapath.FullName -ErrorAction Stop 274 | } 275 | catch 276 | { 277 | Write-Warning -Message "Unable to Copy-Item $($env:windir)\system32\certsrv\certenroll\*.crt to $($aiapath.FullName)" 278 | Write-Warning -Message "$($_.Exception.Message)" 279 | Write-Output -InputObject "Do a manual copy of '($env:windir)\system32\certsrv\certenroll\*.crt' to '$($aiapath.FullName)'" 280 | Confirm-ToContinue 281 | } 282 | #region Copy all AIA (Certificates) from the original store to the new AIA-Path 283 | 284 | #region View all errors 285 | if ($Error.Count -eq 0) 286 | { 287 | Clear-Host 288 | } 289 | else 290 | { 291 | Write-Output -InputObject 'Errors were deteted while running the script, displaying errors:' 292 | Start-Sleep -Milliseconds 500 293 | foreach ($e in $Error) 294 | { 295 | "$($e.Exception.Message)" 296 | } 297 | Write-Output -InputObject 'Does everything look OK?' 298 | Confirm-ToContinue 299 | } 300 | #region View all errors 301 | 302 | Write-Output -InputObject "`nFinished installing and configuring the Root Certificate Authority." 303 | Write-Output -InputObject 'The next step is to install the Subordinate Certificate Authority.' 304 | } 305 | } 306 | end 307 | { 308 | 309 | } -------------------------------------------------------------------------------- /Install-ADCSSubordinateCA.ps1: -------------------------------------------------------------------------------- 1 | #requires -Modules NetTCPIP, ServerManager 2 | <# 3 | .SYNOPSIS 4 | Installs the Subordinate Certificate Authority role and configures it. 5 | 6 | .DESCRIPTION 7 | Script developed for to install a Subordinate Certificate Authority when a Root (Offline) Certificate Authority is already installed and configured. 8 | The configuration uses certutil.exe to modify the CA settings. 9 | Tested on Windows Server 2012R2 and Server 2016. 10 | 11 | .EXAMPLE 12 | Install-ADCSSubordinateCA.ps1 -Company Demo -DomainURL pki.demo.com -SMTPServer smtp.demo.com -ToAddress 'recipient@demo.com' -FromAddress 'sender@demo.com' -City 'Gothenburg' -State 'VG' 13 | 14 | This will install the install and configure the Certificate Authority Service with the CA name "Demo-Subordinate-CA". 15 | It will create the PKI folder in the default location ("$env:SystemDrive\PKI"). 16 | The PKI folder contains the Database and paths for AIA and CRL files. 17 | A Web Virtual Directory will be created with the name PKI mapped to "$env:SystemDrive\PKI\Web" 18 | Three scheduled tasks will be created containg backup scripts and a PKI health information script. 19 | The ADCS Web Enrollment page will contain information used in the City and State parameters. 20 | 21 | 22 | .EXAMPLE 23 | Install-ADCSSubordinateCA.ps1 -Company Contoso -DomainURL pki.contoso.com -LocalPKIPath E:\CALocation -SMTPServer smtp.contoso.com -ToAddress 'recipient@contoso.com' -FromAddress 'sender@contoso.com' -City 'Copenhagen' -State 'Hovedstaden' 24 | 25 | This will install the install and configure the Certificate Authority Service with the CA name "Contoso-Subordinate-CA". 26 | It will create a folder named CALocation in E:\. 27 | The PKI (CALocation) folder contains the Database and paths for AIA and CRL files. 28 | A Web Virtual Directory will be created with the name PKI mapped to "E:\CALocation\Web" 29 | Three scheduled tasks will be created containg backup scripts and a PKI health information script. 30 | The ADCS Web Enrollment page will contain information used in the City and State parameters. 31 | 32 | 33 | .INPUTS 34 | String 35 | IO.DirectoryInfo 36 | mailaddress 37 | 38 | .NOTES 39 | Created on: 2016-05-11 09:15 40 | Created by: Philip Haglund 41 | Organization: Gonjer.com 42 | Filename: Install-ADCSSubordinateCA.ps1 43 | Version: 0.5 44 | Requirements: Powershell 4.0 (Module: NetTCPIP, ServerManager) 45 | Changelog: 2016-05-11 09:15 - Creation of script 46 | 2016-09-19 16:25 - Removed LDAP paths CRL: "\n79:ldap:///CN=%7%8,CN=%2,CN=CDP,CN=Public Key Services,CN=Services,%6%10" AIA: \n3:ldap:///CN=%7,CN=AIA,CN=Public Key Services,CN=Services,%6%11 47 | 2016-09-20 09:10 - Removed SMB Share requirements. 48 | 2016-10-07 15:21 - Completly changed the prompt text for the manual steps. Minor bugfixes and corrections based on PSSharper. 49 | 2017-01-10 14:03 - Set web.config to hidden. Added Group Policy recommendation for auto enrollment. 50 | 2017-01-11 14:57 - Added functions 'Register-CABackup' and 'Add-ScheduledPKIMaintenance'. Change ValidityPeriod to 30 months instead of 2 years. 51 | .LINK 52 | https://www.gonjer.com 53 | #> 54 | #requires -Version 4.0 55 | [cmdletbinding( 56 | SupportsShouldProcess = $true 57 | )] 58 | param ( 59 | 60 | # Company name that will belong in the Certificate Authority Name. 61 | # Example: 'DEMO' will be "DEMO-Subordinate-CA" 62 | [Parameter( 63 | Mandatory = $true, 64 | HelpMessage = "Company name that will belong in the Certificate Authority Name.`nExample: 'DEMO'`n'DEMO' will be 'DEMO-Subordinate-CA'" 65 | )] 66 | [string]$Company, 67 | 68 | # Domain URL for CRL and AIA publishing. Also used for the fileshare path. 69 | # Example: 'pki.demo.com' 70 | [Parameter( 71 | Mandatory = $true, 72 | HelpMessage = "Domain URL for CRL and AIA publishing.`nExample: 'pki.demo.com'" 73 | )] 74 | [string]$DomainURL, 75 | 76 | # Local file path to store Certificate Database and Logs. Also used for creating Web directory and fileshare location. 77 | # Example: 'C:\PKI' 78 | [Alias('Path')] 79 | [IO.DirectoryInfo]$LocalPKIPath = "$env:SystemDrive\PKI", 80 | 81 | # A valid SMTP Server used to send information messages about PKI maintenance. 82 | [Parameter( 83 | Mandatory = $True, 84 | ValueFromPipelineByPropertyName = $True, 85 | HelpMessage = 'Enter a valid SMTPServer. Example: smtp.contoso.net' 86 | )] 87 | [ValidateNotNullOrEmpty()] 88 | [string]$SMTPServer, 89 | 90 | # One or more valid TO mailaddresses used to send information messages about PKI maintenance. 91 | # Example: noreply@contoso.com 92 | [Parameter( 93 | Mandatory = $True, 94 | ValueFromPipelineByPropertyName = $True, 95 | HelpMessage = 'One or more valid TO mailaddresses. Example: recipient@contoso.com' 96 | )] 97 | [ValidateNotNullOrEmpty()] 98 | [mailaddress[]]$ToAddress, 99 | 100 | # A valid FROM mailaddress used to send information messages about PKI maintenance. 101 | # Example: noreply@contoso.com 102 | [Parameter( 103 | Mandatory = $True, 104 | ValueFromPipelineByPropertyName = $True, 105 | HelpMessage = 'Enter a valid mailaddress. Example: noreply@contoso.com' 106 | )] 107 | [ValidateNotNullOrEmpty()] 108 | [mailaddress]$FromAddress, 109 | 110 | # A city used to populate the certdat.inc file with correct information. 111 | # Example: Gothenburg 112 | [Parameter( 113 | Mandatory = $True, 114 | ValueFromPipelineByPropertyName = $True, 115 | HelpMessage = 'Gothenburg' 116 | )] 117 | [ValidateNotNullOrEmpty()] 118 | [string]$City, 119 | 120 | # A state used to populate the certdat.inc file with correct information. 121 | # Example: VG 122 | [Parameter( 123 | Mandatory = $True, 124 | ValueFromPipelineByPropertyName = $True, 125 | HelpMessage = 'VG' 126 | )] 127 | [ValidateNotNullOrEmpty()] 128 | [string]$State 129 | 130 | ) 131 | 132 | begin 133 | { 134 | function Add-ScheduledPKIMaintenance 135 | { 136 | <# 137 | .SYNOPSIS 138 | Creates a Scheduled Task that mail a PKI maintenance job every third month. 139 | 140 | .DESCRIPTION 141 | Creates and register a Scheduled Task containing a Powershell script action that will send a mail containing a recommended to do list for a three month PKI maintenance job. 142 | Default the task will be run every 91 days. 143 | 144 | .EXAMPLE 145 | Add-ScheduledPKIMaintenance -CRTFile 'C:\Windows\system32\certsrv\CertEnroll\Contoso-Subordinate-CA.crt' -MaintenanceScriptFile 'C:\PKI\PKI-MaintenanceJob.ps1' -Company Contoso -SMTPServer smtp.contoso.net -ToAddress recipient@contoso.com -FromAddress noreply@contoso.com 146 | 147 | Creates and register a Scheduled Task containing a Powershell script action that will send a mail containing a recommended to do list for a three month PKI maintenance job. 148 | The parameters ToAddress, FromAddress and SMTP server will be outputed in the Action script (Example: C:\PKI\PKI-MaintenanceJob.ps1). 149 | The parameter Company is used to populate the HTML body text and Subject string in POwershell action script. 150 | 151 | .INPUTS 152 | String 153 | IO.FileInfo 154 | mailaddress 155 | 156 | .NOTES 157 | Created on: 2017-01-04 09:22 158 | Created by: Philip Haglund 159 | Organization: Gonjer.com 160 | Filename: Add-ScheduledPKIMaintenance.ps1 161 | Version: 0.2 162 | Requirements: Powershell 3.0 163 | Changelog: 2017-01-04 09:22 - Creation of script. 164 | 2017-01-11 07:39 - Change HTML body. Rewrite help. Fix typos. 165 | 2017-01-20 15:01 - More bug fixes and typo corrections. 166 | 167 | .LINK 168 | https://www.gonjer.com 169 | #> 170 | [cmdletbinding()] 171 | param ( 172 | # Specify a fully qualified file path for the .CRT file. 173 | [Parameter( 174 | Mandatory = $True, 175 | ValueFromPipelineByPropertyName = $True, 176 | HelpMessage = 'C:\Windows\system32\certsrv\CertEnroll\Contoso-Subordinate-CA.crt' 177 | )] 178 | [ValidateNotNullOrEmpty()] 179 | [ValidatePattern('^.*\.crt$')] 180 | [ValidateScript({ 181 | if (Test-Path -Path $_) 182 | { 183 | $True 184 | } 185 | else 186 | { 187 | throw "The path '$_' is not available." 188 | } 189 | })] 190 | [Alias('CRT')] 191 | [IO.FileInfo]$CRTFile, 192 | 193 | # Specify a fully qualified file path for the script file that contains the mail information for PKI Maintenance. 194 | [Parameter( 195 | Mandatory = $True, 196 | ValueFromPipelineByPropertyName = $True, 197 | HelpMessage = 'C:\PKI\PKI-MaintenanceJob.ps1' 198 | )] 199 | [ValidateNotNullOrEmpty()] 200 | [ValidatePattern('^.*\.ps1$')] 201 | [IO.FileInfo]$MaintenanceScriptFile, 202 | 203 | # A Company name used to populate the email template with correct information. 204 | [Parameter( 205 | Mandatory = $True, 206 | ValueFromPipelineByPropertyName = $True, 207 | HelpMessage = 'Contoso' 208 | )] 209 | [ValidateNotNullOrEmpty()] 210 | [string]$Company, 211 | 212 | # A valid SMTP Server used to send information messages about PKI maintenance. 213 | [Parameter( 214 | Mandatory = $True, 215 | ValueFromPipelineByPropertyName = $True, 216 | HelpMessage = 'Enter a valid SMTPServer. Example: smtp.contoso.net' 217 | )] 218 | [ValidateNotNullOrEmpty()] 219 | [string]$SMTPServer, 220 | 221 | # One or more valid TO mailaddresses used to send information messages about PKI maintenance. 222 | # Example: noreply@contoso.com 223 | [Parameter( 224 | Mandatory = $True, 225 | ValueFromPipelineByPropertyName = $True, 226 | HelpMessage = 'One or more valid TO mailaddresses. Example: recipient@contoso.com' 227 | )] 228 | [ValidateNotNullOrEmpty()] 229 | [mailaddress[]]$ToAddress, 230 | 231 | # A valid FROM mailaddress used to send information messages about PKI maintenance. 232 | # Example: noreply@contoso.com 233 | [Parameter( 234 | Mandatory = $True, 235 | ValueFromPipelineByPropertyName = $True, 236 | HelpMessage = 'Enter a valid mailaddress. Example: noreply@contoso.com' 237 | )] 238 | [ValidateNotNullOrEmpty()] 239 | [mailaddress]$FromAddress 240 | 241 | ) 242 | begin 243 | { 244 | $body = @" 245 | "

It's time for PKI maintenance!

246 | 247 |

It was three months ago since the last PKI maintenance for $($Company).

248 | 249 |

Follow the to-do list below to keep the PKI structure/environment healthy and up to date.
250 | The to-do list is just a recomendation, not a forced task list.

251 |  

252 | 253 | 289 | 290 |

Regards
291 | Your PKI Administrator

" 292 | "@ 293 | 294 | 295 | $maintenancescript = @" 296 | Send-MailMessage `` 297 | -To '$($ToAddress)' `` 298 | -BodyAsHtml `` 299 | -Encoding ([System.Text.Encoding]::UTF8) `` 300 | -From '$($FromAddress)' `` 301 | -SmtpServer '$($SMTPServer)' `` 302 | -Subject 'PKI maintenance - $($Company)' `` 303 | -Body $($body) 304 | "@ 305 | try 306 | { 307 | if (Test-Path -Path $MaintenanceScriptFile.Directory.FullName) 308 | { 309 | $null = New-Item -Path $MaintenanceScriptFile.Directory.FullName -ItemType Directory -Force -ErrorAction Stop 310 | } 311 | $maintenancescript | Out-File -FilePath $MaintenanceScriptFile -ErrorAction Stop -Force -Encoding ascii 312 | } 313 | catch 314 | { 315 | Write-Warning -Message "Unable to create file $($MaintenanceScriptFile) - $($_.Exception.Message)" 316 | Write-Output -InputObject 'Will not create a scheduled task. Contact your PKI Administrator!' 317 | break 318 | } 319 | 320 | try 321 | { 322 | [string]$regex = '^(?:[a-z]+\:\s)(?\d{4}\-\d{2}-\d{2})(?:\s)(?