Powershell & PowerPlatform API

Querying PowerPlatform API via. Powershell

I found out that our netadmins are regularly checking the amount of free space we have in our Power Platform (Dynamics) subscription with the following process:

  1. Launch a browser
  2. Authenticate to portal.office.com
  3. Click Admin. A new window opens.
  4. Click All Admin Centers.
  5. Click Power Apps. A new window opens.
  6. Expand Resources -> Capacity.
  7. Look at the Storage Capacity Usage field and make sure we’re below our capacity.

Whew! I’m tired typing that up once! I don’t think I could stand doing it daily (or weekly, or whenever they remember), so let’s automate it!

We need to know the total capacity and what we’re using. If we cross some threshold, trigger an action. That’s pretty straightforward.

Used Storage
We can grab this from the Business App Platform API, but there are some hoops to jump through. We need to setup an account that’ll have permissions for this, Register an Admin Management Application in PowerPlatform, then query the API.

Setup the Account
How to: Use the portal to create an Azure AD application and service principal that can access resources

Register an Admin Management Application

#Region RegisterAdminManagementApplication
#https://docs.microsoft.com/en-us/power-platform/admin/powershell-create-service-principal
#Requires Powershell 5.1

Import-Module Microsoft.PowerApps.Administration.PowerShell
Import-Module Microsoft.PowerApps.PowerShell

$appId = "APPID_FROM_AZURE"
$tenantId = "TENANTID_FROM_AZURE"

# Login interactively with a tenant administrator for Power Platform
Add-PowerAppsAccount -Endpoint prod -TenantID $tenantId

# Register a new application, this gives the SPN / client application same permissions as a tenant admin
New-PowerAppManagementApp -ApplicationId $appId
#EndRegion RegisterAdminManagementApplication

Query the API

#Region AuthenticateAsServicePrincipalDynamic
$clientID = "CLIENTID_FROM_AZURE"
$clientSecret = "PASSWORD_FROM_AZURE"
$tenantdomain = "company.onmicrosoft.com"
$loginURL = "https://login.microsoftonline.com/"
$resource = "https://api.bap.microsoft.com/"
$body = @{grant_type = "client_credentials"; resource = $resource; client_id = $clientID; client_secret = $clientSecret }
$oauth = Invoke-RestMethod -Method Post -Uri $loginURL/$tenantdomain/oauth2/token?api-version=1.0 -Body $body
$headerParams = @{'Authorization' = "$($oauth.token_type) $($oauth.access_token)" }
#EndRegion  AuthenticateAsServicePrincipalDynamics


$request = Invoke-RestMethod -Method get -Headers $headerParams -Uri 'https://api.bap.microsoft.com/providers/Microsoft.BusinessAppPlatform/scopes/admin/environments?api-version=2020-10-01&$expand=properties.capacity'
$UsedStorage = "{0:n2}" -f (($request.value.properties.capacity | Measure-Object -Property RatedConsumption -Sum).sum / 1024)

Storage Capacity
The available Storage Capacity is more complicated to determine, since it’s not stored anywhere that I could find. It seems to be dynamically generated, which means this script will have to do the legwork. Each tenant gets a base 10GB of Storage Capacity, and additional storage is added per licensed user, or by purchasing more Storage Capacity. We can pull the licesing information from the MS Graph API:

#Region AuthenticateAsServicePrincipalGraph
$resource = "https://graph.microsoft.com/"
$body = @{grant_type = "client_credentials"; resource = $resource; client_id = $clientID; client_secret = $clientSecret }
$oauth = Invoke-RestMethod -Method Post -Uri $loginURL/$tenantdomain/oauth2/token -Body $body
$headerParams = @{'Authorization' = "$($oauth.token_type) $($oauth.access_token)" }
#EndRegion  AuthenticateAsServicePrincipalGraph

$request = Invoke-RestMethod -Method get -Headers $headerParams -Uri 'https://graph.microsoft.com/v1.0/subscribedskus'

#calculate total space in GB
$BaseStorage = 10 #fixed value per tenant
$DYN_365_SALES = .25 * ($request.value | Where-Object { $_.skupartnumber -eq "dyn365_enterprise_sales" } | Select-Object -ExpandProperty prepaidunits).enabled
$CRMStorage = ($request.value | Where-Object { $_.skupartnumber -eq "CRMSTORAGE" } | Select-Object -ExpandProperty prepaidunits).enabled
$TotalStorage = "{0:n2}" -f ($BaseStorage + $DYN_365_SALES + $CRMStorage)

Now let’s see the whole thing together, including the actions we’ll take after crossing certain space thresholds, as well as a function to let us know when the Application Password is near expiry:

#O365-Dynamics-Capacity.ps1

<#
Script to query the used vs total storage space we have in Power Platform (Dynamics)

