powershellazure-functionsaccountmicrosoft-entra-idaudit-logging

How can I make a powershell script work in an Azure function with PowerShell 5 and module AzureADPreview?


How can I make this PowerShell script work in an Azure function? This script is using the PowerShell module "AzureADPreview" and works only in PowerShell 5.

Write-Host "START"

Connect-AzureAD -identity
$disabledUsers = Get-AzureADUser -Filter "AccountEnabled eq false" | Select UserPrincipalName

foreach($disabledUser in $disabledUsers) {
    $logs = Get-AzureADAuditDirectoryLogs -Filter "targetResources/any(tr:tr/userPrincipalName eq '$($disabledUser.UserPrincipalName)' and ActivityDisplayName eq 'Disable account')" -Top 1 | select ActivityDateTime 
    if($logs) {
        foreach ($log in $logs) {
            if($log.ActivityDateTime.DateTime -lt (Get-Date).AddDays(-5).DateTime) {
                write-host "Account $($disabledUser.UserPrincipalName) disabled more as 5 days ago. Logs found."
                break
            }
        }
    } else {
        write-host "Account $($disabledUser.UserPrincipalName) disabled more as 30 days ago. Cannot find logs."
    }

    Start-Sleep -Seconds 20
}

Write-Host "FINISH"

Solution

  • Note that, AzureADPreview only works with Windows PowerShell 5.1, not with PowerShell Core Edition (v6.x, v7.x). However, Azure Functions only offer PowerShell Core (v7.2, v7.4), so there's a conflict.

    Alternatively, you can migrate to Microsoft Graph PowerShell module that works in PowerShell Core for retrieving same results.

    I created one function app named demofunc1811 with below properties:

    enter image description here

    Enable system-assigned managed identity in above function app and add required permissions to this service principal. You can refer this SO thread for the same:

    enter image description here

    Make sure to add below dependencies in requirements.psd1 file of App files and restart the Azure Function App:

    # This file enables modules to be automatically managed by the Functions service.
    # See https://aka.ms/functionsmanageddependency for additional information.
    #
    @{
        'Microsoft.Graph.Authentication' = '2.19.0'
        'Microsoft.Graph.Reports' = '2.19.0'
        'Microsoft.Graph.Users' = '2.19.0' 
    }
    

    Now, I created one HTTP Trigger Function and replaced run.ps1 with below sample code and got the response successfully:

    using namespace System.Net
    
    param($Request, $TriggerMetadata)
    
    Write-Host "PowerShell HTTP trigger function processed a request."
    
    # Interact with query parameters or the body of the request.
    $name = $Request.Query.Name
    if (-not $name) {
        $name = $Request.Body.Name
    }
    
    # Write a simple message for testing
    $body = "This HTTP triggered function executed successfully. Pass a name in the query string or in the request body for a personalized response."
    
    if ($name) {
        $body = "Hello, $name. This HTTP triggered function executed successfully."
    }
    
    # Connect to Microsoft Graph using Managed Identity
    Write-Host "Connecting to Microsoft Graph..."
    Connect-MgGraph -Identity
    
    Get-MgContext 
    
    # Retrieve disabled users from Azure AD
    Write-Host "Retrieving disabled users..."
    $disabledUsers = Get-MgUser -Filter "accountEnabled eq false" | Select-Object UserPrincipalName
    
    # Iterate through disabled users and check activity logs
    foreach ($disabledUser in $disabledUsers) {
        $logs = Get-MgAuditLogDirectoryAudit -Filter "targetResources/any(tr:tr/userPrincipalName eq '$($disabledUser.UserPrincipalName)' and activityDisplayName eq 'Disable account')" -Top 1
    
        if ($logs) {
            foreach ($log in $logs) {
                $disabledDate = [DateTime]$log.ActivityDateTime
                $currentDate = (Get-Date)
    
                if ($disabledDate -ge (Get-Date).AddDays(-1)) {
                    Write-Host "Account $($disabledUser.UserPrincipalName) was disabled today."
                    break
                }
                elseif ($disabledDate -lt (Get-Date).AddDays(-5)) {
                    Write-Host "Account $($disabledUser.UserPrincipalName) disabled more than 5 days ago. Logs found."
                    break
                }
            }
        }
        else {
            Write-Host "Account $($disabledUser.UserPrincipalName) disabled more than 30 days ago. Cannot find logs."
        }
    }
    
    # Associate values to output bindings by calling 'Push-OutputBinding'.
    Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{
        StatusCode = [HttpStatusCode]::OK
        Body = $body
    })
    

    Response: