So, you have completed your access reviews for a gazillion groups or access packages in Entra ID, and now would like to show off the result to management? No problem, we can simply dump the results to Excel and do whatever we’d like with them! Let me show you how:
You will need delegated permissions for AccessReview.Read.All, as well as a role that allows you to manage access reviews.
# Connect to the Microsoft Graph with the required scopes
Connect-MgGraph -Scope "AccessReview.Read.All"
# Get all access review defintions
Write-Host "Getting access review definitions"
$Definitions = Get-MgIdentityGovernanceAccessReviewDefinition -All -PageSize 100 -Verbose
# Filter to only those we care about
# $Definitions = $Definitions | Where-Object {$_.Scope.AdditionalProperties.query -like "*accessPackageAssignments*"}
$Definitions = $Definitions | Where-Object { $_.DisplayName -like "Teams review" }
# For each definition, get the latest completed instance
$Report = foreach ($Definition in $Definitions) {
# Use to step through: $Definition = $Definitions | get-random -count 1
Write-Host "Processing $($Definition.Status) AR: $($Definition.DisplayName) ($($Definition.Id))"
$Instances = Get-MgIdentityGovernanceAccessReviewDefinitionInstance -AccessReviewScheduleDefinitionId $Definition.id -All
$Instance = $Instances | Where-Object Status -in @("Completed") | Sort-object EndDateTime -Descending | Select-Object -First 1
if (!$Instance) {
Write-Warning "No active or completed instances found for $($Definition.DisplayName) ($($Definition.Id)) "
}
else {
# Get all decisions for the instance
$Decisions = Get-MgIdentityGovernanceAccessReviewDefinitionInstanceDecision -AccessReviewInstanceId $Instance.Id -AccessReviewScheduleDefinitionId $Definition.id -All
# Process each decision, outputting a custom object for each
foreach ($Decision in $Decisions) {
# Use to step through: $Decision = $Decisions | get-random -count 1
[PSCustomObject] @{
Definition_DisplayName = $Definition.DisplayName
Definition_DescriptionForReviewers = $Definition.DescriptionForReviewers
Definition_Status = $Definition.Status
Decision_Principal_DisplayName = $Decision.Principal.DisplayName
Decision_Principal_Id = $Decision.Principal.Id
Decision_Principal_UserPrincipalName = $Decision.Principal.userPrincipalName ?? $Decision.Principal.AdditionalProperties.userPrincipalName
Decision_Decision = $Decision.Decision
Decision_Recommendation = $Decision.Recommendation
Decision_Resource_DisplayName = $Decision.Resource.DisplayName
Decision_Resource_Id = $Decision.Resource.Id
Decision_Resource_Type = $Decision.Resource.Type
Decision_ReviewedBy_DisplayName = $Decision.ReviewedBy.DisplayName
Decision_ReviewedBy_Id = $Decision.ReviewedBy.Id
Decision_ReviewedBy_UserPrincipalName = $Decision.ReviewedBy.UserPrincipalName ?? $Decision.ReviewedBy.AdditionalProperties.userPrincipalName
Instance_Id = $Instance.Id
Instance_StartDateTime = $Instance.StartDateTime
Instance_EndDateTime = $Instance.EndDateTime
Decision_ApplyResult = $Decision.ApplyResult
}
}
}
}
# To use Export-Excel, run this first: Install-Module ImportExcel -Scope CurrentUser
$Report | Export-Excel -Path ".\AccessReviewReport.xlsx" -AutoSize -AutoFilter -WorksheetName "AccessReviews" -TableName "AccessReviews" -Show -ClearSheet
From the script you will get a lot of columns in Excel, where the name should be pretty much self explanatory:
