Https- Graph.microsoft.com V1.0 Applications Info
Invoke-RestMethod -Method Post -Uri "https://graph.microsoft.com/v1.0/applications" -Headers $authHeader -Body $body -ContentType "application/json"
$body = @ displayName = "CI/CD Automation App" signInAudience = "AzureADMyOrg" keyCredentials = @( @ type = "AsymmetricX509Cert" usage = "Verify" key = $base64Cert startDateTime = (Get-Date -Format "yyyy-MM-ddTHH:mm:ssZ") endDateTime = (Get-Date).AddYears(1).ToString("yyyy-MM-ddTHH:mm:ssZ")
But the endpoint supports , $filter , $select , and $top — which most people underutilize. Useful query patterns # Get an app by its client ID (not GUID id) GET /applications?$filter=appId eq '11111111-2222-3333-4444-555555555555' Get apps with secrets expiring in the next 30 days GET /applications?$expand=passwordCredentials&$filter=passwordCredentials/any(p:p/endDateTime le 2025-05-17T00:00:00Z) Only fetch specific fields (reduces latency) GET /applications?$select=displayName,appId,web,identifierUris 3. Hidden & Undocumented Behaviors api and web are mutually exclusive You cannot have a public client app ( web redirect URIs) that also exposes an API ( api scopes) in the same object—without causing odd validation failures. If you need both, split into two app registrations. signInAudience controls the universe Many developers leave this as "AzureADMyOrg" (single-tenant). But if you ever want to allow personal Microsoft accounts or other Azure AD tenants, change it to AzureADMultipleOrgs or AzureADandPersonalMicrosoftAccount .
"requests": [ "id": "1", "method": "GET", "url": "/applications/id/passwordCredentials" , "id": "2", "method": "GET", "url": "/applications/id/keyCredentials" ] https- graph.microsoft.com v1.0 applications
| Limit | Value | |-------|-------| | Requests per 10 seconds per app | 2,000 | | Requests per 10 seconds per tenant | 5,000 | | Max $top | 999 |
If you're building a production automation that must last years, stick with /v1.0 . For one-off governance scripts or advanced scenarios, /beta is fine. Find all multi-tenant apps (anyone can consent) that have high-privilege permissions and no owner assigned (security risk):
GET /applications?$filter=signInAudience eq 'AzureADMultipleOrgs'&$expand=owners($top=1),requiredResourceAccess If the response has an empty owners list, any admin in any tenant could theoretically modify the app's consent permissions. That's a red flag for supply chain risk. The /v1.0/applications endpoint looks simple on the surface—just CRUD on app registrations. But its real power comes from understanding the expansion properties, credential types, and the subtle boundary between application and service principal. Invoke-RestMethod -Method Post -Uri "https://graph
If you manage identity in Microsoft 365, you’ve probably spent countless hours in the Azure AD portal clicking through "App registrations." But behind every click is a REST API call.
But $expand on passwordCredentials or keyCredentials is . Avoid it unless necessary. Instead, fetch apps first (no expand), then batch request credentials:
| Entity | Endpoint | Tenant scope | Analogy | |--------|----------|--------------|---------| | Application | /v1.0/applications | Home tenant only | Blueprint | | Service Principal | /v1.0/servicePrincipals | One per tenant | Built house | If you need both, split into two app registrations
POST /servicePrincipals
In this post, we’ll tear down the endpoint, explore its hidden properties, look at real-world automation patterns, and cover the security pitfalls that even seasoned admins miss. Before writing code, we need to clear up a massive source of confusion.
Whether you're automating app lifecycle, building an internal governance tool, or hunting for security misconfigurations, this endpoint is your scalpel. Use it with precision, respect its throttling limits, and always—always—validate the signInAudience before you deploy.
) | ConvertTo-Json -Depth 10