🔐 LeastPrivilegedMSGraph

PowerShell module for analyzing Microsoft Graph permissions

Installation

Install-Module -Name LeastPrivilegedMSGraph -Repository PSGallery
Import-Module LeastPrivilegedMSGraph

Prerequisites

Quick Start Example

1. Setup Credentials

# Define your Azure AD app and workspace details
$tenantId = "12345678-1234-1234-1234-123456789012"
$clientId = "87654321-4321-4321-4321-210987654321"
$clientSecret = "your-client-secret-here" | ConvertTo-SecureString -AsPlainText -Force
$workspaceId = "abcdef00-1111-2222-3333-444444444444"
$daysToAnalyze = 30

2. Initialize and Connect

# Initialize Log Analytics API service
Initialize-LogAnalyticsApi

# Connect to both Microsoft Graph and Log Analytics
$connectSplat = @{
    ClientID     = $clientId
    TenantID     = $tenantId
    ClientSecret = $clientSecret
    Service      = "LogAnalytics", "GraphBeta"
}
Connect-EntraService @connectSplat

3. Analyze Permissions

# Get all apps with Graph permissions
$apps = Get-AppRoleAssignment

# Add API activity data from Log Analytics
$apps | Get-AppActivityData -WorkspaceId $workspaceId -Days $daysToAnalyze

# Add throttling statistics
$apps | Get-AppThrottlingData -WorkspaceId $workspaceId -Days $daysToAnalyze

# Perform permission analysis
$analysis = $apps | Get-PermissionAnalysis

# Generate interactive HTML report
Export-PermissionAnalysisReport -AppData $analysis -OutputPath ".\PermissionReport.html"

4. View Results

# Open the report in your default browser
Invoke-Item ".\PermissionReport.html"

# Or review in PowerShell
$analysis | Where-Object { $_.ExcessPermissions.Count -gt 0 } |
    Select-Object PrincipalName,
                  @{N='Current';E={$_.CurrentPermissions.Count}},
                  @{N='Optimal';E={$_.OptimalPermissions.Count}},
                  @{N='Excess';E={$_.ExcessPermissions.Count}} |
    Format-Table -AutoSize

Complete Workflow Example

# Complete permission analysis workflow
$tenantId = "12345678-1234-1234-1234-123456789012"
$clientId = "87654321-4321-4321-4321-210987654321"
$clientSecret = "your-secret" | ConvertTo-SecureString -AsPlainText -Force
$workspaceId = "abcdef00-1111-2222-3333-444444444444"

# Setup
Import-Module LeastPrivilegedMSGraph
Initialize-LogAnalyticsApi
$connectSplat = @{
    ClientID     = $clientId
    TenantID     = $tenantId
    ClientSecret = $clientSecret
    Service      = "LogAnalytics", "GraphBeta"
}

Connect-EntraService @connectSplat

# Analysis pipeline
$results = Get-AppRoleAssignment |
    Get-AppActivityData -WorkspaceId $workspaceId -Days 30 |
    Get-AppThrottlingData -WorkspaceId $workspaceId -Days 30 |
    Get-PermissionAnalysis

# Generate report
Export-PermissionAnalysisReport -AppData $results -OutputPath ".\analysis-$(Get-Date -Format 'yyyyMMdd').html"

# Display summary
"`nAnalysis Complete!"
"Applications analyzed: $($results.Count)"
"Over-privileged apps: $(($results | Where-Object { $_.ExcessPermissions.Count -gt 0 }).Count)"
"Total excess permissions: $(($results.ExcessPermissions | Measure-Object).Count)"

Analyzing Specific Applications

# Analyze only specific applications
$criticalApps = Get-AppRoleAssignment |
    Where-Object { $_.PrincipalName -like "*Production*" }

$analysis = $criticalApps |
    Get-AppActivityData -WorkspaceId $workspaceId -Days 90 |
    Get-AppThrottlingData -WorkspaceId $workspaceId -Days 90 |
    Get-PermissionAnalysis

# Find apps with high-privilege permissions they don't use
$dangerousPerms = @('Directory.ReadWrite.All', 'RoleManagement.ReadWrite.Directory')
$overPrivileged = $analysis | Where-Object {
    $excessive = $_.ExcessPermissions | Where-Object { $_ -in $dangerousPerms }
    $excessive.Count -gt 0
}

if ($overPrivileged) {
    Write-Warning "Found $($overPrivileged.Count) apps with unused high-privilege permissions!"
    $overPrivileged | Select-Object PrincipalName, @{N='UnusedHighPrivPerms';E={$_.ExcessPermissions | Where-Object { $_ -in $dangerousPerms }}}
}

Troubleshooting

Connection Issues

# Test Log Analytics connectivity
Initialize-LogAnalyticsApi
Connect-EntraService -ClientID $clientId -TenantID $tenantId -ClientSecret $clientSecret -Service "LogAnalytics"

# Verify you can query the workspace
$testQuery = @{
    query = "MicrosoftGraphActivityLogs | take 1"
}
Invoke-EntraRequest -Service 'LogAnalytics' -ApiUrl "/v1/workspaces/$workspaceId/query" -Method POST -Body $testQuery

No Activity Data

Permission Errors

# Verify your app has required permissions
Get-MgServicePrincipal -Filter "appId eq '$clientId'" |
    Select-Object -ExpandProperty AppRoles |
    Where-Object { $_.Value -in @('Application.Read.All', 'AppRoleAssignment.Read.All') }

Next Steps