Runbook to check Entra Apps with expiring ClientSecrets and Certificates

Runbook to check Entra Apps with expiring ClientSecrets and Certificates

Hi All,

In the previous Article i explained how to check for Entra Apps with expiring ClientSecrets and Certificates.

In this Blog Article i explain how we can extend this into a Azure Automation Runbook.

Automation Account

The Azure Automation Account needs to have a Managed Identity

Use the AZ PowerShell to get Infos about the Azure Automation Account

###############################################################################
# Get Managed Identity of Azure Automation Account
###############################################################################
Connect-AzAccount -Tenant icewolfch.onmicrosoft.com
Get-AzAutomationAccount -Name icewolfautomation -ResourceGroupName RG_DEV

#Get ManagedIdentity
$AutomationAccount = Get-AzAutomationAccount -Name icewolfautomation -ResourceGroupName RG_DEV
$AutomationAccount.Identity

Service Principal

We can now use the ObjectID of the ManagedIdentity to get all the Details with Microsoft.Graph

###############################################################################
# Get AzureAD Application with Microsoft.Graph PowerShell
###############################################################################
Connect-MgGraph -Scopes Application.Read.All -NoWelcome
$ServicePrincipalDetails = Get-MgServicePrincipal -ServicePrincipalId "547c53c8-9e84-4dc7-8a65-92c59fb1c34f"
$AppID = $ServicePrincipalDetails.AppId
$ObjectId = $ServicePrincipalDetails.Id
$AppDisplayName = $ServicePrincipalDetails.Displayname
$ServicePrincipalDetails | Format-List Id, AppId, DisplayName, ServicePrincipalType

Exchange Online RBAC for Application

I’ve blogged about this earlyer

Here is the short and updated Version

###############################################################################
# Create Exchange Service Principal
###############################################################################
Connect-ExchangeOnline -ShowBanner:$false
New-ServicePrincipal -AppId $AppID -ObjectId $ObjectId -DisplayName "EXO Serviceprincipal $AppDisplayName"
Get-ServicePrincipal | where-object {$_.AppId -eq $AppID}

###############################################################################
#New-ManagementScope
###############################################################################
# New-ManagementScope 
# https://learn.microsoft.com/en-us/powershell/module/exchange/new-managementscope?view=exchange-ps
# Filterable properties for the RecipientFilter parameter on Exchange cmdlets
# https://learn.microsoft.com/en-us/powershell/exchange/recipientfilter-properties?view=exchange-ps
Get-ManagementScope
New-ManagementScope -Name "a.bohren" -RecipientRestrictionFilter "PrimarySmtpAddress -eq 'a.bohren@icewolf.ch'"

###############################################################################
#New-ManagementRoleAssignment
###############################################################################
New-ManagementRoleAssignment -App $ObjectId -Role "Application Mail.Send" -CustomResourceScope "a.bohren"
Get-ManagementRoleAssignment | where-object {$_.App -eq $ObjectId}

Show the Management Role details

###############################################################################
#Get-ManagementRoleAssignment
###############################################################################
Get-ManagementRoleAssignment | where-object {$_.App -eq $ObjectId}
Get-ManagementRoleAssignment | where-object {$_.App -eq $ObjectId} | fl

Azure Runbook

Here is the Runbook that can be used in Azure Automation

###############################################################################
# Azure Automation Runbook
# CheckEntraAppAuthenticationExpiration
###############################################################################
# Azure Automation Account
# - Managed Identinty
# Required Modules:
# - Microsoft.Graph.Authentication
# - Microsoft.Graph.Applications
# Required Permissions
# - Application.Read.All
# - Mail.Send (Mail.Send.Shared does not exist in Exchange Online RBAC for Applications)
###############################################################################
# Important
###############################################################################
# Limiting application permissions to specific Exchange Online mailboxes
# https://docs.microsoft.com/en-us/graph/auth-limit-mailbox-access
#
# Limit Microsoft Graph Access to specific Exchange Mailboxes
# https://blog.icewolf.ch/archive/2021/02/06/limit-microsoft-graph-access-to-specific-exchange-mailboxes.aspx
#
# or Limit Graph Access with Exchange Online Role Based Access Control (RBAC) for Applications
# https://blog.icewolf.ch/archive/2023/01/05/exchange-online-role-based-access-control-rbac-for-applications/
###############################################################################


###############################################################################
#Send Mail Function Graph
###############################################################################
Function Send-GraphEmailNotification{

    Param(
    [parameter(Mandatory=$true)][String]$From,
    [parameter(Mandatory=$true)][String]$To,
    [parameter(Mandatory=$true)][String]$Subject,
    [parameter(Mandatory=$true)][String]$MessageBody
    )
$URI = "https://graph.microsoft.com/v1.0/users/$From/sendMail"
$ContentType = "application/json"
$Headers = @{"Authorization" = "Bearer "+ $AccessToken}
$Body = @"
{
    "message": {
        "subject": "$Subject",
        "body": {
            "contentType": "Text",
            "content": "$MessageBody"
        },
        "toRecipients": [
            {
                "emailAddress": {
                    "address": "$To"
                }
            }
        ]
    }
}
"@
Invoke-GraphRequest -Method "POST" -Uri $uri -Body $Body -ContentType $ContentType
}


###############################################################################
#Main Script
###############################################################################
Write-Output "Connect-MgGraph"
Connect-MgGraph -Identity

#Expiration Comparsion Date
$ExpirationDate = (Get-Date).Adddays(+60)

#FromAddress
$FromAddress = "a.bohren@icewolf.ch"
$NotificationSubject = "Entra App Authentication Expiration"

#Get Entra Apps
Write-Output "Getting Entra Apps"
$EntraApps = Get-MgApplication

#Loop through the Apps
Foreach ($EntraApp in $EntraApps)
{
    $AppDisplayName = $EntraApp.DisplayName
    $AppId = $EntraApp.AppId
    $ID = $EntraApp.ID
    Write-Output "$AppID > $ID > $AppDisplayName"

    #Get Owner
    $OwnerMailArray = @()
    $OwnerObject = Get-MgApplicationOwner -ApplicationId $ID
    $Owners = $OwnerObject.AdditionalProperties
    If ($Null -eq $Owners)
    {
        Write-Output "No Owner found"
    } else {
        Foreach ($Owner in $Owners)
        {
            $UPN = $Owner.userPrincipalName
            $Mail = $Owner.mail
            $OwnerMailArray += $Mail
            Write-Output "OwnerUPN: $UPN > OwnerMail: $Mail"
        }
    }

    #Certificate
    If ($Null -ne $EntraApp.KeyCredentials)
    {
        $Certificates = $EntraApp.KeyCredentials
        Foreach ($Certificate in $Certificates)
        {
            $CertDisplayName = $Certificate.DisplayName
            $CertStartDate = $Certificate.StartDateTime
            $CertEndDate = $Certificate.EndDateTime
            Write-Output "Certificate: $CertDisplayName > StartDate: $CertStartDate > EndDate: $CertEndDate"
             If ($ExpirationDate  -gt $CertEndDate)
            {
                Write-Output "$CertDisplayName Certificate will soon expire"
                If ($OwnerMailArray.Count -gt 0)
                {
                    #Send Mail to each Owner
                    Foreach ($OwnerMail in $OwnerMailArray)
                    {
                        Write-Output "Send Mail to $OwnerMail > The App $AppDisplayName has an expiring Certificate $CertDisplayName expiring at $CertEndDate"
                        Send-GraphEmailNotification -From $FromAddress -To $OwnerMail -Subject $NotificationSubject -MessageBody "The App <$AppDisplayName> has an expiring Certificate <$CertDisplayName> expiring at $CertEndDate"
                    }
                }
            }
        }
    }

    #ClientSecret
    If ($Null -ne $EntraApp.PasswordCredentials)
    {
        $ClientSecrets = $EntraApp.PasswordCredentials
        Foreach ($ClientSecret in $ClientSecrets)
        {
            $DisplayName = $ClientSecret.DisplayName
            $StartDate = $ClientSecret.StartDateTime
            $EndDate = $ClientSecret.EndDateTime
            Write-Output "ClientSecret: $DisplayName > StartDate: $StartDate > EndDate: $EndDate"
            If ($ExpirationDate  -gt $EndDate)
            {
                Write-Output "ClientSecret $DisplayName will soon expire"
                If ($OwnerMailArray.Count -gt 0)
                {
                    #Send Mail to each Owner
                    Foreach ($OwnerMail in $OwnerMailArray)
                    {
                        Write-Output "Send Mail to $OwnerMail > The App $AppDisplayName has an expiring Certificate $CertDisplayName expiring at $CertEndDate"
                        Send-GraphEmailNotification -From $FromAddress -To $OwnerMail -Subject $NotificationSubject -MessageBody "The App <$AppDisplayName> has an expiring ClientSecret <$DisplayName> expiring at $EndDate"
                    }
                }
            }
        }
    }
}

Notification Email

This is how the Notification Email looks like

Regards
Andres Bohren

EntraID Logo

PowerShell Logo