NL Dutch
FR French
IT Italian
JP Japanese
DE German
US English (US)
ES Spanish

Contact Us

If you still have questions or prefer to get help directly from an agent, please submit a request.
We’ll get back to you as soon as possible.

  • Contact Us
English (US)
NL Dutch
FR French
IT Italian
JP Japanese
DE German
US English (US)
ES Spanish
  • Home
  • CyberFOX Optimize365
  • Getting Started

Manually Assign the Global Reader Role to Optimize365

Written by Owen Parry

Updated at November 12th, 2026

Contact Us

If you still have questions or prefer to get help directly from an agent, please submit a request.
We’ll get back to you as soon as possible.

  • CyberFOX AutoElevate
    Getting Started with AutoElevate AutoElevate Concepts Admin Portal & Settings Elevation & Blocker Rules Technician Mode & Approvals Deploying via RMM Tools PSA & Ticketing Integrations Other Integrations Troubleshooting AutoElevate FAQ Selling AutoElevate
  • CyberFOX Optimize365
    Getting Started Integrations Assessing Client Security Remediation Account & Administration
  • CyberFOX Password Manager
    Using CyberFOX Password Manager Administrating CyberFOX Password Manager Legacy Password Boss
  • CyberFOX DNS Filtering
    Getting Started with DNS Filtering DNS Filtering Concepts Network Requirements for DNS Filtering DNS Filtering Company and Location Setup Managing your DNS Filtering Policies Using Roaming Clients for DNS Filtering DNS Filtering Reports & Logs DNS Filtering Troubleshooting FAQ
  • Marketing Toolkit
    MSP Marketing & Education Toolkit
  • Changelogs for Autoelevate and Password Boss
  • CyberFOX Product Roadmap
  • Current Status
+ More

Overview


Optimize365's security scanning needs the Global Reader role assigned to the Optimize365-ReadOnly app registration. Global Reader lets the platform perform read-only scans of your client's security configuration without making any changes. Use this article when the in-app Optimize Role Granter isn't an option and you need to assign the role yourself, either in the Microsoft Entra admin center or with a PowerShell script.

 

Why Global Reader Is Required


Without the Global Reader role, Optimize365 can scan only a limited set of security controls. Some controls aren't supported through Microsoft Graph and must be checked with PowerShell cmdlets, which need this role.

Basic Security Baseline (no Global Reader needed)

For lightweight prospecting and visibility, Optimize365 supports a Basic Security Baseline. It provides a limited set of controls that do not require the Global Reader role, so you can 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.

 

Prerequisites


  • A Global Administrator user with access to Microsoft Entra ID
  • Repeat the assignment for each client tenant (you may need to do this per tenant)

 

Assign Global Reader


Option 1: Microsoft Entra admin center

Sign in to the Microsoft Entra admin center.

Go to Entra ID > Roles & admins (click Show more... if you can't see it).

Double-click Global Reader to open it.

Global-Reader

Select Add assignments, search for "Optimize365-ReadOnly" (AppId: eb18d58b-c50f-4478-94dc-15ef9f12e34e), and click Add.

Select-App-Reg

Confirm the app now appears in the list of assignments.

App-Reg-Onlist

Option 2: PowerShell script

Download the script: assign-global-reader.ps1, or run the PowerShell below. It connects to Microsoft Graph, finds the Optimize365-ReadOnly service principal, activates the Global Reader role if needed, assigns it, and then verifies the assignment.

# 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

 

Troubleshooting


The script reports "Service principal not found"

The Optimize365-ReadOnly app hasn't been consented in this tenant yet. Complete admin consent first (click CONNECT in Optimize365, or use a consent share link), then run the script again.

The app doesn't appear when searching in Add assignments

Search by the AppId eb18d58b-c50f-4478-94dc-15ef9f12e34e instead of the name, and confirm admin consent has been completed for the tenant.

Need more help

Contact support@optimize365.io.

 

Security Notes


  • Global Reader is a read-only role; Optimize365 uses it to scan, not to change anything.
  • The script requests the Microsoft Graph scopes RoleManagement.ReadWrite.Directory, Directory.ReadWrite.All, and Application.Read.All for the signed-in admin's session, and disconnects when it finishes.

 

Related Articles


  • Required Permissions & Roles
  • Generate Client Login Consent Share Link
  • Manually Enabling Remediation Roles for Optimize365
  • Getting Started
global reader optimize365 global reader role assign global reader manual role assignment optimize365-readonly eb18d58b-c50f-4478-94dc-15ef9f12e34e app registration service principal microsoft entra admin center roles & admins add assignments powershell microsoft graph powershell connect-mggraph assign-global-reader.ps1 basic security baseline prospecting scan read-only scan

Was this article helpful?

Yes
No
Give feedback about this article

Related Articles

  • Manually Assign Remediation Roles to Optimize365
  • Getting Started with Optimize365
CyberFOX

PRACTICAL CYBERSECURITY FOR LEAN IT TEAMS

Platforms
  • Privileged Access Management
  • Password Management
  • DNS Filtering
  • SASE
Industry
  • Higher Education
  • K-12 Education
  • State and Local Government
  • Manufacturing
Company
  • About
  • Awards
  • Partnerships
  • Trust & Legal
  • Contact
  • Login
  • FAQ
  • Referral Program
  • Support
© 2026 CYBERFOX LLC ALL RIGHTS RESERVED | Privacy Policy | Terms of Service | Sitemap
Expand