Skip to content

Call the API from Azure Automation

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.

Seven reads, and nothing else:

WhatEndpoint
The catalogGET /api/apps, /api/apps/paginated, /api/apps/{id}
Update rolloutsGET /api/admin/update-deployments, /api/admin/update-deployments/{id}
Packaging job statusGET /api/packaging/jobs
Apps with an update waitingGET /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.

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.

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.

  1. In the Azure portal, go to Microsoft Entra ID > App registrations > New registration.
  2. Name it something recognizable, for example App Store for Intune API.
  3. Leave Redirect URI empty. Nothing signs in interactively against this registration.
  4. Select Register.
  5. On Overview, note the Application (client) ID.
  6. 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.
  1. Still in that registration, go to App roles > Create app role.
  2. 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.
  3. 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”
  1. Go to your Automation Account > Identity.
  2. On the System assigned tab, set Status to On, and Save.
  3. Note the Object (principal) ID that appears. That is the identity you are about to authorize.

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.

Terminal window
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.Id

To confirm afterwards: Microsoft Entra ID > Enterprise applications, filter Application type to Managed Identities, open your Automation Account, and check Permissions.

Terminal window
# 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-Table

Store 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.


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:

CheckWhat to look at
The role was assignedStep 5. An Automation Account with its identity enabled but no role assignment authenticates perfectly and reaches nothing
App Store knows the audienceStep 4. Without it, App Store accepts no application token at all
The token is for the right audienceThe -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 matchesAppStore.Automation.Read, exactly, including case
The endpoint supports unattended accessOnly 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.

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.