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.