All scripts
Microsoft 365 733

Get-PowerBIWorkspaceAccessReport

Exports every Power BI workspace along with its members and their access level, so you can spot workspaces with too many admins or external accounts still sitting in them.

Get-PowerBIWorkspaceAccessReport.ps1
<#
.SYNOPSIS
    Reports on Power BI workspace membership and access levels across the tenant.
.DESCRIPTION
    Connects to the Power BI service and pulls every workspace along with its members,
    then exports a CSV showing who has Admin, Member, Contributor, or Viewer access to
    each one. Useful for spotting workspaces with too many admins, or external accounts
    that were added once for a project and never removed.
.AUTHOR
    Shehryar Hassan
.EXAMPLE
    .\Get-PowerBIWorkspaceAccessReport.ps1 -OutputPath "C:\Reports\PowerBIAccess.csv"
#>

[CmdletBinding()]
param(
    [Parameter(Mandatory = $false)]
    [string]$OutputPath = ".\PowerBIWorkspaceAccessReport.csv",

    [Parameter(Mandatory = $false)]
    [switch]$IncludePersonalWorkspaces
)

if (-not (Get-Module -ListAvailable -Name MicrosoftPowerBIMgmt)) {
    Write-Host "The MicrosoftPowerBIMgmt module is not installed. Run: Install-Module -Name MicrosoftPowerBIMgmt -Scope CurrentUser" -ForegroundColor Yellow
    return
}

Import-Module MicrosoftPowerBIMgmt -ErrorAction Stop

try {
    Connect-PowerBIServiceAccount -ErrorAction Stop | Out-Null
}
catch {
    Write-Host "Could not connect to the Power BI service. $_" -ForegroundColor Red
    return
}

Write-Host "Pulling workspace list, this can take a minute on larger tenants..."

$workspaces = Get-PowerBIWorkspace -Scope Organization -All

if (-not $IncludePersonalWorkspaces) {
    $workspaces = $workspaces | Where-Object { $_.Type -eq "Workspace" }
}

$results = New-Object System.Collections.Generic.List[Object]

foreach ($workspace in $workspaces) {
    Write-Host "Checking $($workspace.Name)..."

    try {
        $users = Get-PowerBIWorkspaceUser -Scope Organization -Id $workspace.Id -ErrorAction Stop
    }
    catch {
        Write-Host "  Skipped $($workspace.Name), could not read membership. $_" -ForegroundColor Yellow
        continue
    }

    if (-not $users -or $users.Count -eq 0) {
        $results.Add([PSCustomObject]@{
            WorkspaceName      = $workspace.Name
            WorkspaceId        = $workspace.Id
            WorkspaceState     = $workspace.State
            UserPrincipalName  = "(no members returned)"
            AccessRight        = ""
            UserType           = ""
        })
        continue
    }

    foreach ($user in $users) {
        $results.Add([PSCustomObject]@{
            WorkspaceName      = $workspace.Name
            WorkspaceId        = $workspace.Id
            WorkspaceState     = $workspace.State
            UserPrincipalName  = $user.Identifier
            AccessRight        = $user.AccessRight
            UserType           = $user.PrincipalType
        })
    }
}

$results | Sort-Object WorkspaceName, AccessRight | Export-Csv -Path $OutputPath -NoTypeInformation -Encoding UTF8

$adminCount = ($results | Where-Object { $_.AccessRight -eq "Admin" }).Count
Write-Host ""
Write-Host "Done. $($workspaces.Count) workspaces checked, $($results.Count) membership rows exported to $OutputPath"
Write-Host "$adminCount rows have Admin access, worth a look if that number seems high for your tenant size."

Disconnect-PowerBIServiceAccount | Out-Null

Read it before you run it, and test in a safe tenant first.