Back to Blog
5 min read

Microsoft Fabric Security: Protecting Your Data Platform

Security is fundamental to any enterprise data platform. Today, I will cover the security model in Microsoft Fabric and best practices for protecting your data.

Fabric Security Model

┌─────────────────────────────────────────────────────┐
│              Fabric Security Layers                  │
├─────────────────────────────────────────────────────┤
│                                                      │
│  ┌─────────────────────────────────────────────────┐│
│  │            Identity (Azure AD)                   ││
│  │  - User authentication                          ││
│  │  - Service principals                           ││
│  │  - Managed identities                           ││
│  └─────────────────────────────────────────────────┘│
│                        │                            │
│  ┌─────────────────────┴───────────────────────────┐│
│  │              Tenant Settings                     ││
│  │  - Feature enablement                           ││
│  │  - Export/sharing policies                      ││
│  │  - Data residency                               ││
│  └─────────────────────────────────────────────────┘│
│                        │                            │
│  ┌─────────────────────┴───────────────────────────┐│
│  │              Workspace Security                  ││
│  │  - Workspace roles (Admin, Member, etc.)        ││
│  │  - Capacity assignment                          ││
│  └─────────────────────────────────────────────────┘│
│                        │                            │
│  ┌─────────────────────┴───────────────────────────┐│
│  │              Item Permissions                    ││
│  │  - Read, Write, Reshare                         ││
│  │  - Build permissions                            ││
│  └─────────────────────────────────────────────────┘│
│                        │                            │
│  ┌─────────────────────┴───────────────────────────┐│
│  │              Data Security                       ││
│  │  - Row-level security (RLS)                     ││
│  │  - Column-level security (OLS)                  ││
│  │  - Sensitivity labels                           ││
│  └─────────────────────────────────────────────────┘│
│                                                      │
└─────────────────────────────────────────────────────┘

Authentication

Azure AD Integration

# All Fabric authentication flows through Azure AD

authentication_methods = {
    "interactive_users": {
        "method": "Azure AD sign-in",
        "supports": ["MFA", "Conditional Access", "SSO"],
        "license": "Power BI Pro or Fabric capacity"
    },
    "service_principals": {
        "method": "App registration with client secret/certificate",
        "use_cases": ["Automation", "CI/CD", "API access"],
        "setup": "Azure AD > App registrations"
    },
    "managed_identities": {
        "method": "Azure-managed identity",
        "use_cases": ["Azure services integration", "Secure credential-less access"],
        "types": ["System-assigned", "User-assigned"]
    }
}

Service Principal Setup

# Using service principal for Fabric API access
from azure.identity import ClientSecretCredential
import requests

def get_fabric_client(tenant_id: str, client_id: str, client_secret: str):
    """Create authenticated Fabric API client"""

    credential = ClientSecretCredential(
        tenant_id=tenant_id,
        client_id=client_id,
        client_secret=client_secret
    )

    token = credential.get_token("https://api.fabric.microsoft.com/.default")

    return {
        "Authorization": f"Bearer {token.token}",
        "Content-Type": "application/json"
    }

# Enable service principal in Fabric Admin Portal:
# Admin Portal > Tenant settings > Developer settings
# > Allow service principals to use Power BI APIs

Workspace Security

# Workspace roles and permissions
workspace_roles = {
    "Admin": {
        "permissions": [
            "Full control over workspace",
            "Add/remove members",
            "Delete workspace",
            "Manage all items",
            "Publish apps"
        ],
        "assign_to": "Workspace owners, platform team"
    },
    "Member": {
        "permissions": [
            "Create, edit, delete items",
            "Share items",
            "Manage item permissions",
            "Cannot add/remove workspace members"
        ],
        "assign_to": "Data engineers, senior analysts"
    },
    "Contributor": {
        "permissions": [
            "Create, edit, delete items",
            "Cannot share items",
            "Cannot manage permissions"
        ],
        "assign_to": "Developers, analysts"
    },
    "Viewer": {
        "permissions": [
            "View items",
            "Cannot create or edit",
            "Cannot share"
        ],
        "assign_to": "Report consumers, stakeholders"
    }
}

# Best practices
workspace_security_practices = [
    "Use Azure AD groups for role assignments",
    "Minimize Admin role assignments",
    "Separate workspaces by security boundary",
    "Regular access reviews"
]

Managing Workspace Access

import requests

def add_workspace_member(workspace_id: str, user_email: str, role: str, headers: dict):
    """Add user to workspace with specified role"""

    url = f"https://api.powerbi.com/v1.0/myorg/groups/{workspace_id}/users"

    payload = {
        "emailAddress": user_email,
        "groupUserAccessRight": role  # Admin, Member, Contributor, Viewer
    }

    response = requests.post(url, headers=headers, json=payload)
    return response.status_code == 200

