Forum Discussion

rtushar1400's avatar
rtushar1400
Copper Contributor
Mar 16, 2022

ADCS expiring certificates email alerting script

Hi 

 

Can someone please help me with a script that sends out email alerts when a security certificate is about to expire in ADCS ? I found a couple on googling but none seems to work properly. There are some errors in that which I am unable to resolve. Any leads would be greatly appreciated.

 

Thanks!

  • rtushar1400 Ok... Changed the script a little bit:

    - Removed the write-host lines with the certificates found that would expire, date-time issue in formatting

    - Changed the amount of days to 365 for testing

    - Changed "$Certexpirydate = [datetime](Get-date $importall[$i].'Certificate Expiration Date' -Format $formatdata)" to "$Certexpirydate = [datetime](Get-date $importall[$i].'Certificate Expiration Date')" . Tried different formats for $formatdata but errors.

    - $Mailbody gave me this HTML output, don't have a mailserver on my test VM but it gave me the output that I wanted so I guess you just need to test mailing now

     

    The adjusted script, just change the mail and template variables and a correct amount of days (365 now) and test 🙂 

     

    #functions
    
     
    
    Function Send-CertificateList
    
    {
    
        #initialize email pre-reqs
    
        $FromAddress = 'Email address removed'
    
        $ToAddress  = 'Email address removed'
    
        $MessageSubject = "Certificate expiration reminder from $env:COMPUTERNAME.$env:USERDNSDOMAIN"
    
        $SendingServer = 'smtp.office365.com'
    
        $SmtpServerPort = "Port Number"
    
     
    
        #Test if SMTP server is responding
    
        if(Test-Connection -Cn $SendingServer -BufferSize 16 -Count 1 -ea 0 -quiet){
    
                #Send email
    
                Send-MailMessage -From $FromAddress -To $ToAddress -Subject $MessageSubject -Body $mailbody -BodyAsHtml -SmtpServer $SendingServer -Port $SmtpServerPort
    
        }else{
    
                #Error Handling
    
                write-host -object 'No connection to SMTP server. Failed to send email!'
    
        }
    
    }
    
     
    
    Function Send-Certificatemail
    
    {
    
        #initialize email pre-reqs
    
        $FromAddress = 'Email address removed'
    
        $CCAddress  = 'Email address removed'
    
        $MessageSubject = "Certificate expiration reminder from $env:COMPUTERNAME.$env:USERDNSDOMAIN"
    
        $SendingServer = 'smtp.office365.com'
    
        $SmtpServerPort = "Port Number"
    
     
    
        #Test if SMTP server is responding
    
        if(Test-Connection -Cn $SendingServer -BufferSize 16 -Count 1 -ea 0 -quiet){
    
                #Send email
    
                Send-MailMessage -From $FromAddress -To $ToAddress -Cc $CCAddress -Subject $MessageSubject -Body $Emailbody -BodyAsHtml -SmtpServer $SendingServer -Port $SmtpServerPort
    
        }else{
    
                #Error Handling
    
                write-host -object 'No connection to SMTP server. Failed to send email!'
    
        }
    
    }
    
     
    
    # --------------------------------------------------
    
     
    
    #HTML Style
    
    $style = @'
    
    <style>body{font-family:`"Calibri`",`"sans-serif`"; font-size: 14px;}
    
    @font-face
    
           {font-family:`"Cambria Math`";
    
           panose-1:2 4 5 3 5 4 6 3 2 4;}
    
    @font-face
    
           {font-family:Calibri;
    
           panose-1:2 15 5 2 2 2 4 3 2 4;}
    
    @font-face
    
           {font-family:Tahoma;
    
           panose-1:2 11 6 4 3 5 4 4 2 4;}
    
           table{border: 1px solid black; border-collapse:collapse; mso-table-lspace:0pt; mso-table-rspace:0pt;}
    
           th{border: 1px solid black; background: #dddddd; padding: 5px; }
    
           td{border: 1px solid black; padding: 5px; }
    
           .crtsn{font-weight: bold; color: blue; }
    
           .crtexp{font-weight: bold; color: red; }
    
           .crtcn{font-weight: bold; color: orange; }
    
           </style>
    
    '@
    
     
    
    # --------------------------------------------------
    
    #variables
    
    #filter template list
    
    $filterlist ="Copy of User","DomainController"
    
    #setup duration
    
    $duration = 365
    
    #setup strDate with yyyyMMdd-HHmmss
    
    $strDate = get-date -format yyyyMMdd-HHmmss
    
    #create unique export file name
    
    $exportFileName = "certificates_" + $strDate + ".csv"
    
    #date of Now
    
    $now = (Get-Date)
    
    #date of Then
    
    $Then = (Get-Date).AddDays($duration)
    
    #empty mailbody
    
    $mailbody = ""
    
    #empty array initialization
    
    $table = @()
    
     
    
    # --------------------------------------------------
    
    #variables
    
     
    
    #export certificates to CSV
    
    certutil.exe -view csv > $exportFileName
    
     
    
    #Import certificate info where Serial Number is not "empty" with various properties
    
    $importall = Import-Csv $exportFileName | Where-Object {$_.'Serial Number' -notcontains 'EMPTY'} | Select-Object -Property 'Request ID','Serial Number','Requester Name','Certificate Expiration Date','Certificate Template','Request Common Name','Request Disposition' -ErrorAction SilentlyContinue
    
     
    
    #Run through each ObjectID  to get the Certificate Template Name
    
    foreach ($OID in (get-catemplate).Oid)
    
    {
    
        #populate the field "Certificate Template"
    
        $importall | where-object "certificate template" -match $OID | foreach-object {
    
            #ensure whitespaces removed
    
            $_.'Certificate Template' = ($_.'Certificate Template').replace($OID+" ","")
    
        }
    
     
    
    }
    
    #filter only required certificates based on $filterlist
    
    $importall = $importall | where-object "certificate template" -in $filterlist
    
     
    
    #build email body
    
    $mailbody += '<html><head><meta http-equiv=Content-Type content="text/html; charset=utf-8">' + $style + '</head><body>'
    
    $mailbody += "The certificate expiry details:<br />"
    
     
    
    #collect cultureinfo for short date and time pattern
    
    $cultureinfo = Get-Culture
    
    $formatdata = "$($cultureinfo.DateTimeFormat.ShortDatePattern) $($cultureinfo.DateTimeFormat.ShortTimePattern)"
    
     
    
    #mail body template
    
    $mailbody += '<p>'
    
    $mailbody += 'Hello Reader, '+"<br />"
    
    $mailbody += 'Please find below the list of certificaes Expiring in next ' + $duration + ' days' + "</span><br />"
    
    $mailbody += '</p>'
    
     
    
    #cycle through array and search for matching cetificates
    
    for($i=0;$i -lt $importall.Count;$i++)
    
    {
    
         #for each object, get the "certificate expirate date" and convert to [datetime]
    
         $Certexpirydate = [datetime](Get-date $importall[$i].'Certificate Expiration Date')
    
         #perform comparison
    
         If(($Certexpirydate -gt $now) -and ($Certexpirydate -le $then))
    
     
    
            {
    
                #save info in table array
    
                $table += $importall[$i] | Sort-Object 'Certificate Expiration Date' | Select-Object -Property 'Request ID','Serial Number','Requester Name','Certificate Template','Certificate Expiration Date','Request Common Name','Issued Email Address'
    
            }
    
    }
    
     
    
    #mailbody html formatting
    
    $mailbody += '<p><table>'
    
    $mailbody += '<th>Request ID</th><th>Serial Number</th><th>Requester Name</th><th>Requested CN</th><th>Certificate Template</th><th>Expiration date</th>'
    
     
    
    #run through each row
    
    foreach($row in $table)
    
        {
    
            #create necessary row information
    
            $mailbody += "<tr><td>" + $row.'Request ID' + "</td><td>" + $row.'Serial Number' + "</td><td>" + $row.'Requester Name' + "</td><td>" + $row.'Request Common Name' + "</td><td>" + $row.'Certificate Template' + "</td><td>" + $row.'Certificate Expiration Date' + "</td></tr>"
    
        }
    
    #closing html tags
    
    $mailbody += '</table></p>'
    
    $mailbody += '</body>'
    
    $mailbody += '</html>'
    
     
    
    #if there are matching certificates found send email
    
    if($table.Count -gt '0')
    
        {
    
            #send email
    
            Send-CertificateList
    
        }
    
     
    
    #run through each row
    
    foreach($row in $table)
    
    {
    
        #if email address exist
    
        if($($row.'Issued Email Address') -like "*@*")
    
        {
    
            #populate to address and email body
    
            $ToAddress  = $row.'Issued Email Address'
    
            $emailbody =  "Hello Reader,<br>
    
                                <br>
    
                                The certificate requested by you is about to expire :
    
     
    
                                <br>
    
                                Details <br>
    
                                -------------------- <br>
    
                                $row
    
                                <Br>
    
                                <br>
    
                                Thanks"
    
            #send mail
    
            Send-Certificatemail
    
        }
    
    }

     

     

  • Do you have an example script which gives you errors as a start? Perhaps we can fix those issues and you can start using it?
    • rtushar1400's avatar
      rtushar1400
      Copper Contributor
      Hi Harm_Veenstra, Thanks for a fast response. So, I found a nice script over here - https://techcommunity.microsoft.com/t5/core-infrastructure-and-security/script-to-send-email-alerts-on-expiring-certificates-for/ba-p/1169438

      On executing it, I am getting a simple error only and on 2 lines for some "Positional Parameter". Pasting that error below:

      PS C:\Scripts> C:\Scripts\Certificate Expiry Notification Script.ps1
      Write-Host : A positional parameter cannot be found that accepts argument '8'.
      At C:\Scripts\Certificate Expiry Notification Script.ps1:129 char:13
      + write-host -object 'Certificate ID:' $importall[$i].'Requ ...
      + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
      + CategoryInfo : InvalidArgument: (:) [Write-Host], ParameterBindingException
      + FullyQualifiedErrorId : PositionalParameterNotFound,Microsoft.PowerShell.Commands.WriteHostCommand

      Write-Host : A positional parameter cannot be found that accepts argument ' Days!'.
      At C:\Scripts\Certificate Expiry Notification Script.ps1:129 char:161
      + ... -NoNewline; write-host -object ([datetime]($importall[$i].'Certificat ...
      + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
      + CategoryInfo : InvalidArgument: (:) [Write-Host], ParameterBindingException
      + FullyQualifiedErrorId : PositionalParameterNotFound,Microsoft.PowerShell.Commands.WriteHostCommand

      This certificate has DN:
      Please don`t forget to renew this certificate before expiration date: Write-Host : A positional parameter cannot be found that accepts argument '
      '.
      At C:\Scripts\Certificate Expiry Notification Script.ps1:131 char:117
      + ... -NoNewline; write-host -object $importall[$i].'Certificate Expiration ...
      + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
      + CategoryInfo : InvalidArgument: (:) [Write-Host], ParameterBindingException
      + FullyQualifiedErrorId : PositionalParameterNotFound,Microsoft.PowerShell.Commands.WriteHostCommand

      Write-Host : A positional parameter cannot be found that accepts argument '9'.
      At C:\Scripts\Certificate Expiry Notification Script.ps1:129 char:13
      + write-host -object 'Certificate ID:' $importall[$i].'Requ ...
      + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
      + CategoryInfo : InvalidArgument: (:) [Write-Host], ParameterBindingException
      + FullyQualifiedErrorId : PositionalParameterNotFound,Microsoft.PowerShell.Commands.WriteHostCommand

      Write-Host : A positional parameter cannot be found that accepts argument ' Days!'.
      At C:\Scripts\Certificate Expiry Notification Script.ps1:129 char:161
      + ... -NoNewline; write-host -object ([datetime]($importall[$i].'Certificat ...
      + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
      + CategoryInfo : InvalidArgument: (:) [Write-Host], ParameterBindingException
      + FullyQualifiedErrorId : PositionalParameterNotFound,Microsoft.PowerShell.Commands.WriteHostCommand

      This certificate has DN:
      Please don`t forget to renew this certificate before expiration date: Write-Host : A positional parameter cannot be found that accepts argument '
      '.
      At C:\Scripts\Certificate Expiry Notification Script.ps1:131 char:117
      + ... -NoNewline; write-host -object $importall[$i].'Certificate Expiration ...
      + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
      + CategoryInfo : InvalidArgument: (:) [Write-Host], ParameterBindingException
      + FullyQualifiedErrorId : PositionalParameterNotFound,Microsoft.PowerShell.Commands.WriteHostCommand

      Write-Host : A positional parameter cannot be found that accepts argument '10'.
      At C:\Scripts\Certificate Expiry Notification Script.ps1:129 char:13
      + write-host -object 'Certificate ID:' $importall[$i].'Requ ...
      + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
      + CategoryInfo : InvalidArgument: (:) [Write-Host], ParameterBindingException
      + FullyQualifiedErrorId : PositionalParameterNotFound,Microsoft.PowerShell.Commands.WriteHostCommand

      Write-Host : A positional parameter cannot be found that accepts argument ' Days!'.
      At C:\Scripts\Certificate Expiry Notification Script.ps1:129 char:161
      + ... -NoNewline; write-host -object ([datetime]($importall[$i].'Certificat ...
      + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
      + CategoryInfo : InvalidArgument: (:) [Write-Host], ParameterBindingException
      + FullyQualifiedErrorId : PositionalParameterNotFound,Microsoft.PowerShell.Commands.WriteHostCommand

      This certificate has DN:
      Please don`t forget to renew this certificate before expiration date: Write-Host : A positional parameter cannot be found that accepts argument '
      '.
      At C:\Scripts\Certificate Expiry Notification Script.ps1:131 char:117
      + ... -NoNewline; write-host -object $importall[$i].'Certificate Expiration ...
      + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
      + CategoryInfo : InvalidArgument: (:) [Write-Host], ParameterBindingException
      + FullyQualifiedErrorId : PositionalParameterNotFound,Microsoft.PowerShell.Commands.WriteHostCommand

      Many thanks in advance!
      • Harm_Veenstra's avatar
        Harm_Veenstra
        MVP
        Ok, you also reported your issue on the download page of the script so that's better I guess... You did fill in all the variables/settings and have the prerequisites in-place?
    • Harm_Veenstra's avatar
      Harm_Veenstra
      MVP

      farismalaeb What rtushar1400 wants is to scan his own CA for certificates that were provisioned from that to check expiration on them. You can use your method of course and enter all the fqdn's to be checked, but the script that rtushar1400 wants to use is scanning the whole CA for all certificates and do reporting on that. It's more dynamic like that, otherwise he has to be informed by every sysadmin if they enroll a certifcate on the CA.

      • rtushar1400's avatar
        rtushar1400
        Copper Contributor
        farismalaeb Hi, thanks for taking your time to share a nice script but as Harm_Veenstra said I wanna scan my whole CA to check expiration of the certificates and there are many certificates on it. We are here currently looking at a script that is not working or giving any results. Please feel free if you have any other suggestions, would really appreciate that.

        Harm_Veenstra, coming to your question, I don't think there's any value getting stored in $table. There indeed gets a CSV file created on running the script containing the info of all the existing certificates on CA but it does not seems to be loading $table with the relevant certificates from the certificate templates I've been using which are getting expired in less than 30 days as per the condition. Is there a way for you to test the script on your end in your environment ?
  • Techguy7979's avatar
    Techguy7979
    Copper Contributor
    Can someone please help with adding smtp credentials to the script? I've looked all over the Internet and none of the scripts have SMTP credentials lines.
    Would be super great if it supported password hashing. Storing a clear text password isn't ideal. Thanks to the community in advance!

Resources