Copy RLS roles, members, and DAX filters between Power BI semantic models using Tabular Editor 2.
Right-click β done. No PowerShell modules. No SSMS. No scripts to paste.
| Action | What it does |
|---|---|
| π€ Export β All Roles | Exports every role + member + RLS filter to a timestamped JSON file |
| π€ Export β Select Roles | Checkbox UI to pick specific roles. β New auto-selects roles not in previous export |
| π₯ Import β Standard | File picker + confirmation dialog. Simple and reliable for full imports |
| π₯ Import β Select + Diff | Diff view (NEW vs UPDATE), role selector, dry run mode |
| π₯ Merge Members Only | Copies only members into matching target roles, merging with existing ones. RLS DAX untouched, missing roles skipped |
- β Roles
- β Members (Entra users and groups)
- β RLS DAX filter expressions
- β ModelPermission level
Merge Members Only is the exception: it copies members only. The target's existing members are kept (new ones merged in), and its RLS DAX filters and ModelPermission are preserved β never overwritten.
- β OLS (Object Level Security)
- β Measures, columns, or model data
| Requirement | Detail |
|---|---|
| Tabular Editor 2 | Free β tabular.io/te2 or winget install TabularEditor.TabularEditor2 |
| Workspace capacity | Premium, PPU, or Fabric (XMLA endpoint required) |
| XMLA endpoint | Must be set to Read Write in Capacity Admin settings |
| Tenant setting | Allow XMLA endpoints = ON in Power BI Admin Portal |
| Workspace access | Admin or Member on both source and target workspaces (Contributor is not enough) |
No PowerShell modules, no additional NuGet packages, no admin rights.
TE2 ships with AMO/TOM bundled β everything needed is already there.
One-time setup per machine β 30 seconds.
- Download
MacroActions.jsonfrom this repo - Close Tabular Editor 2 if it's open
- Open File Explorer and paste this in the address bar:
%LOCALAPPDATA%\TabularEditor - Drop
MacroActions.jsoninto that folderIf a
MacroActions.jsonalready exists, back it up first β this will replace it. - Reopen Tabular Editor 2
Verify: Connect to any model β right-click the model name in TOM Explorer β you should see Copy Roles βΆ with 5 sub-items.
powerbi://api.powerbi.com/v1.0/myorg/YOUR_WORKSPACE_NAME
Use for full model copy, backup, or routine archive sync.
- TE2 β connect to source model
- Right-click model β Copy Roles β π€ Export β All roles
- Confirmation popup shows role count and file location β OK
- File saved to
C:\temp\PBIRoleCopy\roles_<Model>_<timestamp>.json
Use when you only need to push specific roles (e.g. new roles) to target models.
- TE2 β connect to source model
- Right-click model β Copy Roles β π€ Export β Select roles
- Checkbox UI opens β all roles listed
- Click β New to auto-select roles not present in the previous export file
- Or type a name/code in the filter box to narrow the list
- Click Export Selected
- File saved as
roles_<Model>_partial_<timestamp>.json
β New compares against the latest
roles_*.jsonfile inC:\temp\PBIRoleCopy\.
On first run (no previous file), use the filter box to manually select roles.
Use after a full export or when you trust the file completely.
- TE2 β connect to target model
- Right-click model β Copy Roles β π₯ Import β Standard
- File picker opens at
C:\temp\PBIRoleCopy\β latest file pre-selected - Pick the export file β Open
- Confirmation dialog shows SOURCE β TARGET with role count β Yes
- Done
β οΈ Do NOT press Ctrl+S after import.ExecuteCommandcommits directly via XMLA.
Pressing Ctrl+S causes a conflict error.
Use when you want to preview changes, import only specific roles, or do a dry run.
- TE2 β connect to target model
- Right-click model β Copy Roles β π₯ Import β Select + Diff
- File picker opens β pick file
- Diff view loads β roles classified as:
- π’ [ NEW ] β role does not exist in current model
- π [UPDATE] β role already exists, will be overwritten
- Use β New only to tick just the new additions
- Optional: check Dry Run β click Import β see preview without committing
- Click Import Selected β confirm β done
Use to push members between models without touching RLS DAX β e.g. add new users/groups to existing roles in a target model.
- TE2 β connect to target model
- Right-click model β Copy Roles β π₯ Merge Members Only
- File picker opens at
C:\temp\PBIRoleCopy\β pick a source export file - Grid loads β each role classified by what its members need:
- π’ [+ MERGE] β role exists in target and the file has new members to add (pre-ticked)
- βͺ [IN SYNC] β role exists, all source members already present (nothing to do)
- π΄ [MISSING] β role not present in target β skipped, can't be selected
- Use β New members to tick only roles that have members to add, or the filter box to narrow
- Click βΆ Merge Members β confirmation shows new-member count β Yes
Additive only. The target's existing members are kept and new ones merged in β nothing is removed. RLS DAX filters and ModelPermission are left untouched.
Skipped (missing) roles are written tomissing_roles_<timestamp>.txtin the same folder.
All files are saved to one folder, auto-created on first export:
C:\temp\PBIRoleCopy\
βββ audit.log β every export + import logged
βββ roles_ModelA_20260516_0900.json β full export
βββ roles_ModelA_partial_20260516_1430.json β partial export (selected roles)
βββ roles_ModelB_20260515_1620.json β another model
βββ missing_roles_20260516_1510.txt β roles skipped during a member merge
Audit log format:
2026-05-16 09:00:21 | EXPORT | Model A | 247 roles | roles_ModelA_20260516_0900.json
2026-05-16 09:02:15 | IMPORT | Model A -> Model B | 247 roles | roles_ModelA_20260516_0900.json
2026-05-16 14:30:05 | EXPORT | Model A | 2 of 247 roles | roles_ModelA_partial_20260516_1430.json
2026-05-16 15:10:02 | MERGE MEMBERS | Model A -> Model B | 12 roles, 37 new members, 3 skipped | roles_ModelA_20260516_0900.json
| Feature | Detail |
|---|---|
| Timestamped filenames | No overwrites β every export is a new file |
| _meta block | Files include source model, exported by, timestamp, role count |
| Confirmation dialog | Shows SOURCE β TARGET before any change is made |
| Same-model check | Extra warning if source and target are the same model |
| Diff view | See NEW vs UPDATE before committing |
| Dry run mode | Full preview with zero risk β no ExecuteCommand called |
| Merge is additive | Member merge only adds β existing members, RLS DAX, and ModelPermission are never removed or overwritten |
| Audit log | Every export and import recorded with timestamp |
Export scripts build a TMSL sequence with createOrReplace operations for each role.
The file uses a ##TARGETDB## placeholder β replaced at import time with the name of whichever model TE2 is connected to.
{
"_meta": { "sourceModel": "...", "exportedBy": "...", "roleCount": 247 },
"sequence": {
"operations": [
{
"createOrReplace": {
"object": { "database": "##TARGETDB##", "role": "Role Name" },
"role": { "name": "...", "modelPermission": "read", "members": [...], "tablePermissions": [...] }
}
}
]
}
}ExecuteCommand(tmsl) sends the TMSL directly to Analysis Services via the XMLA endpoint β no Ctrl+S needed.
| Problem | Fix |
|---|---|
| Menu items don't appear | Verify file is at %LOCALAPPDATA%\TabularEditor\MacroActions.json β restart TE2 |
identityProvider 'default' error |
Import scripts auto-fix this. If persists, re-export from source |
| Members not visible in Service | Refresh Service page (F5) β check Security tab |
| Could not connect to server | Workspace name is case-sensitive. Verify XMLA Read/Write is ON |
| Error on save after import | You pressed Ctrl+S. Reconnect to model, re-run import, don't save |
| RLS skipped (missing table) | Target model has different table names β update DAX manually |
| β New selects nothing | No previous export file found β filter by name manually |
System.Drawing compile error |
Scripts require #r "System.Drawing" at top β already included in MacroActions.json |
| File | Description |
|---|---|
MacroActions.json |
Drop into %LOCALAPPDATA%\TabularEditor\ β all 5 actions |
docs/PBI_RoleCopy_Infographic.svg |
Visual overview of the actions |
docs/PBI_RoleCopy_HowTo.docx / .pdf |
Full step-by-step guide with screenshots |
Scripts/TE2_Export - All Rolesy.cs |
Export all roles script (standalone paste version) |
Scripts/TE2_SelectiveExport.cs |
Export selected roles script (standalone paste version) |
Scripts/TE2_ Import - Standard.cs |
Import standard script (standalone paste version) |
Scripts/TE2_SelectiveImport.cs |
Import select + diff script (standalone paste version) |
Scripts/TE2_MergeMembers.cs |
Merge members only script (standalone paste version) |
Issues and PRs welcome. If you find a TE2 version where something breaks, open an issue with the error message and TE2 version number.
MIT β free to use, modify, and share.
Built by DataWithSNS Β· Power BI developer tooling