Row-Level Security in Power BI: zo beveilig je data per gebruiker

Yorrick Zijlstra · Maart 2026 · 7 min leestijd

Row-Level Security implementatie in Power BI

Row-Level Security (RLS) is de oplossing voor een veelvoorkomend probleem: je hebt meerdere gebruikers die elk een ander deel van je data mogen zien. Denk aan regio-managers die alleen hun regio's zien, klantportalen waar elke klant slechts zijn eigen data kan inzien, of multi-tenant dashboards. In dit artikel laten we zien hoe je RLS stap voor stap implementeert, wat de beperkingen zijn, en hoe je fouten voorkomt.

Wat is Row-Level Security?

Row-Level Security is een beveiligingsmechanisme in Power BI dat bepaalt welke rijen een gebruiker in een tabel kan zien. Je definieert rollen met DAX-filterformules, en die rollen koppel je aan gebruikers. Wanneer iemand een rapport opent, ziet hij alleen de rijen die door zijn rol(len) zijn toegestaan.

Voorbeeld: als je een verkooptabel hebt met een kolom "Regio", kun je een rol "Regio Noord" maken met het filter [Regio] = "Noord". Elke gebruiker in die rol ziet dan alleen verkopen uit Noord.

Wanneer heb je RLS nodig?

RLS is essentieel voor:

Zonder RLS zou je meerdere modellen nodig hebben, of je zou alle data openbaar moeten maken. RLS maakt centralisatie mogelijk.

Stap 1: Rollen definiëren in Power BI Desktop

Open Power BI Desktop en ga naar het tabblad Modeling (of Model). Daar vind je de knop Manage roles.

Laten we een praktische rol maken:

[Regio] = "Noord"

Dit is een DAX-filterexpressie. Deze rol zorgt dat alleen rijen zichtbaar zijn waar de kolom Regio gelijk is aan "Noord". Je kunt meerdere condities combineren met AND/OR:

[Regio] = "Noord" AND [Jaar] >= 2025

Stap 2: Dynamische RLS met USERPRINCIPALNAME()

Statische rollen werken, maar voor grotere organisaties is dynamische RLS veel beter. Daarvoor gebruik je de functie USERPRINCIPALNAME(), die automatisch het e-mailadres of de gebruiker-ID van de ingelogde gebruiker ophaalt.

Je hebt een beveiligingstabel nodig met gebruikers en hun toegestane data:

tbl_RLS = {
  ("john@bedrijf.nl", "Noord"),
  ("anna@bedrijf.nl", "Zuid"),
  ("marc@bedrijf.nl", "Oost")
}

Nu maak je één rol met een dynamische filter:

[Regio] IN
CALCULATE(
  VALUES(tbl_RLS[Regio]),
  tbl_RLS[Email] = USERPRINCIPALNAME()
)

Deze formule zegt: "Filter de verkooptabel zodat alleen de regios zichtbaar zijn die in de tbl_RLS tabel voor deze gebruiker zijn gedefinieerd." Veel schaalbaarder dan statische rollen!

Stap 3: Testen met "View as Role"

In Power BI Desktop kun je je RLS testen voordat je het publiceert. Ga naar het tabblad Modeling en klik op View as Role. Selecteer de rol en open een rapport. Je ziet nu alleen de gefilterde data voor die rol.

Dit is cruciaal: test altijd beide zijden. Test statische rollen met verschillende data, test dynamische RLS door verschillende gebruikers na te bootsen.

Stap 4: Rollen toewijzen in Power BI Service

Na publicatie gaat het echt gebeuren in Power BI Service. Ga naar je dataset en open Security.

Hier voeg je gebruikers toe aan rollen:

Power BI zal de gebruiker dan op basis van USERPRINCIPALNAME() automatisch aan de juiste dataset koppelen.

Statische vs. Dynamische RLS

Statische RLS Dynamische RLS
DAX-filter [Regio] = "Noord" USERPRINCIPALNAME() + lookup tabel
Aantal rollen Veel (per unieke waarde) Weinig (meestal één)
Toevoegen gebruiker Handmatig aan rol in Service Update RLS-tabel, gebruiker automatisch gefilterd
Schaalbaar voor 100+ gebruikers Nee (veel onderhoud) Ja (tabel-driven)
Performance Snel, simpel Snel, maar wat meer DAX-evaluatie

Complexer voorbeeld: RLS met organisatie-hierarchie

In de praktijk is de beveiligingslogica zelden zo simpel als "deze gebruiker mag regio Noord zien". Denk aan een organisatie waar een teamleider zijn eigen team ziet, een afdelingsmanager alle teams binnen zijn afdeling, en een directeur de hele organisatie. Dit los je op met een parent-child hierarchie en de DAX PATH()-functies.

De organisatietabel

Stel je hebt een tabel dim_Organisatie met een klassieke parent-child structuur:

MedewerkerID Naam Email ManagerID Rol
1Lisa de Vrieslisa@bedrijf.nlDirecteur
2Mark Jansenmark@bedrijf.nl1Manager Sales
3Sophie Bakkersophie@bedrijf.nl1Manager Operations
4Tom van Dijktom@bedrijf.nl2Teamlead Noord
5Eva Smiteva@bedrijf.nl2Teamlead Zuid
6Jan Petersjan@bedrijf.nl4Verkoper
7Fleur de Grootfleur@bedrijf.nl5Verkoper

