Back to Blog
5 min read

Understanding Microsoft Fabric Capacities and SKUs

Understanding Fabric capacities is crucial for planning and cost optimization. Today, I will break down how Fabric capacity works, the available SKUs, and how to choose the right size for your workloads.

What is a Fabric Capacity?

A Fabric Capacity is a pool of compute resources that powers all Fabric workloads in assigned workspaces. Unlike traditional Azure services where you provision separate resources, Fabric uses a unified capacity model.

┌─────────────────────────────────────────────────────┐
│                 Fabric Capacity                      │
│                   (F64 SKU)                         │
├─────────────────────────────────────────────────────┤
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐ │
│  │ Workspace A │  │ Workspace B │  │ Workspace C │ │
│  ├─────────────┤  ├─────────────┤  ├─────────────┤ │
│  │ Lakehouse   │  │ Warehouse   │  │ Power BI    │ │
│  │ Notebooks   │  │ Pipelines   │  │ Reports     │ │
│  │ Dataflows   │  │ KQL DB      │  │ Dataflows   │ │
│  └─────────────┘  └─────────────┘  └─────────────┘ │
├─────────────────────────────────────────────────────┤
│              Shared Compute Pool                     │
│     (64 Capacity Units - Burst & Smoothing)        │
└─────────────────────────────────────────────────────┘

Fabric SKUs

Fabric offers various capacity SKUs measured in Capacity Units (CUs):

SKUCapacity UnitsPower BI EquivalentUse Case
F22 CU-Trial, Testing
F44 CU-Small workloads
F88 CU-Small teams
F1616 CU-Department
F3232 CU-Department
F6464 CUP1Enterprise
F128128 CUP2Enterprise
F256256 CUP3Large Enterprise
F512512 CUP4Large Enterprise
F10241024 CUP5Very Large Enterprise
F20482048 CU-Massive Scale
# Capacity Unit mapping to workload operations
capacity_mapping = {
    "spark_job": "CU-seconds based on cores used",
    "sql_query": "CU-seconds based on query complexity",
    "dataflow": "CU-hours based on data volume",
    "power_bi_refresh": "CU-seconds based on model size",
    "kql_query": "CU-seconds based on data scanned"
}

Provisioning Capacity

Azure Portal

# Create Fabric capacity via Azure CLI
az resource create \
    --resource-type "Microsoft.Fabric/capacities" \
    --name "my-fabric-capacity" \
    --resource-group "my-resource-group" \
    --location "eastus" \
    --properties '{
        "sku": {"name": "F64", "tier": "Fabric"},
        "administration": {
            "members": ["admin@contoso.com"]
        }
    }'

Terraform

# Terraform configuration for Fabric capacity
resource "azurerm_fabric_capacity" "main" {
  name                = "my-fabric-capacity"
  resource_group_name = azurerm_resource_group.main.name
  location            = azurerm_resource_group.main.location

  sku {
    name = "F64"
    tier = "Fabric"
  }

  administration {
    members = ["admin@contoso.com"]
  }

  tags = {
    environment = "production"
    department  = "analytics"
  }
}

Capacity Management

Pause and Resume

# Pause capacity during off-hours to save costs
import requests
from azure.identity import DefaultAzureCredential

credential = DefaultAzureCredential()
token = credential.get_token("https://management.azure.com/.default")

subscription_id = "your-subscription-id"
resource_group = "your-resource-group"
capacity_name = "your-capacity-name"

# Suspend capacity
suspend_url = f"https://management.azure.com/subscriptions/{subscription_id}/resourceGroups/{resource_group}/providers/Microsoft.Fabric/capacities/{capacity_name}/suspend?api-version=2022-07-01-preview"

response = requests.post(
    suspend_url,
    headers={"Authorization": f"Bearer {token.token}"}
)
print(f"Suspend status: {response.status_code}")

# Resume capacity
resume_url = f"https://management.azure.com/subscriptions/{subscription_id}/resourceGroups/{resource_group}/providers/Microsoft.Fabric/capacities/{capacity_name}/resume?api-version=2022-07-01-preview"

