{
  "nbformat": 4,
  "nbformat_minor": 5,
  "metadata": {
    "kernelspec": {
      "display_name": "Python 3",
      "language": "python",
      "name": "python3"
    },
    "language_info": {
      "name": "python",
      "version": "3.13.0"
    },
    "blog_metadata": {
      "topic": "How to secure OneLake shortcuts without killing data sharing velocity",
      "slug": "how-to-secure-onelake-shortcuts-without-killing-data-sharing",
      "generated_by": "LinkedIn Post Generator + Azure OpenAI",
      "generated_at": "2026-07-06T18:00:39.106Z"
    }
  },
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "# How to secure OneLake shortcuts without killing data sharing velocity\n",
        "\n",
        "This notebook turns the blog post into a hands-on governance validation workflow using Python. It focuses on the core idea that shortcut security is not just about data access, but also about control-plane authority in the workspace. You will build a small mock inventory, detect metadata gaps, correlate shortcut-bearing workspaces with role assignments, and export review outputs."
      ]
    },
    {
      "cell_type": "code",
      "metadata": {},
      "source": [
        "%pip install pandas"
      ],
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {},
      "source": [
        "from collections import defaultdict\n",
        "import csv\n",
        "import os\n",
        "import pandas as pd"
      ],
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## Governance model and validation plan\n",
        "\n",
        "The blog's main security lens is the separation between:\n",
        "\n",
        "- **Control plane**: who can administer or operate within a Fabric workspace\n",
        "- **Data plane**: who can read the underlying OneLake data path\n",
        "\n",
        "A practical review flow is:\n",
        "\n",
        "1. Inventory shortcuts\n",
        "2. Group them by workspace\n",
        "3. Check metadata quality such as owner and sensitivity label\n",
        "4. Correlate shortcut-bearing workspaces with workspace role assignments\n",
        "5. Prioritize review queues where broad roles and weak metadata overlap\n",
        "\n",
        "Below is a simple text rendering of the workflow described in the post:\n",
        "\n",
        "- Producer workspace publishes governed data\n",
        "- Consumer workspace creates a shortcut\n",
        "- Governance reviews shortcut metadata: owner, label, source\n",
        "- Governance reviews workspace role assignments in shortcut-bearing workspaces\n",
        "- Findings go into a review queue by workspace"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## Example 1: Build a shortcut inventory and group it by workspace\n",
        "\n",
        "This first validation step creates a small sample inventory of OneLake shortcuts and groups them by workspace. In a real Fabric implementation, this inventory would come from admin or item APIs, but the mock data is enough to validate the governance logic."
      ]
    },
    {
      "cell_type": "code",
      "metadata": {},
      "source": [
        "# Sample shortcut inventory used by governance checks\n",
        "from collections import defaultdict\n",
        "\n",
        "shortcuts = [\n",
        "    {\"workspace\": \"Sales-Analytics\", \"shortcut\": \"orders_curated\", \"owner\": \"alice@contoso.com\", \"label\": \"Confidential\", \"target\": \"/lakehouse/prod/orders\"},\n",
        "    {\"workspace\": \"Sales-Analytics\", \"shortcut\": \"returns_raw\", \"owner\": \"\", \"label\": \"Internal\", \"target\": \"/lakehouse/raw/returns\"},\n",
        "    {\"workspace\": \"Finance-Planning\", \"shortcut\": \"forecast_gold\", \"owner\": \"bob@contoso.com\", \"label\": \"\", \"target\": \"/warehouse/gold/forecast\"},\n",
        "]\n",
        "\n",
        "by_workspace = defaultdict(list)\n",
        "for item in shortcuts:\n",
        "    by_workspace[item[\"workspace\"]].append(item)\n",
        "\n",
        "for workspace, items in by_workspace.items():\n",
        "    print(f\"{workspace}: {len(items)} shortcut(s)\")\n",
        "\n",
        "print(\"\\nDetailed inventory:\")\n",
        "for workspace, items in by_workspace.items():\n",
        "    for item in items:\n",
        "        print(f\"- {workspace} | {item['shortcut']} | owner={item['owner'] or 'MISSING'} | label={item['label'] or 'MISSING'} | target={item['target']}\")"
      ],
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## Example 2: Run a lightweight metadata audit\n",
        "\n",
        "The blog recommends starting with metadata quality before trying to automate everything. This audit flags shortcuts with missing owners or missing sensitivity labels and creates a review queue by workspace."
      ]
    },
    {
      "cell_type": "code",
      "metadata": {},
      "source": [
        "# Lightweight governance audit: flag missing owners or sensitivity labels\n",
        "from collections import defaultdict\n",
        "\n",
        "shortcuts = [\n",
        "    {\"workspace\": \"Sales-Analytics\", \"shortcut\": \"orders_curated\", \"owner\": \"alice@contoso.com\", \"label\": \"Confidential\"},\n",
        "    {\"workspace\": \"Sales-Analytics\", \"shortcut\": \"returns_raw\", \"owner\": \"\", \"label\": \"Internal\"},\n",
        "    {\"workspace\": \"Finance-Planning\", \"shortcut\": \"forecast_gold\", \"owner\": \"bob@contoso.com\", \"label\": \"\"},\n",
        "]\n",
        "\n",
        "review_queue = defaultdict(list)\n",
        "for s in shortcuts:\n",
        "    issues = []\n",
        "    if not s[\"owner\"]:\n",
        "        issues.append(\"missing_owner\")\n",
        "    if not s[\"label\"]:\n",
        "        issues.append(\"missing_label\")\n",
        "    if issues:\n",
        "        review_queue[s[\"workspace\"]].append({\"shortcut\": s[\"shortcut\"], \"issues\": issues})\n",
        "\n",
        "for workspace, findings in review_queue.items():\n",
        "    print(f\"\\nWorkspace: {workspace}\")\n",
        "    for finding in findings:\n",
        "        print(f\" - {finding['shortcut']}: {', '.join(finding['issues'])}\")\n",
        "\n",
        "print(\"\\nReview queue object:\")\n",
        "print(dict(review_queue))"
      ],
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## Example 3: Export audit findings to CSV\n",
        "\n",
        "Once findings are small enough to act on, export them for workspace owners or governance reviewers. This mirrors the blog's recommendation to create a lightweight, actionable review queue rather than aiming for perfect automation on day one."
      ]
    },
    {
      "cell_type": "code",
      "metadata": {},
      "source": [
        "# Export grouped audit findings to CSV for workspace owners\n",
        "import csv\n",
        "\n",
        "findings = [\n",
        "    {\"workspace\": \"Sales-Analytics\", \"shortcut\": \"returns_raw\", \"issues\": \"missing_owner\"},\n",
        "    {\"workspace\": \"Finance-Planning\", \"shortcut\": \"forecast_gold\", \"issues\": \"missing_label\"},\n",
        "]\n",
        "\n",
        "output_path = \"shortcut_governance_findings.csv\"\n",
        "with open(output_path, \"w\", newline=\"\", encoding=\"utf-8\") as f:\n",
        "    writer = csv.DictWriter(f, fieldnames=[\"workspace\", \"shortcut\", \"issues\"])\n",
        "    writer.writeheader()\n",
        "    writer.writerows(findings)\n",
        "\n",
        "print(f\"Wrote {output_path}\")\n",
        "\n",
        "with open(output_path, \"r\", encoding=\"utf-8\") as f:\n",
        "    print(\"\\nCSV preview:\")\n",
        "    print(f.read())"
      ],
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## Example 4: Correlate shortcut-bearing workspaces with workspace role assignments\n",
        "\n",
        "The most important technical insight in the post is that the shortcut question is really two questions:\n",
        "\n",
        "1. Who can manage or alter access in this workspace?\n",
        "2. Who can read the underlying data?\n",
        "\n",
        "This example recreates the PowerShell logic in Python by joining shortcut-bearing workspaces to workspace role assignments. The goal is to identify where operational authority may be broader than the data owner intended."
      ]
    },
    {
      "cell_type": "code",
      "metadata": {},
      "source": [
        "# Export Fabric workspace role assignments and map to shortcut-bearing workspaces (Python version)\n",
        "shortcut_workspaces = [\n",
        "    {\"Workspace\": \"Sales-Analytics\", \"ShortcutCount\": 2},\n",
        "    {\"Workspace\": \"Finance-Planning\", \"ShortcutCount\": 1},\n",
        "]\n",
        "\n",
        "role_assignments = [\n",
        "    {\"Workspace\": \"Sales-Analytics\", \"Principal\": \"DataOps\", \"Role\": \"Admin\"},\n",
        "    {\"Workspace\": \"Sales-Analytics\", \"Principal\": \"Analysts\", \"Role\": \"Member\"},\n",
        "    {\"Workspace\": \"Finance-Planning\", \"Principal\": \"AllFinance\", \"Role\": \"Admin\"},\n",
        "]\n",
        "\n",
        "shortcut_df = pd.DataFrame(shortcut_workspaces)\n",
        "roles_df = pd.DataFrame(role_assignments)\n",
        "report_df = roles_df.merge(shortcut_df, on=\"Workspace\", how=\"inner\")\n",
        "\n",
        "print(\"Workspace shortcut access report:\")\n",
        "print(report_df.to_string(index=False))\n",
        "\n",
        "report_path = \"workspace_shortcut_access_report.csv\"\n",
        "report_df.to_csv(report_path, index=False)\n",
        "print(f\"\\nWrote {report_path}\")"
      ],
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## Example 5: Flag over-privileged access patterns in shortcut-bearing workspaces\n",
        "\n",
        "Shortcut-bearing workspaces deserve tighter review than ordinary workspaces because broad Admin or Member assignments can widen the effective risk surface. This example flags high-privilege roles in workspaces that contain shortcuts and assigns a simple risk message."
      ]
    },
    {
      "cell_type": "code",
      "metadata": {},
      "source": [
        "# Flag over-privileged access patterns in shortcut-bearing workspaces (Python version)\n",
        "report = [\n",
        "    {\"Workspace\": \"Sales-Analytics\", \"Principal\": \"DataOps\", \"Role\": \"Admin\", \"ShortcutCount\": 2},\n",
        "    {\"Workspace\": \"Sales-Analytics\", \"Principal\": \"Analysts\", \"Role\": \"Member\", \"ShortcutCount\": 2},\n",
        "    {\"Workspace\": \"Finance-Planning\", \"Principal\": \"AllFinance\", \"Role\": \"Admin\", \"ShortcutCount\": 1},\n",
        "]\n",
        "\n",
        "high_privilege_roles = {\"Admin\", \"Member\"}\n",
        "findings = []\n",
        "for row in report:\n",
        "    if row[\"ShortcutCount\"] > 0 and row[\"Role\"] in high_privilege_roles:\n",
        "        risk = \"Review admin scope\" if row[\"Role\"] == \"Admin\" else \"Validate member need\"\n",
        "        findings.append({\n",
        "            \"Workspace\": row[\"Workspace\"],\n",
        "            \"Principal\": row[\"Principal\"],\n",
        "            \"Role\": row[\"Role\"],\n",
        "            \"Risk\": risk,\n",
        "        })\n",
        "\n",
        "findings_df = pd.DataFrame(findings)\n",
        "print(findings_df.to_string(index=False))"
      ],
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## Example 6: Summarize risky principals for a monthly access review\n",
        "\n",
        "The blog recommends recurring review for stale shortcuts and stale role assignments. This example groups risky principals by workspace so governance teams can run a monthly review meeting with a compact summary."
      ]
    },
    {
      "cell_type": "code",
      "metadata": {},
      "source": [
        "# Summarize risky principals per workspace for an access review meeting (Python version)\n",
        "findings = [\n",
        "    {\"Workspace\": \"Sales-Analytics\", \"Principal\": \"DataOps\", \"Role\": \"Admin\", \"Risk\": \"Review admin scope\"},\n",
        "    {\"Workspace\": \"Sales-Analytics\", \"Principal\": \"Analysts\", \"Role\": \"Member\", \"Risk\": \"Validate member need\"},\n",
        "    {\"Workspace\": \"Finance-Planning\", \"Principal\": \"AllFinance\", \"Role\": \"Admin\", \"Risk\": \"Review admin scope\"},\n",
        "]\n",
        "\n",
        "findings_df = pd.DataFrame(findings)\n",
        "summary_df = findings_df.groupby(\"Workspace\", as_index=False).agg({\n",
        "    \"Principal\": lambda s: \"; \".join(s),\n",
        "    \"Role\": lambda s: \"; \".join(s),\n",
        "    \"Risk\": lambda s: \"; \".join(s),\n",
        "}).rename(columns={\n",
        "    \"Principal\": \"RiskyPrincipals\",\n",
        "    \"Role\": \"Roles\",\n",
        "    \"Risk\": \"Risks\",\n",
        "})\n",
        "\n",
        "print(summary_df.to_string(index=False))\n",
        "\n",
        "summary_path = \"workspace_access_review_summary.csv\"\n",
        "summary_df.to_csv(summary_path, index=False)\n",
        "print(f\"\\nWrote {summary_path}\")"
      ],
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## Example 7: End-to-end governance scoring for shortcut-bearing workspaces\n",
        "\n",
        "This final hands-on example combines metadata quality and workspace role scope into a simple risk score. It is not a Fabric-native control, but it helps validate the blog's core argument: shortcut governance fails when teams treat workspace permissions and data permissions as the same decision."
      ]
    },
    {
      "cell_type": "code",
      "metadata": {},
      "source": [
        "# End-to-end governance scoring example\n",
        "shortcuts = [\n",
        "    {\"workspace\": \"Sales-Analytics\", \"shortcut\": \"orders_curated\", \"owner\": \"alice@contoso.com\", \"label\": \"Confidential\", \"target\": \"/lakehouse/prod/orders\"},\n",
        "    {\"workspace\": \"Sales-Analytics\", \"shortcut\": \"returns_raw\", \"owner\": \"\", \"label\": \"Internal\", \"target\": \"/lakehouse/raw/returns\"},\n",
        "    {\"workspace\": \"Finance-Planning\", \"shortcut\": \"forecast_gold\", \"owner\": \"bob@contoso.com\", \"label\": \"\", \"target\": \"/warehouse/gold/forecast\"},\n",
        "    {\"workspace\": \"HR-Insights\", \"shortcut\": \"employees_sensitive\", \"owner\": \"\", \"label\": \"Highly Confidential\", \"target\": \"/lakehouse/hr/employees\"},\n",
        "]\n",
        "\n",
        "role_assignments = [\n",
        "    {\"workspace\": \"Sales-Analytics\", \"principal\": \"DataOps\", \"role\": \"Admin\"},\n",
        "    {\"workspace\": \"Sales-Analytics\", \"principal\": \"Analysts\", \"role\": \"Member\"},\n",
        "    {\"workspace\": \"Finance-Planning\", \"principal\": \"AllFinance\", \"role\": \"Admin\"},\n",
        "    {\"workspace\": \"HR-Insights\", \"principal\": \"HRTeam\", \"role\": \"Viewer\"},\n",
        "    {\"workspace\": \"HR-Insights\", \"principal\": \"PlatformOps\", \"role\": \"Admin\"},\n",
        "]\n",
        "\n",
        "shortcut_df = pd.DataFrame(shortcuts)\n",
        "roles_df = pd.DataFrame(role_assignments)\n",
        "\n",
        "metadata_findings = []\n",
        "for _, row in shortcut_df.iterrows():\n",
        "    issues = []\n",
        "    if not row[\"owner\"]:\n",
        "        issues.append(\"missing_owner\")\n",
        "    if not row[\"label\"]:\n",
        "        issues.append(\"missing_label\")\n",
        "    metadata_findings.append({\n",
        "        \"workspace\": row[\"workspace\"],\n",
        "        \"shortcut\": row[\"shortcut\"],\n",
        "        \"metadata_issue_count\": len(issues),\n",
        "        \"metadata_issues\": \", \".join(issues) if issues else \"none\"\n",
        "    })\n",
        "\n",
        "metadata_df = pd.DataFrame(metadata_findings)\n",
        "role_risk_df = roles_df.copy()\n",
        "role_risk_df[\"role_risk\"] = role_risk_df[\"role\"].map({\"Admin\": 2, \"Member\": 1, \"Viewer\": 0}).fillna(0)\n",
        "\n",
        "workspace_metadata = metadata_df.groupby(\"workspace\", as_index=False).agg({\n",
        "    \"metadata_issue_count\": \"sum\"\n",
        "})\n",
        "workspace_roles = role_risk_df.groupby(\"workspace\", as_index=False).agg({\n",
        "    \"role_risk\": \"sum\"\n",
        "})\n",
        "workspace_shortcuts = shortcut_df.groupby(\"workspace\", as_index=False).agg({\n",
        "    \"shortcut\": \"count\"\n",
        "}).rename(columns={\"shortcut\": \"shortcut_count\"})\n",
        "\n",
        "scorecard = workspace_shortcuts.merge(workspace_metadata, on=\"workspace\", how=\"left\").merge(workspace_roles, on=\"workspace\", how=\"left\")\n",
        "scorecard = scorecard.fillna(0)\n",
        "scorecard[\"total_risk_score\"] = scorecard[\"metadata_issue_count\"] + scorecard[\"role_risk\"]\n",
        "scorecard = scorecard.sort_values([\"total_risk_score\", \"shortcut_count\"], ascending=[False, False])\n",
        "\n",
        "print(\"Workspace governance scorecard:\")\n",
        "print(scorecard.to_string(index=False))\n",
        "\n",
        "print(\"\\nPriority review list:\")\n",
        "for _, row in scorecard.iterrows():\n",
        "    print(f\"- {row['workspace']}: risk_score={int(row['total_risk_score'])}, shortcuts={int(row['shortcut_count'])}\")"
      ],
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## Operating model decision guide\n",
        "\n",
        "Use these rules from the blog to interpret your findings:\n",
        "\n",
        "- **Direct access**: best for high-trust, clearly owned, lower-sensitivity sharing with a small consumer set\n",
        "- **Delegated access**: useful when maturity is improving but source teams cannot handle every request directly\n",
        "- **Centralized governance**: strongest fit for cross-domain, high-sensitivity, or large-scale Fabric estates\n",
        "\n",
        "A shortcut may be the wrong answer when:\n",
        "\n",
        "- trust boundaries are weak\n",
        "- ownership is unclear\n",
        "- review burden stays high over time\n",
        "\n",
        "In those cases, mirroring or controlled duplication may create a cleaner operational boundary."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## Summary\n",
        "\n",
        "This notebook validated the blog's main claim: securing OneLake shortcuts is a design problem, not a permissions cleanup task. The most useful practical step is to review shortcut metadata together with workspace role assignments so you can identify shortcut-bearing workspaces where control-plane authority is broader than intended.\n",
        "\n",
        "## Next Steps\n",
        "\n",
        "1. Replace the mock shortcut inventory with a real Fabric export.\n",
        "2. Replace the sample role assignments with your workspace role inventory.\n",
        "3. Add sensitivity tiers and approval cadence to the intake record.\n",
        "4. Schedule a monthly review for shortcut-bearing workspaces.\n",
        "5. Define decision rules for when to use shortcuts, mirroring, or copied datasets."
      ]
    }
  ]
}