Forum Discussion
Fabric administrator’s perspective
Hi,
I am the only person responsible for managing Microsoft Fabric in my company, including administration, data engineering, workspace management, security, governance, and reporting.
What initial setup steps and preventive measures should I complete from a Fabric administrator’s perspective? For example, I would like to enable item recovery and configure other important settings that protect the environment from accidental deletion, data loss, security issues, and operational failures.
Could you provide a practical checklist covering tenant settings, workspace governance, access control, backup and recovery, monitoring, auditing, capacity management, deployment practices, and business continuity?
Hi reddyr2502 ,
Since you are the sole Fabric administrator, I would prioritize recoverability, least-privilege access, monitoring, and change control before adding more workloads.
A practical initial checklist would be:
1. Protect against accidental deletion
- Enable Item Recovery in the Fabric Admin portal. Microsoft currently allows a retention period of 7–90 days for supported Fabric items.
- Configure workspace retention as well. Collaborative workspaces can also be retained for 7–90 days.
- Document and periodically test the recovery procedure rather than waiting for an actual incident.
- Be aware that item recovery doesn't currently support every Fabric item type.
Microsoft documentation: Retention and recovery in Fabric
2. Lock down tenant settings
Review the Fabric Admin portal → Tenant settings rather than accepting every default.
In particular, review:
- Who can create workspaces
- Who can create Fabric items
- External sharing
- Guest-user access
- Publish to web
- Service-principal/API access
- Git integration
- Export/download capabilities
- Information protection and sensitivity-label settings
Where possible, enable capabilities for specific Microsoft Entra security groups rather than the entire organization.
3. Establish workspace governance
Even in a small organization, define a naming convention such as:
<Domain>-<Purpose>-<Environment> Finance-Analytics-DEV Finance-Analytics-TEST Finance-Analytics-PROD
Avoid using one workspace indefinitely for development and production.
As the environment grows, separate DEV / TEST / PROD so experimental changes don't directly affect production workloads.
4. Apply least privilege
Avoid making everyone a Workspace Admin.
Use Fabric's workspace roles appropriately:
Admin → Platform/workspace administration Member → Content management/sharing Contributor → Development Viewer → Consumption
Prefer Entra groups over assigning permissions individually.
Also avoid using your personal identity as the only owner of critical processes. Where supported and appropriate, use managed identities or service principals for automation.
5. Put important artifacts under source control
Use Fabric Git integration for supported artifacts.
At minimum, version:
- Notebooks
- Pipelines
- SQL/scripts
- Metadata/configuration
- Semantic model definitions where supported
- Environment configuration where supported
Git is particularly important because item recovery and source control solve different problems.
Recovery protects against deletion; Git gives you change history and the ability to return definitions/code to an earlier version.
6. Introduce controlled deployments
As the platform becomes production-critical, move toward:
Developer ↓ DEV Workspace ↓ Git ↓ TEST Workspace ↓ Deployment Pipeline ↓ PROD Workspace
Fabric supports Git integration and deployment pipelines for lifecycle management, although support varies by Fabric item type.
Microsoft guidance: Fabric CI/CD best practices
7. Set up monitoring from day one
Install and regularly review the Microsoft Fabric Capacity Metrics app.
Monitor:
- Capacity Unit (CU) consumption
- Throttling
- Query rejections/delays
- Expensive notebooks
- Expensive pipelines
- Semantic model refreshes
- Peak workload periods
- Storage growth
Also use Monitoring hub/workspace monitoring for operational workloads and the Admin monitoring workspace for tenant-level visibility.
Microsoft recommends the Capacity Metrics app for understanding capacity utilization and identifying high-consuming workloads.
8. Build operational logging into pipelines
Platform monitoring alone isn't enough.
For important ingestion processes, maintain operational information such as:
PipelineRunId SourceSystem ObjectName StartTime EndTime Status RowsRead RowsWritten WatermarkFrom WatermarkTo ErrorMessage
This gives you application-level traceability in addition to Fabric's platform monitoring.
9. Protect credentials and connections
Don't store passwords, tokens, SAS tokens, or secrets directly in notebooks or configuration files.
Use supported secure authentication mechanisms and centrally managed connections/credentials. Review who can create, modify and use connections.
10. Plan for business continuity
Don't treat Fabric item recovery as a complete backup/DR strategy.
Document:
- Critical workspaces
- Critical data and artifacts
- Data sources from which Bronze can be rebuilt
- Which artifacts are protected by Git
- Recovery dependencies
- Recovery order
- RPO/RTO expectations
- Who can administer Fabric if you are unavailable
This last point is especially important when there is only one Fabric administrator. There should be at least one controlled break-glass/secondary administrative path so the organization isn't dependent on one person's account.
My priority order would therefore be:
Recovery → Security → Monitoring → Source control → DEV/TEST/PROD → Deployment controls → Business continuity
For a small Fabric environment, I would start with these controls rather than implementing a large enterprise governance framework immediately. The goal is to make sure that a deleted item, compromised account, bad deployment, failed pipeline, or overloaded capacity is something you can detect, investigate and recover from.
If this post helps, then please appreciate giving a Kudos or accepting as a Solution to help the other members find it more quickly.
If I misunderstand your needs or you still have problems on it, please feel free to let me know. Thanks a lot!
4 Replies
- gslickFrequent Visitor
I would enrol in this course even if you don't want to get certified, it will be able to answer most of your questions: Microsoft Certified: Fabric Data Engineer Associate - Certifications | Microsoft Learn
- ssritharSuper User
Hi reddyr2502 ,
Since you are the sole Fabric administrator, I would prioritize recoverability, least-privilege access, monitoring, and change control before adding more workloads.
A practical initial checklist would be:
1. Protect against accidental deletion
- Enable Item Recovery in the Fabric Admin portal. Microsoft currently allows a retention period of 7–90 days for supported Fabric items.
- Configure workspace retention as well. Collaborative workspaces can also be retained for 7–90 days.
- Document and periodically test the recovery procedure rather than waiting for an actual incident.
- Be aware that item recovery doesn't currently support every Fabric item type.
Microsoft documentation: Retention and recovery in Fabric
2. Lock down tenant settings
Review the Fabric Admin portal → Tenant settings rather than accepting every default.
In particular, review:
- Who can create workspaces
- Who can create Fabric items
- External sharing
- Guest-user access
- Publish to web
- Service-principal/API access
- Git integration
- Export/download capabilities
- Information protection and sensitivity-label settings
Where possible, enable capabilities for specific Microsoft Entra security groups rather than the entire organization.
3. Establish workspace governance
Even in a small organization, define a naming convention such as:
<Domain>-<Purpose>-<Environment> Finance-Analytics-DEV Finance-Analytics-TEST Finance-Analytics-PROD
Avoid using one workspace indefinitely for development and production.
As the environment grows, separate DEV / TEST / PROD so experimental changes don't directly affect production workloads.
4. Apply least privilege
Avoid making everyone a Workspace Admin.
Use Fabric's workspace roles appropriately:
Admin → Platform/workspace administration Member → Content management/sharing Contributor → Development Viewer → Consumption
Prefer Entra groups over assigning permissions individually.
Also avoid using your personal identity as the only owner of critical processes. Where supported and appropriate, use managed identities or service principals for automation.
5. Put important artifacts under source control
Use Fabric Git integration for supported artifacts.
At minimum, version:
- Notebooks
- Pipelines
- SQL/scripts
- Metadata/configuration
- Semantic model definitions where supported
- Environment configuration where supported
Git is particularly important because item recovery and source control solve different problems.
Recovery protects against deletion; Git gives you change history and the ability to return definitions/code to an earlier version.
6. Introduce controlled deployments
As the platform becomes production-critical, move toward:
Developer ↓ DEV Workspace ↓ Git ↓ TEST Workspace ↓ Deployment Pipeline ↓ PROD Workspace
Fabric supports Git integration and deployment pipelines for lifecycle management, although support varies by Fabric item type.
Microsoft guidance: Fabric CI/CD best practices
7. Set up monitoring from day one
Install and regularly review the Microsoft Fabric Capacity Metrics app.
Monitor:
- Capacity Unit (CU) consumption
- Throttling
- Query rejections/delays
- Expensive notebooks
- Expensive pipelines
- Semantic model refreshes
- Peak workload periods
- Storage growth
Also use Monitoring hub/workspace monitoring for operational workloads and the Admin monitoring workspace for tenant-level visibility.
Microsoft recommends the Capacity Metrics app for understanding capacity utilization and identifying high-consuming workloads.
8. Build operational logging into pipelines
Platform monitoring alone isn't enough.
For important ingestion processes, maintain operational information such as:
PipelineRunId SourceSystem ObjectName StartTime EndTime Status RowsRead RowsWritten WatermarkFrom WatermarkTo ErrorMessage
This gives you application-level traceability in addition to Fabric's platform monitoring.
9. Protect credentials and connections
Don't store passwords, tokens, SAS tokens, or secrets directly in notebooks or configuration files.
Use supported secure authentication mechanisms and centrally managed connections/credentials. Review who can create, modify and use connections.
10. Plan for business continuity
Don't treat Fabric item recovery as a complete backup/DR strategy.
Document:
- Critical workspaces
- Critical data and artifacts
- Data sources from which Bronze can be rebuilt
- Which artifacts are protected by Git
- Recovery dependencies
- Recovery order
- RPO/RTO expectations
- Who can administer Fabric if you are unavailable
This last point is especially important when there is only one Fabric administrator. There should be at least one controlled break-glass/secondary administrative path so the organization isn't dependent on one person's account.
My priority order would therefore be:
Recovery → Security → Monitoring → Source control → DEV/TEST/PROD → Deployment controls → Business continuity
For a small Fabric environment, I would start with these controls rather than implementing a large enterprise governance framework immediately. The goal is to make sure that a deleted item, compromised account, bad deployment, failed pipeline, or overloaded capacity is something you can detect, investigate and recover from.
If this post helps, then please appreciate giving a Kudos or accepting as a Solution to help the other members find it more quickly.
If I misunderstand your needs or you still have problems on it, please feel free to let me know. Thanks a lot!
- AnmoldeepNew Member
hi,
You can go through this training on Microsoft Learn ,by the end of it you will be able to understand and implement the tenant settings, workspace governance, access control, backup and recovery, monitoring, auditing, capacity management, deployment practices, and business continuity.
Manage a Microsoft Fabric Environment - Training | Microsoft Learn
- tayloramySuper User
Hi reddyr2502,
The very first thing I would recommend is setting up a process to collect audit logs. The API only stores them for 28 days, so if you want a full audit history of who did what, you need to start capturing them and storing them.
Here is the PySpark script that I use for this. I schedule it daily but it is designed to catch up if up to 14 days are missed for whatever reason.
import requests import notebookutils import json from datetime import datetime, timedelta, timezone import pyspark.sql.functions as F from pyspark.sql.types import StructType, StructField, StringType, TimestampType from delta.tables import DeltaTable # --------------------------------------------------------------------------- # Config # --------------------------------------------------------------------------- API_URL = "https://api.powerbi.com/v1.0/myorg/admin/activityevents" AUDITLOG_TABLE = "LH_Fabric_Meta.bronze.auditlog" VIEW_REPORT_TABLE = "LH_Fabric_Meta.bronze.auditlog_view_report" # How far back to scan for gaps. Must not exceed the API retention window. API_MAX_DAYS_BACK = 28 # How many calendar days to check for gaps. Keep <= API_MAX_DAYS_BACK. GAP_LOOKBACK_DAYS = 14 VIEW_REPORT_JSON_SCHEMA = StructType([ StructField("UserId", StringType(), True), StructField("ItemName", StringType(), True), StructField("DatasetName", StringType(), True), StructField("ReportName", StringType(), True), StructField("WorkSpaceName", StringType(), True), StructField("ReportId", StringType(), True), ]) # --------------------------------------------------------------------------- # API helpers # --------------------------------------------------------------------------- def _get_token(): return notebookutils.credentials.getToken("pbi") def _day_window_iso(date: datetime.date): """Return (start_iso, end_iso) for a full UTC day given a date object.""" start = datetime(date.year, date.month, date.day, 0, 0, 0, tzinfo=timezone.utc) end = datetime(date.year, date.month, date.day, 23, 59, 59, tzinfo=timezone.utc) # Power BI API expects millisecond precision without offset suffix return ( start.strftime("%Y-%m-%dT%H:%M:%S.000"), end.strftime( "%Y-%m-%dT%H:%M:%S.999"), ) def query_powerbi_audit(token: str, start_iso: str, end_iso: str): """ Fetch all activity events for the given UTC window. Handles continuation URI pagination automatically. Raises on HTTP errors (will surface as notebook failure). """ headers = { "Authorization": f"Bearer {token}", "Content-Type": "application/json", } params = { "startDateTime": f"'{start_iso}'", "endDateTime": f"'{end_iso}'", } all_events = [] current_url = API_URL while True: response = requests.get(current_url, headers=headers, params=params, timeout=60) response.raise_for_status() data = response.json() all_events.extend(data.get("activityEventEntities", [])) if data.get("lastResultSet"): break current_url = data.get("continuationUri") if not current_url: break params = {} # continuation URI carries its own query string return all_events # --------------------------------------------------------------------------- # Gap detection # --------------------------------------------------------------------------- def get_present_dates(table_name: str, lookback_days: int) -> set: """ Return the set of UTC dates (as date objects) that already have rows in the audit log within the lookback window. Returns an empty set if the table does not exist. """ if not spark.catalog.tableExists(table_name): return set() cutoff = datetime.now(timezone.utc).date() - timedelta(days=lookback_days) rows = ( spark.table(table_name) .filter(F.col("CreationTime") >= F.lit(cutoff.isoformat())) .select(F.to_date("CreationTime").alias("log_date")) .distinct() .collect() ) return {row["log_date"] for row in rows} def resolve_dates_to_load(lookback_days: int, api_max_days_back: int) -> list: """ Determine the sorted list of dates that need to be fetched: 1. Always include yesterday (UTC). 2. Scan the last `lookback_days` for dates missing from the audit log. 3. Exclude dates older than `api_max_days_back` (outside API retention). Returns a sorted list of date objects (oldest first). """ today = datetime.now(timezone.utc).date() yesterday = today - timedelta(days=1) # Oldest date the API can still return api_horizon = today - timedelta(days=api_max_days_back) # Full calendar of expected dates within lookback window (excluding today — # today's events are still accumulating so we never try to load them) expected_dates = { today - timedelta(days=d) for d in range(1, lookback_days + 1) if (today - timedelta(days=d)) >= api_horizon } present_dates = get_present_dates(AUDITLOG_TABLE, lookback_days) gap_dates = expected_dates - present_dates # Union with yesterday (always reload to catch late-arriving events) dates_to_load = gap_dates | {yesterday} if not dates_to_load: return [] return sorted(dates_to_load) # oldest → newest # --------------------------------------------------------------------------- # Write helpers # --------------------------------------------------------------------------- def _auditlog_schema(): return StructType([ StructField("Id", StringType(), True), StructField("CreationTime", StringType(), True), StructField("Operation", StringType(), True), StructField("FullRecord", StringType(), True), ]) def _build_auditlog_df(events: list): rows = [ ( event.get("Id"), event.get("CreationTime"), event.get("Operation"), json.dumps(event), ) for event in events ] df = spark.createDataFrame(rows, schema=_auditlog_schema()) df = df.withColumn("CreationTime", F.to_timestamp("CreationTime")) return df def merge_into(df, table_name: str, merge_key: str = "Id"): """ Idempotent merge by merge_key. Creates the table on first load, then merges on subsequent runs. """ if not spark.catalog.tableExists(table_name): df.write.format("delta").mode("overwrite").saveAsTable(table_name) print(f" [INIT] {df.count()} rows written to {table_name}") return delta_table = DeltaTable.forName(spark, table_name) ( delta_table.alias("target") .merge(df.alias("source"), f"target.{merge_key} = source.{merge_key}") .whenMatchedUpdateAll() .whenNotMatchedInsertAll() .execute() ) print(f" [MERGE] {df.count()} source rows merged into {table_name}") # --------------------------------------------------------------------------- # View report silver transform # --------------------------------------------------------------------------- def _transform_view_report(auditlog_df): filtered = auditlog_df.filter(F.col("Operation") == "ViewReport") parsed = filtered.withColumn( "FullRecord_parsed", F.from_json(F.col("FullRecord"), VIEW_REPORT_JSON_SCHEMA), ) return parsed.select( F.col("Id"), F.col("CreationTime"), F.col("FullRecord_parsed.UserId").alias("UserId"), F.col("FullRecord_parsed.DatasetName").alias("DatasetName"), F.col("FullRecord_parsed.ReportName").alias("ReportName"), F.col("FullRecord_parsed.WorkSpaceName").alias("WorkSpaceName"), F.col("FullRecord_parsed.ReportId").alias("ReportId"), ) # --------------------------------------------------------------------------- # Per-date fetch + write # --------------------------------------------------------------------------- def process_date(token: str, target_date) -> dict: """ Fetch all audit events for `target_date` (a date object, UTC), merge them into the auditlog bronze table, and propagate ViewReport events to the view_report table. Returns a summary dict for reporting. """ start_iso, end_iso = _day_window_iso(target_date) print(f"\n>>> Processing {target_date} ({start_iso} → {end_iso})") events = query_powerbi_audit(token, start_iso, end_iso) if not events: print(f" No events returned for {target_date} — skipping write") return {"date": target_date, "events": 0, "status": "empty"} # --- Auditlog bronze --- auditlog_df = _build_auditlog_df(events) merge_into(auditlog_df, AUDITLOG_TABLE, merge_key="Id") # --- ViewReport silver --- vr_df = _transform_view_report(auditlog_df) if not vr_df.isEmpty(): merge_into(vr_df, VIEW_REPORT_TABLE, merge_key="Id") else: print(f" No ViewReport events for {target_date}") return {"date": target_date, "events": len(events), "status": "ok"} # --------------------------------------------------------------------------- # Main # --------------------------------------------------------------------------- def main(): token = _get_token() print("=" * 60) print("Power BI Audit Log — Bronze Ingestion with Gap Detection") print(f"Lookback window : {GAP_LOOKBACK_DAYS} days") print(f"API horizon : {API_MAX_DAYS_BACK} days") print("=" * 60) dates_to_load = resolve_dates_to_load(GAP_LOOKBACK_DAYS, API_MAX_DAYS_BACK) if not dates_to_load: print("All dates within lookback window are present. Nothing to load.") return present_dates = get_present_dates(AUDITLOG_TABLE, GAP_LOOKBACK_DAYS) today = datetime.now(timezone.utc).date() yesterday = today - timedelta(days=1) print(f"\nDates to load ({len(dates_to_load)} total):") for d in dates_to_load: reason = [] if d == yesterday: reason.append("yesterday") if d not in present_dates: reason.append("gap") print(f" {d} [{', '.join(reason)}]") results = [] for target_date in dates_to_load: try: result = process_date(token, target_date) results.append(result) except Exception as e: print(f" [ERROR] Failed to process {target_date}: {e}") results.append({"date": target_date, "events": 0, "status": f"error: {e}"}) # --- Summary --- print("\n" + "=" * 60) print("Run Summary") print("=" * 60) for r in results: print(f" {r['date']} | {r['events']:>6} events | {r['status']}") errors = [r for r in results if r["status"].startswith("error")] if errors: raise RuntimeError( f"{len(errors)} date(s) failed to load: " + ", ".join(str(r['date']) for r in errors) ) main()