response = requests.post(
    resume_url,
    headers={"Authorization": f"Bearer {token.token}"}
)
print(f"Resume status: {response.status_code}")

Auto-Pause Schedule

# Azure Function to auto-pause capacity
import azure.functions as func
from azure.mgmt.fabric import FabricManagementClient
from azure.identity import DefaultAzureCredential

def main(timer: func.TimerRequest) -> None:
    credential = DefaultAzureCredential()
    client = FabricManagementClient(credential, subscription_id)

    # Pause at 8 PM
    if timer.schedule_status.last_occurrence.hour == 20:
        client.capacities.begin_suspend(resource_group, capacity_name)

    # Resume at 6 AM
    if timer.schedule_status.last_occurrence.hour == 6:
        client.capacities.begin_resume(resource_group, capacity_name)

Capacity Metrics

Monitor capacity usage through the Fabric admin portal or Azure metrics:

# Key metrics to monitor
metrics = {
    "capacity_cu_consumption": "Total CU consumption",
    "capacity_cu_percentage": "Percentage of capacity used",
    "active_users": "Number of concurrent users",
    "query_duration_seconds": "Average query execution time",
    "throttling_events": "Number of throttling occurrences"
}

# Query metrics via Azure Monitor
from azure.monitor.query import MetricsQueryClient
from datetime import timedelta

metrics_client = MetricsQueryClient(credential)

response = metrics_client.query_resource(
    resource_uri=f"/subscriptions/{subscription_id}/resourceGroups/{resource_group}/providers/Microsoft.Fabric/capacities/{capacity_name}",
    metric_names=["CUConsumption"],
    timespan=timedelta(days=1),
    granularity=timedelta(hours=1)
)

for metric in response.metrics:
    for ts in metric.timeseries:
        for data in ts.data:
            print(f"{data.timestamp}: {data.average} CU")

Capacity Optimization

Smoothing and Bursting

# Fabric uses smoothing to handle burst workloads
# Operations are measured in CU-seconds
# Consumption is smoothed over time windows

# Example: A Spark job uses 128 CUs for 30 seconds
# Total consumption: 128 * 30 = 3,840 CU-seconds
# Smoothed over 5 minutes: 3,840 / 300 = 12.8 CU average

# If your capacity is F64 (64 CU):
# - Short bursts above 64 CU are allowed
# - Sustained load above 64 CU will cause throttling

Right-Sizing Recommendations

# Capacity sizing guidelines
def recommend_sku(daily_cu_hours, peak_cu, concurrent_users):
    """
    Recommend Fabric SKU based on usage patterns
    """
    recommendations = []

    # Based on sustained usage
    sustained_sku = daily_cu_hours / 24  # Average hourly CU

    # Based on peak with burst headroom (2x)
    peak_sku = peak_cu / 2

    # Based on concurrent users (rough estimate)
    user_sku = concurrent_users * 0.5  # ~0.5 CU per active user

    required_cu = max(sustained_sku, peak_sku, user_sku)

    # Map to available SKUs
    skus = [2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048]
    recommended_sku = min(s for s in skus if s >= required_cu)

    return f"F{recommended_sku}"

# Example
sku = recommend_sku(
    daily_cu_hours=800,      # 800 CU-hours per day
    peak_cu=100,             # Peak of 100 CU
    concurrent_users=50      # 50 concurrent users
)
print(f"Recommended SKU: {sku}")  # Output: F64

Cost Optimization Strategies

# 1. Use appropriate SKU for workload
# Don't over-provision; monitor and adjust

# 2. Pause during off-hours
# Savings: Up to 50% if paused 12 hours/day

# 3. Use workspace load balancing
# Spread workloads across time if possible

# 4. Optimize queries and jobs
# Efficient code = less CU consumption

# 5. Consider reserved capacity
# 1-year commitment can save ~40%

cost_comparison = {
    "pay_as_you_go_f64": 4995.84,  # Monthly USD
    "reserved_1yr_f64": 2997.50,   # Monthly USD (40% savings)
}

Tomorrow, I will cover Fabric licensing for users and organizations.

Resources

Michael John Peña

Michael John Peña

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