def get_workspace_users(workspace_id: str, headers: dict):
    """Get all users in a workspace"""

    url = f"https://api.powerbi.com/v1.0/myorg/groups/{workspace_id}/users"
    response = requests.get(url, headers=headers)
    return response.json()["value"]

def remove_workspace_user(workspace_id: str, user_email: str, headers: dict):
    """Remove user from workspace"""

    url = f"https://api.powerbi.com/v1.0/myorg/groups/{workspace_id}/users/{user_email}"
    response = requests.delete(url, headers=headers)
    return response.status_code == 200

Item-Level Permissions

# Permissions at item level
item_permissions = {
    "read": "View the item and its data",
    "write": "Modify the item",
    "reshare": "Share item with others",
    "build": "Create new content based on this item (semantic models)"
}

# Grant item permissions via API
def grant_item_permission(workspace_id: str, item_id: str, user_email: str, permission: str, headers: dict):
    """Grant permission on specific item"""

    url = f"https://api.fabric.microsoft.com/v1/workspaces/{workspace_id}/items/{item_id}/permissions"

    payload = {
        "principal": {
            "type": "User",
            "emailAddress": user_email
        },
        "permission": permission  # Read, Write, Reshare
    }

    response = requests.post(url, headers=headers, json=payload)
    return response.json()

Row-Level Security (RLS)

// RLS in semantic models

// 1. Create security table mapping users to their data access
// Table: UserSecurity
// Columns: UserEmail, Region, Department

// 2. Define RLS role with DAX filter
// Role: RegionalAccess
// Table: Sales
// Filter:
Sales[Region] IN
    CALCULATETABLE(
        VALUES(UserSecurity[Region]),
        UserSecurity[UserEmail] = USERPRINCIPALNAME()
    )

// 3. Complex RLS: Manager sees all reports' data
// Role: ManagerAccess
// Table: Sales
Sales[SalesRepEmail] IN
    CALCULATETABLE(
        DISTINCT(OrgHierarchy[ReportEmail]),
        PATHCONTAINS(
            OrgHierarchy[ManagerPath],
            LOOKUPVALUE(
                OrgHierarchy[EmployeeID],
                OrgHierarchy[Email],
                USERPRINCIPALNAME()
            )
        )
    )
    ||
    Sales[SalesRepEmail] = USERPRINCIPALNAME()

Testing RLS

# Test RLS with API
def test_rls(dataset_id: str, role_name: str, user_email: str, headers: dict):
    """Execute query as specific user to test RLS"""

    url = f"https://api.powerbi.com/v1.0/myorg/datasets/{dataset_id}/executeQueries"

    payload = {
        "queries": [
            {
                "query": "EVALUATE SUMMARIZE(Sales, Sales[Region], \"Total\", SUM(Sales[Amount]))"
            }
        ],
        "impersonatedUserName": user_email
    }

    response = requests.post(url, headers=headers, json=payload)
    return response.json()

Sensitivity Labels

# Microsoft Information Protection labels
sensitivity_labels = {
    "public": {
        "description": "Data can be shared externally",
        "restrictions": "None",
        "encryption": "No"
    },
    "internal": {
        "description": "Internal use only",
        "restrictions": "No external sharing",
        "encryption": "Optional"
    },
    "confidential": {
        "description": "Sensitive business data",
        "restrictions": "Limited sharing, no export",
        "encryption": "Yes"
    },
    "highly_confidential": {
        "description": "Most sensitive data",
        "restrictions": "Strict access control",
        "encryption": "Yes, with additional controls"
    }
}

# Apply label via API
def apply_sensitivity_label(workspace_id: str, item_id: str, label_id: str, headers: dict):
    """Apply sensitivity label to item"""

    url = f"https://api.fabric.microsoft.com/v1/workspaces/{workspace_id}/items/{item_id}/sensitivityLabel"

    payload = {
        "labelId": label_id
    }

    response = requests.put(url, headers=headers, json=payload)
    return response.json()

Data Protection Best Practices

security_best_practices = {
    "principle_of_least_privilege": [
        "Grant minimum necessary permissions",
        "Use Viewer role as default",
        "Elevate only when needed"
    ],
    "access_management": [
        "Use Azure AD groups for assignments",
        "Implement regular access reviews",
        "Remove access promptly on role change"
    ],
    "data_classification": [
        "Apply sensitivity labels consistently",
        "Train users on classification",
        "Automate label application where possible"
    ],
    "monitoring": [
        "Enable audit logging",
        "Review access patterns",
        "Alert on suspicious activity"
    ]
}

Security is an ongoing responsibility. Tomorrow, I will cover Workspace Roles in more detail.

Resources

Michael John Peña

Michael John Peña

Senior Data Engineer based in Sydney. Writing about data, cloud, and technology.