# Example how to query resource (agents and workflows) Copilot credit consumption for a given time period.  Allows for reporting at the user level or total the values per resource.

> Source: <https://gist.github.com/joerodgers/335c428de741451e2f1eecbbb49f58cb>
> Published: 2026-08-24 16:52:51+00:00

| #requries -modules "Az.Accounts" | |
| function Get-AccessToken | |
| { | |
| [CmdletBinding()] | |
| param | |
| ( | |
| [Parameter(Mandatory=$true)] | |
| [string] | |
| $ResourceUrl | |
| ) | |
| if (-not (Get-AzContext)) | |
| { | |
| Write-Host "No Azure context - launching sign-in..." -ForegroundColor Yellow | |
| Connect-AzAccount -WarningAction SilentlyContinue | Out-Null | |
| } | |
| $azAccessToken = Get-AzAccessToken -ResourceUrl $ResourceUrl -WarningAction SilentlyContinue | |
| if ($azAccessToken.Token -is [System.Security.SecureString]) | |
| { | |
| return [System.Net.NetworkCredential]::new('', $azAccessToken.Token).Password | |
| } | |
| return $azAccessToken.Token | |
| } | |
| function Get-PowerPlatformResource | |
| { | |
| [CmdletBinding()] | |
| param | |
| ( | |
| [Parameter(Mandatory=$false)] | |
| [switch] | |
| $IncludeAgentResources, | |
| [Parameter(Mandatory=$false)] | |
| [switch] | |
| $IncludeWorkflowResources | |
| ) | |
| begin | |
| { | |
| $accessToken = Get-AccessToken -ResourceUrl "https://api.powerplatform.com/" | |
| $headers = @{ "Authorization" = "Bearer $accessToken"; "Content-Type" = "application/json"; "Accept" = "application/json" } | |
| $skipToken = "" | |
| $skip = 0 | |
| $top = 1000 | |
| $resourceQueryTemplate = @' | |
| {{ | |
| "Options": {{ | |
| "Top": {0}, | |
| "Skip": {1}, | |
| "SkipToken": "{2}" | |
| }}, | |
| "TableName": "PowerPlatformResources", | |
| "Clauses": [ | |
| {{ | |
| "$type": "where", | |
| "FieldName": "type", | |
| "Operator": "in~", | |
| "Values": [ {3} ] | |
| }}, | |
| {{ | |
| "$type": "extend", | |
| "FieldName": "joinKey", | |
| "Expression": "tolower(tostring(properties.environmentId))" | |
| }}, | |
| {{ | |
| "$type": "project", | |
| "FieldList": [ | |
| "joinKey", | |
| "resourceId = tolower(name)", | |
| "type", | |
| "resourceName = properties.displayName", | |
| "resourceType = iif( type == 'microsoft.powerautomate/agentflows', 'Agent Workflow', iif( properties.isGithubCopilotAgent == 1, 'GitHub Copilot Harness Agent',iif(properties.model == 'Microsoft 365 Copilot','M365 Copilot Chat Harness Agent','Standard Harness Agent')))", | |
| "properties" | |
| ] | |
| }}, | |
| {{ | |
| "$type": "join", | |
| "JoinKind": "leftouter", | |
| "RightTable": {{ | |
| "TableName": "PowerPlatformResources", | |
| "Clauses": [ | |
| {{ | |
| "$type": "where", | |
| "FieldName": "type", | |
| "Operator": "==", | |
| "Values": [ | |
| "'microsoft.powerplatform/environments'" | |
| ] | |
| }}, | |
| {{ | |
| "$type": "project", | |
| "FieldList": [ | |
| "joinKey = tolower(name)", | |
| "environmentId = tolower(name)", | |
| "environmentName = properties.displayName", | |
| "environmentRegion = location", | |
| "environmentType = properties.environmentType", | |
| "isManagedEnvironment = properties.isManaged", | |
| "environmentGroupId = tolower(properties.environmentGroupId)" | |
| ] | |
| }} | |
| ] | |
| }}, | |
| "LeftColumnName": "joinKey", | |
| "RightColumnName": "joinKey" | |
| }}, | |
| {{ | |
| "$type": "join", | |
| "JoinKind": "leftouter", | |
| "RightTable": {{ | |
| "TableName": "PowerPlatformResources", | |
| "Clauses": [ | |
| {{ | |
| "$type": "where", | |
| "FieldName": "type", | |
| "Operator": "==", | |
| "Values": [ | |
| "'microsoft.powerplatform/environmentgroups'" | |
| ] | |
| }}, | |
| {{ | |
| "$type": "project", | |
| "FieldList": [ | |
| "environmentGroupId = tolower(name)", | |
| "environmentGroupName = properties.displayName" | |
| ] | |
| }} | |
| ] | |
| }}, | |
| "LeftColumnName": "environmentGroupId", | |
| "RightColumnName": "environmentGroupId" | |
| }} | |
| ] | |
| }} | |
| '@ | |
| } | |
| process | |
| { | |
| $uri = "https://api.powerplatform.com/resourcequery/resources/query?api-version=2022-03-01-preview" | |
| $resourceTypes = @() | |
| if( $IncludeAgentResources.IsPresent ) | |
| { | |
| $resourceTypes += "`"'microsoft.copilotstudio/agents'`"" | |
| } | |
| if( $IncludeWorkflowResources.IsPresent ) | |
| { | |
| $resourceTypes += "`"'microsoft.powerautomate/agentflows'`"" | |
| } | |
| $page = 0 | |
| $resources = do | |
| { | |
| $page++ | |
| Write-Verbose "[$(Get-Date)] - Querying Power Platform resources, page $page." | |
| $body = $resourceQueryTemplate -f $top, $skip, $skipToken, ($resourceTypes -join ",") | |
| $response = Invoke-RestMethod -Method Post -Uri $uri -Body $body -Headers $headers -Verbose:$false | |
| $skipToken = $response.skipToken | |
| $skip += $top | |
| $response.data | |
| } | |
| while( $skipToken ) | |
| $resources = foreach( $resource in $resources ) | |
| { | |
| [PSCustomObject] @{ | |
| ResourceType = $resource.resourceType | |
| ResourceName = $resource.resourceName | |
| ResourceId = $resource.resourceId | |
| EnvironmentId = $resource.environmentId | |
| EnvironmentName = $resource.environmentName | |
| EnvironmentRegion = $resource.environmentRegion | |
| EnvironmentType = $resource.environmentType | |
| EnvironmentIsManaged = $resource.isManagedEnvironment | |
| EnvironmentGroupId = $resource.environmentGroupId | |
| EnvironmentGroupName = $resource.environmentGroupName | |
| ResourceCreatedAt = $resource.properties.createdAt | |
| ResourceCreatedBy = $resource.properties.createdBy | |
| ResourceOwnerId = $resource.properties.ownerId | |
| ResourceLastPublishedAt = $resource.properties.lastPublishedAt | |
| } | |
| } | |
| if( $IncludeAgentResources.IsPresent ) | |
| { | |
| $rows = @($resources | where-object -Property ResourceType -eq "Agent Workflow") | |
| Write-Verbose "[$(Get-Date)] - Discovered $($rows.Count) Power Platform Agent resources." | |
| } | |
| if( $IncludeWorkflowResources.IsPresent ) | |
| { | |
| $rows = @($resources | where-object -Property ResourceType -ne "Agent Workflow") | |
| Write-Verbose "[$(Get-Date)] - Discovered $($rows.Count) Power Platform Workflow resources." | |
| } | |
| $resources | |
| } | |
| end | |
| { | |
| } | |
| } | |
| function Get-PowerPlatformResourceMessageConsumption | |
| { | |
| [CmdletBinding()] | |
| param | |
| ( | |
| [Parameter(Mandatory=$true)] | |
| [Guid] | |
| $ResourceId, | |
| [Parameter(Mandatory=$true,ParameterSetName="PerUserCreditConsumption")] | |
| [switch] | |
| $PerUserCreditConsumption, | |
| [Parameter(Mandatory=$true,ParameterSetName="TotalCreditConsumption")] | |
| [switch] | |
| $TotalCreditConsumption, | |
| [Parameter(Mandatory=$true)] | |
| [DateTime] | |
| $StartDate, | |
| [Parameter(Mandatory=$true)] | |
| [DateTime] | |
| $EndDate | |
| ) | |
| begin | |
| { | |
| $accessToken = Get-AccessToken -ResourceUrl "https://api.powerplatform.com/" | |
| $headers = @{ "Authorization" = "Bearer $accessToken"; "Content-Type" = "application/json"; "Accept" = "application/json" } | |
| $uri = 'https://api.powerplatform.com/licensing/entitlements/MCSMessages/resources/{0}/users?fromDate={1}&toDate={2}&api-version=2024-10-01' -f $ResourceId, $StartDate.ToString("MM-dd-yyyy"), $EndDate.ToString("MM-dd-yyyy") | |
| } | |
| process | |
| { | |
| Write-Verbose "[$(Get-Date)] - Querying resource '$ResourceId' for usage." | |
| $response = Invoke-RestMethod -Method GET ` | |
| -Uri $uri ` | |
| -Headers $headers ` | |
| -ErrorAction Stop ` | |
| -Verbose:$false | |
| if( $TotalCreditConsumption.IsPresent ) | |
| { | |
| Write-Verbose "[$(Get-Date)] - Calculating total usage across $($response.value.users.Count) users." | |
| $totalBillableCreditsUsed = $totalCreditsUsed = $totalUserCount = $consumedUserCount = $billableConsumedUserCount = 0 | |
| foreach( $user in $response.value.users ) | |
| { | |
| $totalUserCount++ | |
| $totalBillableCreditsUsed += $user.consumed | |
| $totalCreditsUsed += $user.metadata.NonBillableQuantity | |
| if( $user.consumed -gt 0 ) | |
| { | |
| $billableConsumedUserCount++ | |
| } | |
| if( $user.metadata.NonBillableQuantity -gt 0 ) | |
| { | |
| $consumedUserCount++ | |
| } | |
| } | |
| [PSCustomObject] @{ | |
| BillableCreditsUsed = $totalBillableCreditsUsed | |
| CreditsUsed = $totalCreditsUsed | |
| BillableCreditUserCount = $billableConsumedUserCount | |
| CreditUserCount = $consumedUserCount | |
| TotalUserConsumptionCount = $totalUserCount | |
| } | |
| } | |
| else | |
| { | |
| Write-Verbose "[$(Get-Date)] - Calculating user level usage." | |
| if( $response.value.users ) | |
| { | |
| foreach( $user in $response.value.users ) | |
| { | |
| [PSCustomObject] @{ | |
| UserId = $user.userId | |
| BillableCreditsUsed = $user.consumed | |
| CreditsUsed = $user.metadata.NonBillableQuantity | |
| CreditUsageAsOfDate = $user.asOfDate | |
| } | |
| } | |
| } | |
| } | |
| } | |
| end | |
| { | |
| } | |
| } | |
| # lookback last 30 days | |
| $startDate = [DateTime]::Today.AddDays(-31) | |
| $endDate = [DateTime]::Today.AddDays(-1) | |
| # show total or per user per resource credit consumption | |
| $reportPerUserCreditConsumption = $false | |
| # get all copilot studio agents and agent workflows | |
| $resources = Get-PowerPlatformResource -IncludeAgentResources -IncludeWorkflowResources -Verbose | |
| # example filters if you want to narrow the scope | |
| # $resources = $resources | Where-Object -Property ResourceType -eq "Agent Workflow" | |
| # $resources = $resources | Where-Object -Property ResourceType -eq "GitHub Copilot Harness Agent" | |
| # $resources = $resources | Where-Object -Property ResourceType -eq "Standard Harness Agent" | |
| # pull agent usage | |
| $results = foreach( $resource in $resources ) | |
| { | |
| if( $reportPerUserCreditConsumption ) | |
| { | |
| $messages = Get-PowerPlatformResourceMessageConsumption -ResourceId $resource.ResourceId -StartDate $startDate -EndDate $endDate -PerUserCreditConsumption -Verbose | |
| foreach( $message in $messages ) | |
| { | |
| $row = $resource.psobject.Copy() | |
| $row | Add-Member -MemberType NoteProperty -Name "UserId" -Value $message.UserId | |
| $row | Add-Member -MemberType NoteProperty -Name "BillableCreditsConsumed" -Value $message.BillableCreditsUsed | |
| $row | Add-Member -MemberType NoteProperty -Name "CreditsConsumed" -Value $message.CreditsUsed | |
| $row | Add-Member -MemberType NoteProperty -Name "CreditUsageAsOfDate" -Value $message.CreditUsageAsOfDate | |
| $row | |
| } | |
| } | |
| else | |
| { | |
| $messages = Get-PowerPlatformResourceMessageConsumption -ResourceId $resource.ResourceId -StartDate $startDate -EndDate $endDate -TotalCreditConsumption -Verbose | |
| $resource | Add-Member -MemberType NoteProperty -Name "BillableCreditsConsumed" -Value $messages.BillableCreditsUsed | |
| $resource | Add-Member -MemberType NoteProperty -Name "CreditsConsumed" -Value $messages.CreditsUsed | |
| $resource | Add-Member -MemberType NoteProperty -Name "BillableCreditUserCount" -Value $messages.BillableCreditUserCount | |
| $resource | Add-Member -MemberType NoteProperty -Name "CreditUserCount" -Value $messages.CreditUserCount | |
| $resource | Add-Member -MemberType NoteProperty -Name "TotalUserConsumptionCount" -Value $messages.TotalUserConsumptionCount | |
| $resource | |
| } | |
| } | |
| $timestamp = Get-Date -Format FileDateTime | |
| $results | Export-Csv -Path "C:\_temp\PowerPlatformResourceMessageConsumption_$timestamp.csv" -NoTypeInformation |
