Call the API from Azure Automation
A scheduled job has no person to sign in as. Running one as a named employee is not unattended automation with extra steps: it breaks when they leave, and it inherits everything else that person can do.
Instead, the workload authenticates as itself, using a managed identity Azure maintains for it. This page sets that up end to end for an Azure Automation Account, which is the supported scenario.
What a managed identity can do
Section titled “What a managed identity can do”Seven reads, and nothing else:
| What | Endpoint |
|---|---|
| The catalog | GET /api/apps, /api/apps/paginated, /api/apps/{id} |
| Update rollouts | GET /api/admin/update-deployments, /api/admin/update-deployments/{id} |
| Packaging job status | GET /api/packaging/jobs |
| Apps with an update waiting | GET /api/winget/updates/available |
It cannot change anything, and it cannot approve or reject a request. An approval is a named person’s decision recorded against them, so the product refuses rather than recording one it cannot attribute. See API authentication.
Before you start
Section titled “Before you start”You need:
- Application Administrator or Cloud Application Administrator in Microsoft Entra ID, to create the registration and assign the role.
- Permission to create or edit an Azure Automation Account.
- Somebody who can redeploy or edit App Store’s App Service configuration, for step 4.
Step 1: Register the workload API
Section titled “Step 1: Register the workload API”This is a second app registration, separate from the one people sign in to App Store with. It exists only to name an audience and hold App Store’s application roles. Do not add a client secret, a certificate, or any Microsoft Graph or Intune permission to it, and do not reuse the App Store sign-in registration for this.
- In the Azure portal, go to Microsoft Entra ID > App registrations > New registration.
- Name it something recognizable, for example
App Store for Intune API. - Leave Redirect URI empty. Nothing signs in interactively against this registration.
- Select Register.
- On Overview, note the Application (client) ID.
- Go to Expose an API > Application ID URI > Add, and accept the default
api://<application-client-id>. This value is the audience, and you need it twice below.
Step 2: Define the application role
Section titled “Step 2: Define the application role”- Still in that registration, go to App roles > Create app role.
- Fill it in exactly:
- Display name:
App Store automation read - Allowed member types: Applications
- Value:
AppStore.Automation.Read - Description:
Read the App Store catalog and operational status.
- Display name:
- Select Apply.
The Value must match exactly, including case. It is what App Store looks for in the token.
Step 3: Enable the Automation Account’s managed identity
Section titled “Step 3: Enable the Automation Account’s managed identity”- Go to your Automation Account > Identity.
- On the System assigned tab, set Status to On, and Save.
- Note the Object (principal) ID that appears. That is the identity you are about to authorize.
Step 4: Give App Store the audience
Section titled “Step 4: Give App Store the audience”App Store refuses every application token until it knows which audience to accept, so unattended access stays off until this is done.
Set the App Service application setting AzureAd__WorkloadApiAudience to the Application ID URI
from step 1, for example api://11111111-2222-3333-4444-555555555555, then restart the App Service.
If you are deploying from the Azure Marketplace, the same value is the Workload API audience field
on the Advanced tab.
This is an identifier, not a credential. Knowing it grants nobody anything.
Step 5: Assign the role to the managed identity
Section titled “Step 5: Assign the role to the managed identity”Role assignment for an application permission has no portal experience, so this is a short PowerShell snippet. Run it once.
Connect-MgGraph -Scopes 'AppRoleAssignment.ReadWrite.All','Application.Read.All'
# The workload API registration from step 1, by its Application ID URI.$apiAppId = '<application-client-id-from-step-1>'$automationObjectId = '<object-id-from-step-3>'
$apiSp = Get-MgServicePrincipal -Filter "appId eq '$apiAppId'"if (-not $apiSp) { # A registration has no service principal until something needs one. Create it. $apiSp = New-MgServicePrincipal -AppId $apiAppId}
$role = $apiSp.AppRoles | Where-Object { $_.Value -eq 'AppStore.Automation.Read' }if (-not $role) { throw "The AppStore.Automation.Read role is not defined on that registration yet." }
New-MgServicePrincipalAppRoleAssignment ` -ServicePrincipalId $automationObjectId ` -PrincipalId $automationObjectId ` -ResourceId $apiSp.Id ` -AppRoleId $role.IdTo confirm afterwards: Microsoft Entra ID > Enterprise applications, filter Application type to Managed Identities, open your Automation Account, and check Permissions.
Step 6: Call the API from a runbook
Section titled “Step 6: Call the API from a runbook”# The Automation Account's own system-assigned managed identity. No credential is supplied# because none exists.Connect-AzAccount -Identity | Out-Null
$audience = 'api://<application-client-id-from-step-1>'$appStore = 'https://appstore.contoso.com'
$token = (Get-AzAccessToken -ResourceUrl $audience).Token$headers = @{ Authorization = "Bearer $token" }
$updates = Invoke-RestMethod -Uri "$appStore/api/winget/updates/available" -Headers $headers
$updates | Select-Object appName, currentVersion, availableVersion | Format-TableStore the audience and the host as Automation variables rather than hard-coding them, and remember
that Get-AzAccessToken returns a short-lived token: fetch one per run rather than caching it.
If you get 403
Section titled “If you get 403”A workload token has to prove four things at once, and missing any one of them gives the same status code. In rough order of likelihood:
| Check | What to look at |
|---|---|
| The role was assigned | Step 5. An Automation Account with its identity enabled but no role assignment authenticates perfectly and reaches nothing |
| App Store knows the audience | Step 4. Without it, App Store accepts no application token at all |
| The token is for the right audience | The -ResourceUrl in step 6 must be the Application ID URI from step 1, not the App Store sign-in registration’s client ID |
| The role value matches | AppStore.Automation.Read, exactly, including case |
| The endpoint supports unattended access | Only the seven reads listed above. Everything else returns 403 for a workload, by design |
If you are calling /approve or /reject, that 403 is not a misconfiguration. Those are
delegated-human-only and always will be.
What this does not do
Section titled “What this does not do”It does not impersonate anyone. The workload acts as itself. The audit trail records it as an application, identified by its service principal, and never manufactures a user identity for it.
It does not borrow App Store’s privileges. App Store performs Intune and Graph work as the App Service’s own managed identity. Your caller’s token is never forwarded to Graph or Intune.
It does not widen what App Store can do. The role grants reads of data App Store already holds.