Last active
June 5, 2026 18:05
-
-
Save joerodgers/c2a56a5328ba2c2bd7d586eaf03a589d to your computer and use it in GitHub Desktop.
Creates a report mapping Power Platform environments to billing policies. Includes PAYGO details of each billing policy.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #requires -modules "Microsoft.PowerApps.Administration.PowerShell" | |
| function Get-BillingPolicyEnvironmentMap | |
| { | |
| [CmdletBinding()] | |
| param | |
| ( | |
| ) | |
| begin | |
| { | |
| $accessToken = Get-JwtToken -Audience "https://licensing.powerplatform.microsoft.com" | |
| $headers = @{ "Authorization" = "Bearer $accessToken"; "Content-Type" = "application/json"; "Accept" = "application/json" } | |
| $tenantId = $global:currentSession.tenantId | |
| $request = [PSCustomObject] @{ environmentIds = @() } | |
| $body = $request | ConvertTo-Json -Compress | |
| } | |
| process | |
| { | |
| $uri = 'https://licensing.powerplatform.microsoft.com/v2.0/tenants/{0}/BillingPolicies/getEnvironmentMap' -f $tenantId | |
| $response = Invoke-RestMethod -Method POST ` | |
| -Uri $uri ` | |
| -Headers $headers ` | |
| -Body $body ` | |
| -ErrorAction Stop ` | |
| -Verbose:$false | |
| return $response | |
| } | |
| end | |
| { | |
| } | |
| } | |
| function Get-BillingPolicy | |
| { | |
| [CmdletBinding()] | |
| param | |
| ( | |
| ) | |
| begin | |
| { | |
| $accessToken = Get-JwtToken -Audience "https://licensing.powerplatform.microsoft.com" | |
| $headers = @{ "Authorization" = "Bearer $accessToken"; "Content-Type" = "application/json"; "Accept" = "application/json" } | |
| $tenantId = $global:currentSession.tenantId | |
| } | |
| process | |
| { | |
| $uri = 'https://licensing.powerplatform.microsoft.com/v2.0/tenants/{0}/BillingPolicies' -f $tenantId | |
| $response = Invoke-RestMethod -Method GET ` | |
| -Uri $uri ` | |
| -Headers $headers ` | |
| -ErrorAction Stop ` | |
| -Verbose:$false | |
| $response.value | |
| } | |
| end | |
| { | |
| } | |
| } | |
| function Get-BillingPolicyReport | |
| { | |
| [CmdletBinding()] | |
| param | |
| ( | |
| ) | |
| begin | |
| { | |
| } | |
| process | |
| { | |
| $environmentMaps = Get-BillingPolicyEnvironmentMap | |
| $policies = Get-BillingPolicy | |
| foreach( $environmentMap in $environmentMaps ) | |
| { | |
| $policy = $policies | Where-Object -Property Id -eq $environmentMap.billingPolicyId | |
| [PSCustomObject] @{ | |
| EnvironmentId = $environmentMap.environmentId | |
| Id = $environmentMap.billingPolicyId | |
| Name = $policy.name | |
| Type = $policy.type | |
| Status = $policy.status | |
| Location = $policy.location | |
| CreatedOn = $policy.createdOn | |
| LastModifiedOn = $policy.lastModifiedOn | |
| CreatedBy = $policy.createdBy.id | |
| LastModifiedBy = $policy.lastModifiedBy.id | |
| SubscriptionId = $policy.billingInstrument.subscriptionId | |
| ResourceGroup = $policy.billingInstrument.resourceGroup | |
| ResourceGroupId = $policy.billingInstrument.id | |
| DataversePayGoEnabled = $policy.payGoEntitlements | Where-Object -Property entitlementId -eq "Database" | Select-Object -ExpandProperty payAsYouGoState | |
| PowerAutomatePayGoEnabled = $policy.payGoEntitlements | Where-Object -Property entitlementId -eq "CloudFlowRuns" | Select-Object -ExpandProperty payAsYouGoState | |
| PowerPagesPayGoEnabled = $policy.payGoEntitlements | Where-Object -Property entitlementId -eq "PowerPagesAuthenticated" | Select-Object -ExpandProperty payAsYouGoState | |
| Windows365ForAgentsPayGoEnabled = $policy.payGoEntitlements | Where-Object -Property entitlementId -eq "W365APAYGO" | Select-Object -ExpandProperty payAsYouGoState | |
| PowerAppsPayGoEnabled = $policy.payGoEntitlements | Where-Object -Property entitlementId -eq "AppPass" | Select-Object -ExpandProperty payAsYouGoState | |
| CopilotStudioPayGoEnabled = $policy.payGoEntitlements | Where-Object -Property entitlementId -eq "MCSMessages" | Select-Object -ExpandProperty payAsYouGoState | |
| PowerPlatformPayGoEnabled = $policy.payGoEntitlements | Where-Object -Property entitlementId -eq "ApiCalls" | Select-Object -ExpandProperty payAsYouGoState | |
| } | |
| } | |
| } | |
| end | |
| { | |
| } | |
| } | |
| function Get-EnvironmentMetadata | |
| { | |
| [CmdletBinding()] | |
| param | |
| ( | |
| ) | |
| begin | |
| { | |
| $accessToken = Get-JwtToken -Audience "https://api.bap.microsoft.com" -ErrorAction Stop | |
| $headers = @{ "Authorization" = "Bearer $accessToken"; "Content-Type" = "application/json"; "Accept" = "application/json" } | |
| } | |
| process | |
| { | |
| $uri = 'https://api.bap.microsoft.com/providers/Microsoft.BusinessAppPlatform/scopes/admin/environments?api-version=2020-06-01&$expand=properties.addons,properties.capacity,properties.permissions&$top=999' | |
| do | |
| { | |
| $response = Invoke-RestMethod -Method GET ` | |
| -Uri $uri ` | |
| -Headers $headers ` | |
| -ErrorAction Stop ` | |
| -Verbose:$false | |
| $uri = $response.nextLink | |
| foreach( $environment in $response.value ) | |
| { | |
| [PSCustomObject] @{ | |
| Id = $environment.name | |
| Name = $environment.properties.displayname | |
| Location = $environment.location | |
| CreatedDate = $environment.properties.createdTime | |
| CreatedBy = $environment.properties.createdBy.Id | |
| LastModifiedDate = $environment.properties.lastModifiedTime | |
| Sku = $environment.properties.EnvironmentSku | |
| ReleaseCycle = $environment.properties.cluster.category | |
| IsManaged = $environment.properties.governanceConfiguration.protectionLevel -eq "Standard" | |
| UpdateCadence = $environment.properties.updateCadence.Id | |
| DataverseSize = $environment.properties.capacity | Where-Object -Property "capacityType" -eq "Database" | Select-Object -ExpandProperty actualConsumption | |
| DataverseFileMB = $environment.properties.capacity | Where-Object -Property "capacityType" -eq "File" | Select-Object -ExpandProperty actualConsumption | |
| DataverseLogMB = $environment.properties.capacity | Where-Object -Property "capacityType" -eq "Log" | Select-Object -ExpandProperty actualConsumption | |
| VNetId = $environment.properties.enterprisePolicies.vnets | Select-Object -First 1 -ExpandProperty id | |
| VNetPolicyName = ($environment.properties.enterprisePolicies.vnets | Select-Object -First 1 -ExpandProperty id) -split "/" | Select-Object -Last 1 | |
| VNetLinkStatus = $environment.properties.enterprisePolicies.vnets | Select-Object -First 1 -ExpandProperty linkStatus | |
| VNetLocation = $environment.properties.enterprisePolicies.vnets | Select-Object -First 1 -ExpandProperty location | |
| VNetPolicyId = $environment.properties.enterprisePolicies.vnets | Select-Object -First 1 -ExpandProperty policyId | |
| VNetSystemId = $environment.properties.enterprisePolicies.vnets | Select-Object -First 1 -ExpandProperty systemId | |
| } | |
| } | |
| } | |
| while( $response.nextLink ) | |
| } | |
| end | |
| { | |
| } | |
| } | |
| function Get-EnvironmentReport | |
| { | |
| [CmdletBinding()] | |
| param | |
| ( | |
| ) | |
| begin | |
| { | |
| } | |
| process | |
| { | |
| $environments = Get-EnvironmentMetadata | |
| $billingReports = Get-BillingPolicyReport | |
| foreach( $environment in $environments ) | |
| { | |
| $billingReport = $billingReports | Where-Object -Property "EnvironmentId" -eq $environment.Id | |
| [PSCustomObject] @{ | |
| EnvironmentId = $environment.Id | |
| EnvironmentName = $environment.Name | |
| EnvironmentLocation = $environment.Location | |
| EnvironmentCreatedDate = $environment.CreatedDate | |
| EnvironmentCreatedBy = $environment.CreatedBy | |
| EnvironmentLastModifiedDate = $environment.LastModifiedDate | |
| EnvironmentSku = $environment.Sku | |
| EnvironmentUpdateCadence = $environment.UpdateCadence | |
| EnvironmentDataverseStorageMB = $environment.DataverseSize | |
| EnvironmentDataverseFileMB = $environment.DataverseFileMB | |
| EnvironmentDataverseLogMB = $environment.DataverseLogMB | |
| EnvironmentReleaseCycle = $environment.ReleaseCycle | |
| EnvironmentIsManaged = $environment.IsManaged | |
| EnvironmentVNetId = $environment.VNetId | |
| EnvironmentVNetPolicyName = $environment.VNetPolicyName | |
| EnvironmentVNetLinkStatus = $environment.VNetLinkStatus | |
| EnvironmentVNetLocation = $environment.VNetLocation | |
| EnvironmentVNetPolicyId = $environment.VNetPolicyId | |
| EnvironmentVNetSystemId = $environment.VNetSystemId | |
| BillingPolicyId = $billingReport.Id | |
| BillingPolicyName = $billingReport.Name | |
| BillingPolicyType = $billingReport.Type | |
| BillingPolicyStatus = $billingReport.Status | |
| BillingPolicyLocation = $billingReport.Location | |
| BillingPolicyCreatedOn = $billingReport.CreatedOn | |
| BillingPolicyLastModifiedOn = $billingReport.LastModifiedOn | |
| BillingPolicyCreatedBy = $billingReport.CreatedBy | |
| BillingPolicyLastModifiedBy = $billingReport.LastModifiedBy | |
| BillingSubscriptionId = $billingReport.SubscriptionId | |
| BillingResourceGroup = $billingReport.ResourceGroup | |
| BillingResourceGroupId = $billingReport.ResourceGroupId | |
| BillingDataversePayGoEnabled = $billingReport.DataversePayGoEnabled | |
| BillingPowerAutomatePayGoEnabled = $billingReport.PowerAutomatePayGoEnabled | |
| BillingPowerPagesPayGoEnabled = $billingReport.PowerPagesPayGoEnabled | |
| BillingWindows365ForAgentsPayGoEnabled = $billingReport.Windows365ForAgentsPayGoEnabled | |
| BillingPowerAppsPayGoEnabled = $billingReport.PowerAppsPayGoEnabled | |
| BillingCopilotStudioPayGoEnabled = $billingReport.CopilotStudioPayGoEnabled | |
| BillingPowerPlatformPayGoEnabled = $billingReport.PowerPlatformPayGoEnabled | |
| } | |
| } | |
| } | |
| end | |
| { | |
| } | |
| } | |
| # requires power platform admin role when prompted. This is kinda a hack b/c Add-PowerAppsAccount clear existing sessions when run, so you can't load 2+ into the current session w/ that cmdlet. | |
| $null = Get-JwtToken -Audience "https://licensing.powerplatform.microsoft.com" -ErrorAction Stop | |
| $null = Get-JwtToken -Audience "https://api.bap.microsoft.com" -ErrorAction Stop | |
| Get-EnvironmentReport | Export-Csv -Path "C:\_temp\PowerPlatformEnvironmentReport.csv" -NoTypeInformation | |
| <# EXAMPLE OUTPUT | |
| EnvironmentId : 2e20961f-f961-ef2b-b6c8-ed6b09201115 | |
| EnvironmentName : Restricted Agents | |
| EnvironmentLocation : unitedstates | |
| EnvironmentCreatedDate : 2025-05-12T20:37:31.1024356Z | |
| EnvironmentCreatedBy : a8734c7e-a19b-61c6-b384-5e550ca02b9e | |
| EnvironmentLastModifiedDate : 2025-09-02T20:58:13.9111884Z | |
| EnvironmentSku : Sandbox | |
| EnvironmentUpdateCadence : Frequent | |
| EnvironmentDataverseMB : 1460.25 | |
| EnvironmentDataverseFileMB : 720.4951 | |
| EnvironmentDataverseLogMB : 0.2026167 | |
| EnvironmentVNetId : /subscriptions/aecd75f0-76ea-42b0-89d7-45bdd9977b0c/resourceGroups/powerplatform-virtualnetwork/providers/Microsoft.PowerPlatform/enterprisePolicies/powerplatform-virtualnetwork | |
| EnvironmentVNetPolicyName : powerplatform-virtualnetwork | |
| EnvironmentVNetLinkStatus : Linked | |
| EnvironmentVNetLocation : unitedstates | |
| EnvironmentVNetPolicyId : 1fd914e0-adfe-6a29-9ded-e5e6d92d7f19 | |
| EnvironmentVNetSystemId : /regions/unitedstates/providers/Microsoft.PowerPlatform/enterprisePolicies/1fd914e0-adfe-4a29-8ded-e5e6d92d7f19 | |
| BillingPolicyId : fb225338-55d8-4831-a9ca-0f74d502eeaa | |
| BillingPolicyName : OtherDepartments | |
| BillingPolicyType : TenantOwned | |
| BillingPolicyStatus : Enabled | |
| BillingPolicyLocation : unitedstates | |
| BillingPolicyCreatedOn : 2025-08-27T17:11:37Z | |
| BillingPolicyLastModifiedOn : 2026-04-23T13:09:07Z | |
| BillingPolicyCreatedBy : a8734c7e-a19b-41c6-b384-4e550ca02b9e | |
| BillingPolicyLastModifiedBy : a8734c7e-a19b-41c6-b384-4e550ca02b9e | |
| BillingSubscriptionId : aecd75f0-76ea-42b0-89d7-45bdd9977b0c | |
| BillingResourceGroup : paygo-catchall | |
| BillingResourceGroupId : /subscriptions/aecd75f0-76ea-42b0-89d7-45bdd9977b0c/resourceGroups/paygo-catchall/providers/Microsoft.PowerPlatform/accounts/OtherDepartments | |
| BillingDataversePayGoEnabled : True | |
| BillingPowerAutomatePayGoEnabled : True | |
| BillingPowerPagesPayGoEnabled : True | |
| BillingWindows365ForAgentsPayGoEnabled : False | |
| BillingPowerAppsPayGoEnabled : True | |
| BillingCopilotStudioPayGoEnabled : False | |
| BillingPowerPlatformPayGoEnabled : True | |
| #> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment