Monitor your Azure Secrets, Zerts and SPN’s Expire Date with PowerShell

Azure App Registrations are a secure way to provide Credentials and Access to the Azure World. The Amount of Azure App Registrations in a Tenant is increasing very fast, so it is not that easy to control and monitor all those Secrets, Zert, and SPN’s Expire Date.

So I have written a PowerShell Script to send an Email, when an Azure App Secret or Zert, or an Azure SPN is expiring in the near future. This Script can be executed in your OnPrem Datacenter or as an Azure Automation Runbook.

Prerequisites

First, we need to create an Azure App to provide all permissions required. Yes, that one will also be monitored by the Script 🙂

MS Graph References:

Create an Azure App Registration and add the following GRAPH API Application Permissions

  • Application.Read.All
  • Application.ReadWrite.All
  • Directory.Read.All
  • User.Read

Create a Secret and copy the Value

If your are not familiar with Azur eapp Regs, and how als this work together, see my Blogs Post for Details:

To learn more from Microsoft GRAPH API, see my Blog Series:
Part 1 – Authentication and Azure App – Use Microsoft Graph API with PowerShell – Part 1 » TechGuy
Part 2 – Oauth2.0 – Use Microsoft Graph API with PowerShell – Part 2 » TechGuy
Part 3 – First Powershell Script to get a Teams Lis and Walkthrough – Use Microsoft Graph API with PowerShell – Part 3 » TechGuy
Part 4 – this one – Use Microsoft Graph API with PowerShell – Part 4 » TechGuy

Second, we are sending an Email using Graph API, so see my previous Blog Post for Details and Enter the GRAPH Details in the Script: Send Mail with PowerShell and Microsoft Graph API – TechGuy

With all those information, we can take a look at the Script

The Script

Please fill Settings and the Azure App Details til the Line “#STOP HERE”

The Script will send you an Email for each Azure App or SP which will expire within the next 90 Days ($TimeSpanInDays). Already expired ones will not trigger an Email

#Settings
$TimeSpanInDays=90
$MailSender="Mail Sender Mail"
$MailRecipient="Mail Recipient Mail"

#Azure App Credentials to get Apps and SP
$EXPIRE_AppId = "your EXPIRE APP Client ID"
$EXPIRE_secret = "your EXPIRE APP Secret"

$tenantID = "Azure Tenant ID"

#Azure App Credentials to send the Mail
$MAIL_AppId = "your Mail Client ID"
$MAIL_secret = "your Mail Secret"

#STOP HERE!

#Connect to GRAPH API with EXPIRE credentials
$EXPIRE_tokenBody = @{
    Grant_Type    = "client_credentials"
    Scope         = "https://graph.microsoft.com/.default"
    Client_Id     = $EXPIRE_AppId
    Client_Secret = $EXPIRE_secret
}
$EXPIRE_tokenResponse = Invoke-RestMethod -Uri "https://login.microsoftonline.com/$tenantID/oauth2/v2.0/token" -Method POST -Body $EXPIRE_tokenBody
$EXPIRE_headers = @{
    "Authorization" = "Bearer $($EXPIRE_tokenResponse.access_token)"
    "Content-type"  = "application/json"
}



#Connect to GRAPH API with MAIL Credentials
$MAIL_tokenBody = @{
    Grant_Type    = "client_credentials"
    Scope         = "https://graph.microsoft.com/.default"
    Client_Id     = $MAIL_AppId
    Client_Secret = $MAIL_secret
}
$MAIL_tokenResponse = Invoke-RestMethod -Uri "https://login.microsoftonline.com/$tenantID/oauth2/v2.0/token" -Method POST -Body $MAIL_tokenBody
$MAIL_headers = @{
    "Authorization" = "Bearer $($MAIL_tokenResponse.access_token)"
    "Content-type"  = "application/json"
}



#Build Array to store PSCustomObject
$Array = @()



# List Get all Apps from Azure
$URLGetApps = "https://graph.microsoft.com/v1.0/applications"
$AllApps = Invoke-RestMethod -Method GET -Uri $URLGetApps -Headers $EXPIRE_headers



#Go through each App and add to our Array
foreach ($App in $AllApps.value) {

    $URLGetApp = "https://graph.microsoft.com/v1.0/applications/$($App.ID)"
    $App = Invoke-RestMethod -Method GET -Uri $URLGetApp -Headers $EXPIRE_headers

    if ($App.passwordCredentials) {
        foreach ($item in $App.passwordCredentials) {
            $Array += [PSCustomObject]@{
                "Type"           = "AZAPP"
                "displayName"    = $app.displayName
                "ID"             = $App.ID
                "AppID"          = $app.appId
                "SecType"        = "Secret"
                "Secret"         = $item.displayName
                "Secret-EndDate" = (Get-date $item.endDateTime)
            }
        }
    }
    

    if ($App.keyCredentials) {
        foreach ($item in $App.keyCredentials) {
            $Array += [PSCustomObject]@{
                'Type'           = "AZAPP"
                'displayName'    = $app.displayName
                'ID'             = $App.ID
                'AppID'          = $app.appId
                'SecType'        = "Zert"
                'Secret'         = $item.displayName
                'Secret-EndDate' = (Get-date $item.endDateTime)
            }
        }
    }
}




#Get all Service Principals
$servicePrincipals = "https://graph.microsoft.com/v1.0/servicePrincipals"
$SP = Invoke-RestMethod -Method GET -Uri $servicePrincipals -Headers $EXPIRE_headers

$SPList = $SP.value 
$UserNextLink = $SP."@odata.nextLink"

while ($UserNextLink -ne $null) {

    $SP = (Invoke-RestMethod -Uri $UserNextLink -Headers $EXPIRE_headers -Method Get )
    $UserNextLink = $SP."@odata.nextLink"
    $SPList += $SP.value
}

#Go through each SP and add to our Array
foreach ($SAML in $SPList) {
    if ($Saml.passwordCredentials) {
        foreach ($PW in $Saml.passwordCredentials) {
            $Array += [PSCustomObject]@{
                'Type'           = "SP"
                'displayName'    = $SAML.displayName
                'ID'             = $SAML.id
                'AppID'          = $Saml.appId
                'SecType'        = "Secret"
                'Secret'         = $PW.displayName
                'Secret-EndDate' = (Get-date $PW.endDateTime)
            }
        }
    }
}



$ExpireringZerts = $Array | Where-Object -Property Secret-EndDate -Value (Get-Date).AddDays($TimeSpanInDays) -lt  | Where-Object -Property Secret-EndDate -Value (Get-Date) -gt

foreach ($Zert in $ExpireringZerts) {
    $HTML = $Zert | Convertto-HTML -Fragment -As List

    $URLsend = "https://graph.microsoft.com/v1.0/users/$MailSender/sendMail"
    
    $BodyJsonsend = @"
                        {
                            "message": {
                              "subject": "Azure App or SPN will expire soon $($Zert.displayName)",
                              "body": {
                                "contentType": "HTML",
                                "content": "$HTML
                                <br>
                                Michael Seidl (au2mator)
                                <br>

                                "
                              },
                              "toRecipients": [
                                {
                                  "emailAddress": {
                                    "address": "$MailRecipient"
                                  }
                                }
                              ]
                            },
                            "saveToSentItems": "false"
                          }
"@
    
    Invoke-RestMethod -Method POST -Uri $URLsend -Headers $MAIL_headers -Body $BodyJsonsend
}

GitHub Repo

Here you can find the GitHub Repo: Seidlm/Microsoft-Azure: Azure Rest API Examples (github.com) with the Script

Michael Seidl aka Techguy
au2mate everything

Leave a Comment

Your email address will not be published. Required fields are marked *

*