Update : updated script to support Azure Az powershell module.
Once we register an Application with Azure AD, we configure Secrets or Certificates or sometimes both. This is required to set up application authentication and authorization, Sign-On or OAuth authorization service etc.
The secret or the certificate we set on the application has an expiry. Once it is expired, it can not be used. It can cause unwanted downtime, if we miss to renew certificate or secret with new value after it has expired. It is very critical that we get notified before it expires and we renew it in time.
I provided a script to generate a list of all Azure AD Application along with expiration in my previous blog. Today, I sharing a script which will notify you on expired or expiring Certificate.
Before we start, we need an Azure AD user account with rights to read AD Application details. Script will retrieve the credentials from Key Vault. Along with credentials, you need an SMTP server or a Relay to send emails through it. Also, you need AzureAD, Az powershell module installed and imported in to your powershell console.
Once you have those prerequisites set up, login to Azure using Login-AzAccount. Provides your credentials when prompted.
Alternatively, if you are configuring this script as an automation run book, use below code block to login to Azure using the Service Principal configured for the Automation RunAs account.
#region - login to Azure from the Automation Account
$connectionName = "AzureRunAsConnection"
try{
$servicePrincipalConnection = Get-AutomationConnection -Name $connectionName
"Logging in to Azure..."
Add-AzAccount `
-ServicePrincipal `
-TenantId $servicePrincipalConnection.TenantId `
-ApplicationId $servicePrincipalConnection.ApplicationId `
-CertificateThumbprint $servicePrincipalConnection.CertificateThumbprint
}
catch{
if(!$servicePrincipalConnection){
$errorMessage = "Connection $connectionName not found"
throw $errorMessage
}else{
Write-Error -Message $_.Exception
throw $_.Exception
}
}
#endregion
Now, let’s define a variable for the number of days for the certificate expiry check. Here I am using 7 days. You may change this number to whatever value you want to check before considering the certificate renewal.
$daysToExpire = 7
Next, define a variable for the Key Vault name where you have stored your Azure AD user id and password to use to login to Azure AD.
#region - Retrieve Azure AD Service Account Credentials from Key Vault
$kvName = <Your Key Vault Name>
$azADSvcAcc = (Get-AzureKeyVaultSecret -VaultName $kvName -Name AzureADSvcAccount).SecretValueText
$azADSvcAccPwd = (Get-AzureKeyVaultSecret -VaultName $kvName -Name AzureADSvcAccountPassword).SecretValueText
$password = ConvertTo-SecureString $azADSvcAccPwd -AsPlainText -Force
$Cred = New-Object System.Management.Automation.PSCredential ($azADSvcAcc, $password)
#endregion
Next, check if AzureAD powershell module is already imported. If not, import it as shows below:
#region - import AzureAD Module if it is not already imported and then connect to Azure AD
if((Get-Module -Name AzureAD).count -gt 0){
Write-Output "Azure AD Module is already imported"
}else{
Write-Output "Azure AD Module is missing. Importing Module."
Import-Module AzureAD -Force
}
Connect-AzureAD -Credential $Cred
#end - region
Now, define an array variable to store application details and mail details – including sender email, recipient email, SMTP server and port Number.
$allApplications = @()
$mailFrom = <Email id of the sender - it could be a fake email>
$mailTo = <Email id of recipient>
$subject = "AzureAD App Certificates Expired/Expiring in $daysToExpire days"
$smtpserver = <SMTP Server FQDN / IP Address>
Once we have all required variables set up, we will start parsing through each Azure AD application.
#region - Check Azure AD Applications and adds to an array defined earlier
Get-AzureADApplication -All $true | ForEach-Object{
$applicationName = $objectType = $objectId = $applicationId = $keyId = $certExpirationDate = ""
$certExpired = $certExpiring = "False"
$owner = ""
$applicationName = $_.DisplayName
$objectType = $_.ObjectType
$objectId = $_.ObjectId
$applicationId = $_.AppId
$owner = (Get-AzureADApplicationOwner -ObjectId $objectId).DisplayName
$certCreds = (Get-AzureADApplication -ObjectId $objectId).KeyCredentials
if(!([String]::IsNullOrEmpty($certCreds))){
$certCreds | ForEach-Object{
$keyId = $_.KeyId
$certExpirationDate = $_.EndDate
if($certExpirationDate -lt (Get-Date)){
$certExpired = "True"
$certExpiring = "N/A"
}
elseif($certExpirationDate -le (Get-Date).AddDays($daysToExpire)){
$certExpiring = "True"
}
if($certExpired -ne "False" -and $certExpiring -ne "False"){
$objCert = New-Object psobject
$objCert | Add-Member ApplicationName $applicationName
$objCert | Add-Member ApplicationId $applicationId
$objCert | Add-Member ObjectType $objectType
$objCert | Add-Member Owner $owner
$objCert | Add-Member KeyId $keyId
$objCert | Add-Member CertExpirationDate $certExpirationDate
if($certExpired){$objCert | Add-Member CertExpired $certExpired}
if($certExpiring){$objCert | Add-Member CertExpiring $certExpiring}
$allApplications += $objCert
}
}
}
}
#endregion
At this point, we have required data to process. Let’s start working on the presentation of data in to a nice looking table and attach it to the body of the email.
#region - create Table and its schema
$table = New-Object system.Data.DataTable "certReportTable"
$col1 = New-Object system.Data.DataColumn "Application Name",([string])
$col2 = New-Object system.Data.DataColumn "Application Id", ([string])
$col3 = New-Object system.Data.DataColumn "Object Type", ([string])
$col4 = New-Object system.Data.DataColumn "Owner", ([string])
$col5 = New-Object system.Data.DataColumn "Key Id", ([string])
$col6 = New-Object system.Data.DataColumn "Expiration Date", ([string])
$col7 = New-Object system.Data.DataColumn "Cert Expired?", ([string])
$col8 = New-Object system.Data.DataColumn "Expiring in $($daysToExpire) days?", ([string])
#Add colums to table
$table.Columns.Add($col1)
$table.Columns.Add($col2)
$table.Columns.Add($col3)
$table.Columns.Add($col4)
$table.Columns.Add($col5)
$table.Columns.Add($col6)
$table.Columns.Add($col7)
$table.Columns.Add($col8)
#populate table with data
foreach ($app in $allApplications){
$row = $table.NewRow()
$row."Application Name" = $app.ApplicationName
$row."Application Id" = $app.ApplicationId
$row."Object Type" = $app.ObjectType
$row."Owner" = $app.Owner
$row."Key Id" = $app.KeyId
$row."Expiration Date" = $app.CertExpirationDate
$row."Cert Expired?" = $app.CertExpired
$row."Expiring in $($daysToExpire) days?" = $app.CertExpiring
$table.Rows.Add($row)
}
#endregion
Finally, we will work on setting up the HTML email body formatting and send the email as shown below:
#region - set HTML CSS values, HTML body and send email
$header = @"
<style>
body {
font-family: "Arial";
font-size: 8pt;
color: #4C607B;
}
th, td {
border: 1px solid #e57300;
border-collapse: collapse;
padding: 5px;
}
th {
font-size: 1.2em;
text-align: left;
background-color: #003366;
color: #ffffff;
}
td {
color: #000000;
}
.even { background-color: #ffffff; }
.odd { background-color: #bfbfbf; }
</style>
"@
[string]$body = [PSCustomObject]$table | Select-Object -Property "Application name","Application Id","Object Type","Owner","Key Id", "Expiration Date", "Cert Expired?", "Expiring in $($daysToExpire) days?" | Sort-Object -Property "Application Name" | ConvertTo-HTML -head $header -Body "<font color=`"Black`"><h4>AzureAD Application Certificate Expired/Expiring in $daysToExpire days</h4></font>"
#Send email to stake holders
$mailMessageParameters = @{
From = $mailFrom
To = $mailTo
Subject = $subject
SmtpServer = $smtpserver
Port = 25
Body = $body
}
Send-MailMessage @mailMessageParameters -BodyAsHtml
#endregion
Great! we are all set now. Run the script and expect an email with all required details in a nicely formatted table. You can schedule it to run in a certain interval by either configuring it through Scheduled Task or a Schedule in Automation account.
Provide your comments in the comment section of this page. You can contact me if you face any issue with this script.