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. A developer shared a PowerShell script that queries Copilot credit consumption for Power Platform resources such as agents and workflows over a specified time period. The script uses Azure authentication and the Power Platform API to retrieve resource data, enabling user-level or per-resource reporting. | 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 |