When we use paid Fabric Capacity, we might need to auto pause and resume the capacity for cost saving. We can use Azure Automation Runbook to achieve this.
We setp an Automation Account with a Managed Idenity.
Step 1: Set Up Permissions
- Go to your Azure Automation Account.
- Under Account Settings, click Identity and ensure the “System assigned” toggle is On.
- Go to your Fabric Capacity resource in the Azure Portal.
- Select Access Control (IAM) -> Add role assignment.
- Assign the Contributor role to the Managed Identity of your Automation Account.
Step 2: Create the PowerShell Runbook
In your Automation Account, go to Runbooks -> Create a runbook using following PowerShell script.
param (
[Parameter (Mandatory = $true)] [String] $SubscriptionId,
[Parameter (Mandatory = $true)] [String] $ResourceGroupName,
[Parameter (Mandatory = $true)] [String] $CapacityName
)
# 1. STOP if parameters didn't flow in
if ([string]::IsNullOrEmpty($SubscriptionId) -or $SubscriptionId -eq "`$SubscriptionId") {
Write-Error "CRITICAL: Parameters were not passed to the runbook. Check your Schedule or Test Pane inputs."
return
}
# 2. Authenticate
Connect-AzAccount -Identity
# 3. Build the URL carefully
$baseUrl = "https://management.azure.com/subscriptions/$SubscriptionId/resourceGroups/$ResourceGroupName/providers/Microsoft.Fabric/capacities/$CapacityName"
# 3.1. Ask Azure for the available versions of Fabric Capacities
$fabricProvider = Get-AzResourceProvider -ProviderNamespace "Microsoft.Fabric"
$resourceType = $fabricProvider.ResourceTypes | Where-Object { $_.ResourceTypeName -eq "capacities" }
# 3.2. Extract the newest version that IS NOT a preview
$LatestStableVersion = $resourceType.ApiVersions | Where-Object { $_ -notmatch "preview" } | Select-Object -First 1
# 3.3. Fallback: If for some reason we can't find one, use a safe default
if ([string]::IsNullOrEmpty($LatestStableVersion)) {
Write-Output "Fail to detect newest version, fallback to default version."
$LatestStableVersion = "2023-11-01"
}
Write-Output "Detected Latest Stable API Version: $LatestStableVersion"
# 4. Build the URLs
# we use the Sub-expression operator $() to forces evaluation of the variable first, to handle ? after $baseUrl properly
$statusUrl = "$($baseUrl)?api-version=$LatestStableVersion"
$suspendUrl = "$baseUrl/suspend?api-version=$LatestStableVersion"
# 4.1. Debug: This will show you exactly what the script "sees"
#Write-Output "DEBUG: Attempting to connect to: $statusUrl"
#Write-Output "DEBUG: Attempting to connect to: $suspendUrl"
# 5. Make the API call
try {
$token = (Get-AzAccessToken -ResourceUrl "https://management.azure.com").Token
$headers = @{
"Authorization" = "Bearer $token"
"Content-Type" = "application/json"
}
# Sometimes, commands like Invoke-RestMethod don't "trap" into the catch block unless they encounter a "Terminating Error." To be 100% safe, add -ErrorAction Stop to API calls
$details = Invoke-RestMethod -Method Get -Uri $statusUrl -Headers $headers -ErrorAction Stop
$currentState = $details.properties.state
if ($currentState -eq "Active") {
Write-Output "Capacity is Active. Sending suspend command..."
Invoke-RestMethod -Method Post -Uri $suspendUrl -Headers $headers
} else {
Write-Output "Capacity is already $currentState. Skipping suspend."
}
}
catch {
Write-Error "Detailed Error: $_"
throw "Runbook failed to execute the command. Check the logs for details."
}
Step 3: Link the Runbook with a schedule
Configure a schedule for your runbook.
Auto resume
If you need a runbook to auto resume the capacity:
- replace
suspendwithresumein following line in# 4. Build the URLssection
$suspendUrl = "$baseUrl/resume?api-version=$LatestStableVersion"
- Update the logic to check if the state is Paused (or Suspended) before trying to start it, replace
-eqwith-ne.
if ($currentState -ne "Active")
The full script:
param (
[Parameter (Mandatory = $true)] [String] $SubscriptionId,
[Parameter (Mandatory = $true)] [String] $ResourceGroupName,
[Parameter (Mandatory = $true)] [String] $CapacityName
)
# 1. STOP if parameters didn't flow in
if ([string]::IsNullOrEmpty($SubscriptionId) -or $SubscriptionId -eq "`$SubscriptionId") {
Write-Error "CRITICAL: Parameters were not passed to the runbook. Check your Schedule or Test Pane inputs."
return
}
# 2. Authenticate
Connect-AzAccount -Identity
# 3. Build the URL carefully
$baseUrl = "https://management.azure.com/subscriptions/$SubscriptionId/resourceGroups/$ResourceGroupName/providers/Microsoft.Fabric/capacities/$CapacityName"
# 3.1. Ask Azure for the available versions of Fabric Capacities
$fabricProvider = Get-AzResourceProvider -ProviderNamespace "Microsoft.Fabric"
$resourceType = $fabricProvider.ResourceTypes | Where-Object { $_.ResourceTypeName -eq "capacities" }
# 3.2. Extract the newest version that IS NOT a preview
$LatestStableVersion = $resourceType.ApiVersions | Where-Object { $_ -notmatch "preview" } | Select-Object -First 1
# 3.3. Fallback: If for some reason we can't find one, use a safe default
if ([string]::IsNullOrEmpty($LatestStableVersion)) {
Write-Output "Fail to detect newest version, fallback to default version."
$LatestStableVersion = "2023-11-01"
}
Write-Output "Detected Latest Stable API Version: $LatestStableVersion"
# 4. Build the URLs
# we use the Sub-expression operator $() to forces evaluation of the variable first, to handle ? after $baseUrl properly
$statusUrl = "$($baseUrl)?api-version=$LatestStableVersion"
$resumeUrl = "$baseUrl/resume?api-version=$LatestStableVersion"
# 4.1. Debug: This will show you exactly what the script "sees"
#Write-Output "DEBUG: Attempting to connect to: $statusUrl"
#Write-Output "DEBUG: Attempting to connect to: $resumeUrl"
# 5. Make the API call
try {
$token = (Get-AzAccessToken -ResourceUrl "https://management.azure.com").Token
$headers = @{
"Authorization" = "Bearer $token"
"Content-Type" = "application/json"
}
# Sometimes, commands like Invoke-RestMethod don't "trap" into the catch block unless they encounter a "Terminating Error." To be 100% safe, add -ErrorAction Stop to API calls
$details = Invoke-RestMethod -Method Get -Uri $statusUrl -Headers $headers -ErrorAction Stop
$currentState = $details.properties.state
if ($currentState -ne "Active") {
Write-Output "Capacity is not Active. Sending resume command..."
Invoke-RestMethod -Method Post -Uri $resumeUrl -Headers $headers
} else {
Write-Output "Capacity is already $currentState. Skipping resume."
}
}
catch {
Write-Error "Detailed Error: $_"
throw "Runbook failed to execute the command. Check the logs for details."
}
Monitoring
It’s good practice to setup an Azure Monitoring Alert on the automation account to send notification (email or Azure Mobile App) when the automation runbook failed.