Lisa is directeur en moet alles kunnen zien. Mark is manager Sales en ziet Tom, Eva en hun teams. Tom ziet alleen zijn eigen verkopen en die van Jan. Elke laag in de hierarchie erft de data van de lagen eronder.

Stap 1: PATH() kolommen toevoegen

Voeg twee berekende kolommen toe aan dim_Organisatie:

// Berekende kolom: HierarchiePad
HierarchiePad =
PATH(dim_Organisatie[MedewerkerID], dim_Organisatie[ManagerID])

Dit geeft per rij het volledige pad van de top naar de medewerker. Bijvoorbeeld:

Naam HierarchiePad
Lisa de Vries1
Mark Jansen1|2
Tom van Dijk1|2|4
Jan Peters1|2|4|6
Fleur de Groot1|2|5|7

Het pad van Jan (6) is 1|2|4|6 - hij valt onder Tom (4), die onder Mark (2) valt, die onder Lisa (1) valt.

Stap 2: De RLS-filterexpressie

Nu komt het slimme deel. In de RLS-rol maak je een filter op dim_Organisatie:

// RLS-filter op dim_Organisatie
VAR HuidigeMedewerkerID =
    LOOKUPVALUE(
        dim_Organisatie[MedewerkerID],
        dim_Organisatie[Email], USERPRINCIPALNAME()
    )
RETURN
    PATHCONTAINS(dim_Organisatie[HierarchiePad], HuidigeMedewerkerID)

Wat doet dit precies?

Als Mark (ID 2) inlogt, geeft PATHCONTAINS TRUE voor alle rijen waar 2 in het pad voorkomt: Mark zelf, Tom, Eva, Jan en Fleur. Maar niet Sophie (Operations), want haar pad is 1|3.

Belangrijk: Zorg dat je verkooptabel (of andere feitentabel) een relatie heeft naar dim_Organisatie, bijvoorbeeld via een MedewerkerID-kolom. Het RLS-filter op dim_Organisatie propageert dan automatisch naar de feitentabel via die relatie.

Stap 3: Meerdere niveaus tegelijk

Wat als een gebruiker meerdere rollen heeft? Bijvoorbeeld een regiomanager die ook tijdelijk een ander team beheert? Breid de beveiligingstabel dan uit met meerdere rijen per gebruiker:

// RLS-filter met meerdere posities per gebruiker
VAR MedewerkerIDs =
    CALCULATETABLE(
        VALUES(tbl_RLS_Mapping[MedewerkerID]),
        tbl_RLS_Mapping[Email] = USERPRINCIPALNAME()
    )
RETURN
    COUNTROWS(
        FILTER(
            MedewerkerIDs,
            PATHCONTAINS(
                dim_Organisatie[HierarchiePad],
                [MedewerkerID]
            )
        )
    ) > 0

Met deze opzet kun je iemand in meerdere takken van de hierarchie plaatsen zonder de organisatietabel aan te passen.

Performance-tip voor grote hierarchieën

Bij organisaties met duizenden medewerkers kan PATHCONTAINS per rij kostbaar worden. Een alternatief: maak een bridge-tabel aan in Power Query die voor elke manager alle onderliggende MedewerkerIDs uitklapt naar aparte rijen. Je RLS-filter wordt dan een simpele lookup in plaats van een pad-berekening per rij.

// Bridge-tabel aanpak (eenvoudiger RLS-filter)
[Email] = USERPRINCIPALNAME()

De bridge-tabel bevat dan een rij voor elke combinatie van manager en medewerker die onder hem valt. Dit is performanter maar vereist meer voorbereiding in Power Query.

RLS en DirectQuery beperkingen

Als je directQuery gebruikt (live verbinding met SQL Server, Azure SQL, etc.), werkt RLS anders:

Zorg dat je dit goed plant voordat je DirectQuery en RLS combineert.

Veelgemaakte fouten

1. RLS-rollen toewijzen vergeten
Je publiceert je model met RLS, maar vergeet gebruikers toe te wijzen in Power BI Service. Iemand kan dan het hele model zien. Test dit!

2. Bidirectionele relaties
Als je bidirectionele relaties hebt in je model en je gebruikt CALCULATE() in je RLS-formule, kan dit onverwachte resultaten geven. RLS werkt beter met unidirectionele relaties.

3. CALCULATE() en ALL()
ALL() op een dimensietabel die een RLS-filter heeft, kan dat filter verwijderen en onverwachte resultaten geven. Gebruik je ALL() op de feitentabel zelf, dan verwijdert het de filtercontext maar het RLS-filter op tabelniveau blijft intact. Het risico zit vooral bij dimensietabellen:

// Risico: ALL() op een dimensie met RLS-filter
CALCULATE(
  SUM(Verkopen[Bedrag]),
  ALL(dim_Organisatie)  // Verwijdert het RLS-filter op deze dimensie
)

Vermijd ALL() op tabellen die direct een RLS-filter hebben. Gebruik in plaats daarvan ALLSELECTED() of filter op specifieke kolommen met REMOVEFILTERS().

4. Onvoldoende testen
Test RLS goed in Desktop (View as Role) en in Service. Zorg dat je test met echte gebruikers en verschillende rollen.

Best practices

Hulp bij governance?

YorrData helpt bij het ontwerpen en implementeren van veilige RLS-architecturen op maat voor je organisatie.

Neem contact op →

DAX tools voor je dagelijkse werk

Formatteer je DAX met de DAX Formatter, zoek functies in de Cheat Sheet, of kopieer patronen uit de Measure Patronen bibliotheek.