Skip to Content
DocsMicrosoft365 ConnectorsManually Assign a Global Reader

Manually enabling the Global Reader Role for Optimize365

This guide walks you through enabling the required Global Reader role for Optimize365’s security scanning, ensuring comprehensive visibility across your Microsoft 365 environment.

The Global Reader role is needed specifically for the Optimize365 app registration, allowing the platform to perform read-only scans of your clients security configurations without making any changes.

You can enable the required permissions for Optimize365 using one of the following methods:

  1. Manual Configuration via the Microsoft 365 Admin Portal
    Follow step-by-step instructions to assign the necessary roles directly through the portal interface.

  2. Automated Setup via PowerShell Script
    Run a pre-built script to streamline the setup process and apply the required permissions with minimal manual effort.

Prerequisites

  • A global administrator user with access to Microsoft Entra-ID

Why Global Reader is Required

Without Global Reader role, Optimize365 can only scan limited security controls. Some controls are not supported via MS-Graph, and require this role to preform via a Powershell cmdlet.

Basic Security Baseline Support

We’ve added support for a Basic Security Baseline to help with lightweight prospecting and visibility use cases. This option provides limited controls that do not require the Global Reader role, making it easier to get started with minimal friction.

Note: The Basic Baseline is intended for entry-level/prospecting scans and insight and does not require enabling the full security baseline.

Enabling Scanning roles

Step 1: Verify Global Reader role (You may need to do this per tenant)

Option 1

  1. Sign in to the Microsoft Entra admin center 
  2. Browse to Entra ID > Roles & admins (Press - Show more.. if you cant see it)
  3. Select the Global Reader (Double-Click on it)

Global-Reader

  1. Select Add assignments and search for “Optimize365-ReadOnly” (AppId: eb18d58b-c50f-4478-94dc-15ef9f12e34e) and press Add

Select-App-Reg

  1. It will appear on the list once it has been added

App-Reg-Onlist

Option 2

Download the PowerShell script: assign-global-reader.ps1

Alternatively, run this PowerShell:

# Disconnect any existing sessions Disconnect-MgGraph -ErrorAction SilentlyContinue # Connect with required scopes Connect-MgGraph -Scopes @( "RoleManagement.ReadWrite.Directory", "Directory.ReadWrite.All", "Application.Read.All" ) $appId = "eb18d58b-c50f-4478-94dc-15ef9f12e34e" $rolesToAssign = @( "Global Reader" ) $servicePrincipal = Get-MgServicePrincipal -Filter "appId eq '$appId'" if (-not $servicePrincipal) { Write-Host "❌ Service principal not found" -ForegroundColor Red Disconnect-MgGraph -ErrorAction SilentlyContinue | Out-Null exit } Write-Host "✅ Found: $($servicePrincipal.DisplayName)`n" -ForegroundColor Green $roleTemplates = Get-MgDirectoryRoleTemplate -All $activatedRoles = Get-MgDirectoryRole -All $assignmentResults = @() foreach ($roleName in $rolesToAssign) { $result = [PSCustomObject]@{ RoleName = $roleName Status = "Unknown" Action = "" } $roleTemplate = $roleTemplates | Where-Object { $_.DisplayName -eq $roleName } if (-not $roleTemplate) { $result.Status = "Failed" $result.Action = "Template not found" $assignmentResults += $result continue } $role = $activatedRoles | Where-Object { $_.RoleTemplateId -eq $roleTemplate.Id } if (-not $role) { try { $role = New-MgDirectoryRole -RoleTemplateId $roleTemplate.Id $result.Action = "Activated" } catch { $result.Status = "Failed" $result.Action = "Activation failed" $assignmentResults += $result continue } } try { $members = Get-MgDirectoryRoleMember -DirectoryRoleId $role.Id -All if ($members | Where-Object { $_.Id -eq $servicePrincipal.Id }) { $result.Status = "Success" $result.Action = "Already assigned" } else { New-MgDirectoryRoleMemberByRef -DirectoryRoleId $role.Id -BodyParameter @{ "@odata.id" = "https://graph.microsoft.com/v1.0/directoryObjects/$($servicePrincipal.Id)" } $result.Status = "Success" $result.Action = "Newly assigned" } } catch { $result.Status = "Failed" $result.Action = "Assignment failed" } $assignmentResults += $result } Start-Sleep -Seconds 5 Write-Host "Verifying assignments...`n" -ForegroundColor Cyan $uri = "https://graph.microsoft.com/v1.0/roleManagement/directory/roleAssignments?`$filter=principalId eq '$($servicePrincipal.Id)'" $roleAssignments = Invoke-MgGraphRequest -Method GET -Uri $uri $verifiedRoles = @() foreach ($assignment in $roleAssignments.value) { $roleDefUri = "https://graph.microsoft.com/v1.0/roleManagement/directory/roleDefinitions/$($assignment.roleDefinitionId)" $roleDef = Invoke-MgGraphRequest -Method GET -Uri $roleDefUri $verifiedRoles += $roleDef.displayName } $successCount = ($assignmentResults | Where-Object { $_.Status -eq "Success" }).Count $failedCount = ($assignmentResults | Where-Object { $_.Status -eq "Failed" }).Count $totalCount = $rolesToAssign.Count $verifiedCount = ($verifiedRoles | Where-Object { $rolesToAssign -contains $_ }).Count Write-Host ("=" * 60) -ForegroundColor Cyan Write-Host "FINAL STATUS" -ForegroundColor Cyan Write-Host ("=" * 60) -ForegroundColor Cyan Write-Host "Total: $totalCount | Success: $successCount | Failed: $failedCount | Verified: $verifiedCount`n" $assignmentResults | Format-Table -Property RoleName, Status, Action -AutoSize if ($verifiedCount -eq $totalCount) { Write-Host "✅ All $totalCount roles assigned and verified" -ForegroundColor Green } elseif ($verifiedCount -gt 0) { Write-Host "⚠️ Partial success: $verifiedCount/$totalCount verified" -ForegroundColor Yellow } else { Write-Host "⚠️ Roles assigned but verification incomplete" -ForegroundColor Yellow } Disconnect-MgGraph -ErrorAction SilentlyContinue | Out-Null Write-Host "`n✅ Disconnected" -ForegroundColor Green

Need Help?

Contact [email protected] if you encounter any issues.

Last updated on