https://docs.microsoft.com/en-us/power-platform/admin/list-environments
https://jussiroine.com/2019/12/building-a-monitoring-solution-for-power-platform-events-using-powershell-c-azure-log-analytics-and-azure-sentinel/

Greg Beifuss
2021-06-14 21:27
#>

<#
#Region RegisterAdminManagementApplication
#https://docs.microsoft.com/en-us/power-platform/admin/powershell-create-service-principal
#Requires Powershell 5.1

Import-Module Microsoft.PowerApps.Administration.PowerShell
Import-Module Microsoft.PowerApps.PowerShell

$appId = "APPID_FROM_AZURE"
$tenantId = "TENANTID_FROM_AZURE"

# Login interactively with a tenant administrator for Power Platform
Add-PowerAppsAccount -Endpoint prod -TenantID $tenantId

# Register a new application, this gives the SPN / client application same permissions as a tenant admin
New-PowerAppManagementApp -ApplicationId $appId
#EndRegion RegisterAdminManagementApplication
#>

#Region AuthenticateAsServicePrincipalDynamics

$clientID = "CLIENTID_FROM_AZURE"
$clientSecret = "PASSWORD_FROM_AZURE"
$tenantdomain = "company.onmicrosoft.com"
$loginURL = "https://login.microsoftonline.com/"
$resource = "https://api.bap.microsoft.com/"
$body = @{grant_type = "client_credentials"; resource = $resource; client_id = $clientID; client_secret = $clientSecret }
$oauth = Invoke-RestMethod -Method Post -Uri $loginURL/$tenantdomain/oauth2/token?api-version=1.0 -Body $body
$headerParams = @{'Authorization' = "$($oauth.token_type) $($oauth.access_token)" }
#EndRegion  AuthenticateAsServicePrincipalDynamics


$request = Invoke-RestMethod -Method get -Headers $headerParams -Uri 'https://api.bap.microsoft.com/providers/Microsoft.BusinessAppPlatform/scopes/admin/environments?api-version=2020-10-01&$expand=properties.capacity'
$UsedStorage = "{0:n2}" -f (($request.value.properties.capacity | Measure-Object -Property RatedConsumption -Sum).sum / 1024)


#Region AuthenticateAsServicePrincipalGraph
$resource = "https://graph.microsoft.com/"
$body = @{grant_type = "client_credentials"; resource = $resource; client_id = $clientID; client_secret = $clientSecret }
$oauth = Invoke-RestMethod -Method Post -Uri $loginURL/$tenantdomain/oauth2/token -Body $body
$headerParams = @{'Authorization' = "$($oauth.token_type) $($oauth.access_token)" }
#EndRegion  AuthenticateAsServicePrincipalGraph

$request = Invoke-RestMethod -Method get -Headers $headerParams -Uri 'https://graph.microsoft.com/v1.0/subscribedskus'


#calculate total space in GB
$BaseStorage = 10 #fixed value per tenant
$DYN_365_SALES = .25 * ($request.value | Where-Object { $_.skupartnumber -eq "dyn365_enterprise_sales" } | Select-Object -ExpandProperty prepaidunits).enabled
$CRMStorage = ($request.value | Where-Object { $_.skupartnumber -eq "CRMSTORAGE" } | Select-Object -ExpandProperty prepaidunits).enabled
$TotalStorage = "{0:n2}" -f ($BaseStorage + $DYN_365_SALES + $CRMStorage)

#Send email based on how much storage is left.
Switch ($UsedStorage / $TotalStorage) {
  { $_ -ge 0.70 } { Send-MailMessage -To helpdesk@company.com -From helpdesk@company.com -Subject "Dynamics Capacity - $(($_*100).ToString("#.##"))% in use!" -Body "We're using $UsedStorage / $TotalStorage GB. Please immediately escalate to a Netadmin." -SmtpServer 192.168.1.18 ; break}
  { $_ -gt 0.65 } { Send-MailMessage -To helpdesk@company.com -From helpdesk@company.com -Subject "Dynamics Capacity - 65% in use" -Body "We're using $UsedStorage / $TotalStorage GB, or $((($UsedStorage / $TotalStorage)*100).ToString("#.##"))%. Please escalate to a Netadmin." -SmtpServer 192.168.1.18 }
}

#Send email if Application password is near expiry
if ((Get-Date).adddays(14) -ge "6/10/2022") {
  Send-MailMessage -To helpdesk@company.com -From helpdesk@company.com -Subject "O365-Dynamics-Capacity.ps1" -Body "The AzureAD Application Powershell-PowerPlatform-CapacityReport has a Client Secret near expiry. Please escalate to Greg for renewal." -SmtpServer 192.168.1.18
}

Greg

Built with Hugo
Theme Stack designed by Jimmy