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.
A User Access Group decides which people can use App Store, and it doesn’t apply to a managed identity. The seven reads work whether or not a User Access Group is set.
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 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. Saving the setting restarts the
App Service. If you are deploying from the Azure Marketplace, enter the same value in Workload API
audience (optional) on the Advanced step.
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# Az.Accounts 5.0 and later returns the token as a SecureString. Convert it if so.if ($token -is [System.Security.SecureString]) { $token = [System.Net.NetworkCredential]::new('', $token).Password}$headers = @{ Authorization = "Bearer $token" }
$updates = Invoke-RestMethod -Uri "$appStore/api/winget/updates/available" -Headers $headers
$updates | Select-Object displayName, publisher, currentVersion, latestVersion | 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. In rough order of likelihood, check these:
| 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, and the call returns 401 instead of 403 |
| 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. A token for an audience App Store doesn’t know returns 401 |
| 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.