Skip to content

Instantly share code, notes, and snippets.

@joerodgers
Last active September 28, 2026 15:24
Show Gist options
  • Select an option

  • Save joerodgers/335c428de741451e2f1eecbbb49f58cb to your computer and use it in GitHub Desktop.

Select an option

Save joerodgers/335c428de741451e2f1eecbbb49f58cb to your computer and use it in GitHub Desktop.
Example how to query resource (agents and workflows) Copilot credit consumption for a given time period.
#requries -modules "Az.Accounts"
class PowerPlatformResource
{
[string]
$ResourceType
[string]
$ResourceName
[Guid]
$ResourceId
[string]
$EnvironmentId
[string]
$EnvironmentName
[string]
$EnvironmentType
[string]
$EnvironmentRegion
[bool]
$EnvironmentIsManaged
[string]
$EnvironmentGroupId
[string]
$EnvironmentGroupName
}
class PowerPlatformResourceMessageConsumption : PowerPlatformResource
{
PowerPlatformResourceMessageConsumption([PowerPlatformResource]$resource)
{
$this.ResourceType = $resource.ResourceType
$this.ResourceName = $resource.ResourceName
$this.ResourceId = $resource.ResourceId
$this.EnvironmentId = $resource.EnvironmentId
$this.EnvironmentName = $resource.EnvironmentName
$this.EnvironmentType = $resource.EnvironmentType
$this.EnvironmentRegion = $resource.EnvironmentRegion
$this.EnvironmentIsManaged = $resource.EnvironmentIsManaged
$this.EnvironmentGroupId = $resource.EnvironmentGroupId
$this.EnvironmentGroupName = $resource.EnvironmentGroupName
}
[string]
$UserId
[int]
$NonBillableCreditsConsumed
[int]
$BillableCreditsConsumed
[DateTime]
$AsOfDate
}
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]
$IncludeStandardHarnessAgents,
[Parameter(Mandatory=$false)]
[switch]
$IncludeCopilotChatHarnessAgents,
[Parameter(Mandatory=$false)]
[switch]
$IncludeGithubHarnessAgents,
[Parameter(Mandatory=$false)]
[switch]
$IncludeAgentWorkflows
)
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": "extend",
"FieldName": "resourceType",
"Expression": "iif( type == 'microsoft.powerautomate/agentflows', 'Agent Workflow', iif( tostring(properties.createdIn) == 'Copilot Studio' and tostring(properties.harness) == 'GitHub Copilot', 'Copilot Studio GitHub Copilot Agent', iif((tostring(properties.createdIn) == 'Copilot Studio' and tostring(properties.harness) == 'Standard'), 'Copilot Studio Standard Agent', iif((tostring(properties.createdIn) == 'Copilot Studio' and tostring(properties.harness) == 'Copilot Chat'), 'Copilot Studio Declarative Agent', iif((tostring(properties.createdIn) == 'Copilot Studio Lite' and tostring(properties.harness) == 'Copilot Chat'), 'Agent Builder Agent', 'Unknown')))))"
}},
{{
"$type": "project",
"FieldList": [
"joinKey",
"resourceId = tolower(name)",
"type",
"harness = properties.harness",
"createdId = properties.createdIn",
"resourceName = properties.displayName",
"resourceType"
]
}},
{{
"$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( $IncludeStandardHarnessAgents.IsPresent -or $IncludeGithubHarnessAgents.IsPresent -or $IncludeCopilotChatHarnessAgents.IsPresent )
{
$resourceTypes += "`"'microsoft.copilotstudio/agents'`""
}
if( $IncludeAgentWorkflows.IsPresent )
{
$resourceTypes += "`"'microsoft.powerautomate/agentflows'`""
}
$page = 0
$results = 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( $result in $results )
{
if( $result.resourceType -eq "Unknown" -or $result.resourceType -eq "Agent Builder Agent" )
{
continue
}
if( -not $IncludeStandardHarnessAgents.IsPresent -and $result.resourceType -eq "Copilot Studio Standard Agent" )
{
continue
}
if( -not $IncludeGithubHarnessAgents.IsPresent -and $result.resourceType -eq "Copilot Studio GitHub Copilot Agent" )
{
continue
}
if( -not $IncludeCopilotChatHarnessAgents.IsPresent -and $result.resourceType -eq "Copilot Studio Declarative Agent" )
{
continue
}
$resource = [PowerPlatformResource]::new()
$resource.ResourceType = $result.resourceType
$resource.ResourceName = $result.resourceName
$resource.ResourceId = $result.resourceId
$resource.EnvironmentId = $result.environmentId
$resource.EnvironmentName = $result.environmentName
$resource.EnvironmentRegion = $result.environmentRegion
$resource.EnvironmentType = $result.environmentType
$resource.EnvironmentIsManaged = $result.isManagedEnvironment
$resource.EnvironmentGroupId = $result.environmentGroupId
$resource.EnvironmentGroupName = $result.environmentGroupName
$resource
}
Write-Verbose "[$(Get-Date)] - Discovered $($resources.Count) matching resources."
$resources
}
end
{
}
}
function Get-PowerPlatformResourceMessageConsumption
{
[CmdletBinding()]
param
(
[Parameter(Mandatory=$false)]
[PowerPlatformResource]
$PowerPlatformResource,
[Parameter(Mandatory=$true)]
[DateTime]
$StartDate,
[Parameter(Mandatory=$true)]
[DateTime]
$EndDate
)
begin
{
$continuationToken = $null
$accessToken = Get-AccessToken -ResourceUrl "https://api.powerplatform.com/"
$headers = @{ "Authorization" = "Bearer $accessToken"; "Content-Type" = "application/json"; "Accept" = "application/json" }
}
process
{
$uri = 'https://api.powerplatform.com/licensing/entitlements/MCSMessages/resources/{0}/users?fromDate={1}&toDate={2}&api-version=2024-10-01' -f $PowerPlatformResource.ResourceId, $StartDate.ToString("yyyy-MM-dd"), $EndDate.ToString("yyyy-MM-dd")
Write-Verbose "[$(Get-Date)] - Querying consumption data for $($PowerPlatformResource.ResourceType) - $($PowerPlatformResource.ResourceName)"
$response = Invoke-RestMethod -Method GET -Uri $uri -Headers $headers -ErrorAction Stop -Verbose:$false
foreach( $user in $response.value.users )
{
$powerPlatformResourceMessageConsumption = [PowerPlatformResourceMessageConsumption]::new($PowerPlatformResource)
$powerPlatformResourceMessageConsumption.UserId = $user.userId
$powerPlatformResourceMessageConsumption.NonBillableCreditsConsumed = $user.metadata.NonBillableQuantity
$powerPlatformResourceMessageConsumption.BillableCreditsConsumed = $user.consumed
$powerPlatformResourceMessageConsumption.AsOfDate = $user.asOfDate
$powerPlatformResourceMessageConsumption
}
}
end
{
}
}
# output file timestamp
$timestamp = Get-Date -Format FileDateTime
# lookback last 180 days
$startDate = [DateTime]::Today.AddDays(-180)
$endDate = [DateTime]::Today.AddDays(-1)
# query all agent types and workflows you desire... the more you pick the longer it'll take
$resources = Get-PowerPlatformResource -IncludeGithubHarnessAgents -IncludeStandardHarnessAgents -IncludeAgentWorkflows -Verbose
# enumerate each agent/workflow and query for message consumption data
foreach( $resource in $resources | Sort-Object -Property EnvironmentId, ResourceName )
{
$consumption = Get-PowerPlatformResourceMessageConsumption -PowerPlatformResource $resource -StartDate $startdate -EndDate $endDate -Verbose
$consumption | Export-Csv -Path "C:\_temp\PowerPlatformResourceMessageConsumption_$timestamp.csv" -NoTypeInformation -Append
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment