Problem or use case
apiops publish fails preflight validation unless the executing identity has
Reader (or broader) at the Resource Group scope, even though publishing
itself only ever touches the target APIM service/workspace.
validatePreFlight() (src/clients/apim-client.ts) performs two sequential
existence checks before publishing:
GET {armEndpoint}/subscriptions/{sub}/resourceGroups/{rg} — requires
Microsoft.Resources/subscriptions/resourceGroups/read, granted only by
Reader (or higher) at resource group or subscription scope.
GET {baseUrl} (the APIM service resource itself) — satisfied by
Reader/Service Reader at APIM service scope.
Step 1 fails with a 403 for any identity scoped only to the APIM service,
before step 2 (the actually-relevant check) even runs:
AuthorizationFailed: The client does not have authorization to perform action
'Microsoft.Resources/subscriptions/resourceGroups/read' over scope
'/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}'
Use case: organizations running a shared-APIM, multi-team model (one
resource group, several teams' service principals) currently must choose
between granting every team broad RG-scoped read access to every other
resource in the RG (other APIM instances, storage, App Insights, etc.), or
funneling all publishes through a single centrally-scoped identity — both
work against per-team least privilege. This also breaks parity with extract,
where workspace-scoped Reader + Service Reader (no RG scope) is already
sufficient.
Proposed solution
Scope the preflight check down to only what publish actually needs: read
access to the target APIM service. Step 2 above already GETs the APIM
service resource directly and will itself return 404 if the resource group
or instance doesn't exist, so the RG-level check in step 1 is purely
diagnostic (a slightly clearer "resource group not found" message) and isn't
load-bearing for correctness. Two options, in order of preference:
- Drop the RG-level check entirely and rely on the APIM-service GET
(step 2) for existence validation. Slightly less specific error message
on a missing RG vs. missing APIM instance, but removes the RG Reader
requirement entirely.
- Make the RG check non-fatal: if it returns 403 specifically, log a
warning and continue to step 2, only hard-failing on a genuine 404.
Preserves the diagnostic for identities that do have RG-level access,
without hard-blocking least-privilege ones.
Affected command
apiops publish
Problem or use case
apiops publishfails preflight validation unless the executing identity hasReader(or broader) at the Resource Group scope, even though publishingitself only ever touches the target APIM service/workspace.
validatePreFlight()(src/clients/apim-client.ts) performs two sequentialexistence checks before publishing:
GET {armEndpoint}/subscriptions/{sub}/resourceGroups/{rg}— requiresMicrosoft.Resources/subscriptions/resourceGroups/read, granted only byReader(or higher) at resource group or subscription scope.GET {baseUrl}(the APIM service resource itself) — satisfied byReader/Service Readerat APIM service scope.Step 1 fails with a 403 for any identity scoped only to the APIM service,
before step 2 (the actually-relevant check) even runs:
AuthorizationFailed: The client does not have authorization to perform action
'Microsoft.Resources/subscriptions/resourceGroups/read' over scope
'/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}'
Use case: organizations running a shared-APIM, multi-team model (one
resource group, several teams' service principals) currently must choose
between granting every team broad RG-scoped read access to every other
resource in the RG (other APIM instances, storage, App Insights, etc.), or
funneling all publishes through a single centrally-scoped identity — both
work against per-team least privilege. This also breaks parity with extract,
where workspace-scoped
Reader+Service Reader(no RG scope) is alreadysufficient.
Proposed solution
Scope the preflight check down to only what publish actually needs: read
access to the target APIM service. Step 2 above already GETs the APIM
service resource directly and will itself return 404 if the resource group
or instance doesn't exist, so the RG-level check in step 1 is purely
diagnostic (a slightly clearer "resource group not found" message) and isn't
load-bearing for correctness. Two options, in order of preference:
(step 2) for existence validation. Slightly less specific error message
on a missing RG vs. missing APIM instance, but removes the RG
Readerrequirement entirely.
warning and continue to step 2, only hard-failing on a genuine 404.
Preserves the diagnostic for identities that do have RG-level access,
without hard-blocking least-privilege ones.
Affected command
apiops publish