BenV
BenV

Reputation: 12452

Get-AzureRmRoleAssignment - Access denied to the specified API version

Update: I've found that the issue is not related to the IncludeClassicAdministrators parameter - the problem still occurs without that. I've updated the question accordingly.

Background
I am writing an Azure Automation script to monitor who has access to my subscriptions. I am calling Get-AzureRmRoleAssignment to get the list of admins. When I run my script locally, authenticated as myself, it works fine. When I run it in Automation, I'm authenticating with a service principal, and it's failing with Access denied to the specified API version.

Details
I enabled debug output and I can see that Get-AzureRmRoleAssignment makes 3 API calls:

GET https://management.azure.com/subscriptions/xxx/providers/Microsoft.Authorization
    /roleAssignments?api-version=2015-07-01 

GET https://management.azure.com//subscriptions/xxx/providers/Microsoft.Authorization
    /roleDefinitions?$filter=atScopeAndBelow()&api-version=2015-07-01

POST https://graph.windows.net/xxx/getObjectsByObjectIds?api-version=1.42-previewInternal 
{
  "objectIds": [
    "282e2807-cbd9-43bb-89c2-edc3122fcc04",
    "b3a38989-fcf1-4569-b1a6-444e1cd583b6",
    "6b0b69bb-49d8-48a6-a740-8c354eb575ea",
    "6b0b69bb-49d8-48a6-a740-8c354eb575ea",
    "90c78c97-1b3b-444f-a3bb-9fb9dfdb3455",
    "7db061f0-361d-4148-a5ef-0a37b138f37c",
    "844d81c7-7027-44c3-899d-21e90a7e599d",
    "6b0b69bb-49d8-48a6-a740-8c354eb575ea"
  ],
  "includeDirectoryObjectReferences": true
}

In Azure Automation it's this last call (which gets user details for the user object IDs returned by the first call) that's failing:

Status Code:
Forbidden

Headers:
ocp-aad-diagnostics-server-name: CL43Rj9/Eg1H2aYXpRQBM3J4ssdQAWJx32NV+f61Yn4=
request-id                    : 0a184c82-1dd0-4f29-9192-aa746dff470a
client-request-id             : e94a29bf-9846-427e-b958-8a2f9d3a32e3
x-ms-dirapi-data-contract-version: 1.42-previewInternal
ocp-aad-session-key           : inmY52ItGZojOuCWsXYSgmNBsJZIQXlg8-l0FLzrvOnftsGwyOfzjmNtXMfLiXQqOp32Pk1n1GjvnvbJdT0Pak6d
7NwIFNIy0tUBep-rTRH__HFt_Kb_2ffSgOPlJSe-.ms4g4jKJV0H4VNYwMDPJljQFyNDjrw42XxrOKmkPXHU
Strict-Transport-Security     : max-age=31536000; includeSubDomains
Access-Control-Allow-Origin   : *
Duration                      : 1323869
Cache-Control                 : private
Server                        : Microsoft-IIS/8.5
X-AspNet-Version              : 4.0.30319
X-Powered-By                  : ASP.NET,ASP.NET
Date                          : Wed, 24 Feb 2016 02:45:55 GMT

Body:
{
  "odata.error": {
    "code": "Authentication_Unauthorized",
    "message": {
      "lang": "en",
      "value": "Access denied to the specified API version."
    }
  }
}

I see the exact same calls when running locally so I don't think it's a version issue. (I'm using the MSOnline module from the gallery.)

I'm thinking I need to grant some additional permissions to my service principal, but not sure which ones. I've added all of the following permissions and am still getting the same error:

Windows Azure Active Directory
App Permissions

Delegated Permissions

Windows Azure Service Management API
Delegated Permissions

Microsoft Graph
App Permissions

Upvotes: 2

Views: 2267

Answers (3)

BenV
BenV

Reputation: 12452

This is a known issue when using cmdlets that call the Graph API while authenticated with a service principal.

https://github.com/Azure/azure-powershell/issues/1896

Upvotes: 1

Jack Zeng
Jack Zeng

Reputation: 2267

I don't think a service principal would work in this case, because getting the list of administrators needs co-admin permission, which means you need to create a None-Microsoft-account and MFA-disabled user account to you default Active Directory (a service administrator would be ok, but you can have a global administrator if you want), and add it to your administrator of your subscription as a co-admin.

In your automation runbook, you need to login with credential.

For more information, see Configuring Azure Automation and Credential assets in Azure Automation





After a few digging on Graph API, I have found some strange behaviours going on for Get-AzureRmRoleAssignment. Here is the powerShell script I use to test the Graph API:

try{
    $subscription = Get-AzureRmSubscription
}
catch{
    Login-AzureRmAccount
    $subscription = Get-AzureRmSubscription
}

$tenantID = $subscription.TenantId
$authString = "https://login.microsoftonline.com/" + $tenantID

$clientId = "<The client id of your AD application>"
$key = "<The key of the above client>"

$authenticationContext = New-Object Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext ($authString, $false)

$clientCred = New-Object Microsoft.IdentityModel.Clients.ActiveDirectory.ClientCredential ($clientId, $key)

$resource = "https://graph.windows.net"

$authenticationResult = $authenticationContext.AcquireToken($resource, $clientCred);

$authHeader = $authenticationResult.AccessTokenType + " " + $authenticationResult.AccessToken

$headers = @{"Authorization"=$authHeader; "Content-Type"="application/json"}

$result = Invoke-RestMethod -Method Post -Uri "https://graph.windows.net/$tenantID/getObjectsByObjectIds?api-version=beta" -Headers $headers -InFile .\objectIds.json

$result.value

Note: in .\objectIds.json, please store the json request body you get from the debug mode.

With api-version=beta, in one subscription, it gives me error {"odata.error":{"code":"Authorization_RequestDenied","message":{"lang":"en","value":"Insufficient privileges to complete the operation."}}}, while in another subscription, it runs perfectly.

About the difference between this 2 subscriptions, I don't have permission to access the user list in the default directory of the first subscription; on the other hand, I do have access to the user list in the default directory of the second subscription.

Please try the script I provided and tell me your result.

Further more, if you only need the list of classic administrator, the following scripts is sufficient enough. The Above issue only happens when you try to get the role assignment.

$tenantID = "<the tenant id of your subscription>"
$authString = "https://login.windows.net/" + $tenantID

$clientId = "<the client id of your AD application>"
$key = "<the key of the above client>"

$resource = "https://management.core.windows.net/"

$secpasswd = ConvertTo-SecureString $key -AsPlainText -Force
$mycreds = New-Object System.Management.Automation.PSCredential ($clientId, $secpasswd)

try{
    $subscription = Get-AzureRmSubscription
}
catch{
    Login-AzureRmAccount -Credential $mycreds -Tenant $tenantID -ServicePrincipal
    $subscription = Get-AzureRmSubscription
}

$subscriptionId = $subscription.subscriptionId

$authenticationContext = New-Object Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext ($authString, $false)

$clientCred = New-Object Microsoft.IdentityModel.Clients.ActiveDirectory.ClientCredential ($clientId, $key)

$authenticationResult = $authenticationContext.AcquireToken($resource, $clientCred);

$authHeader = $authenticationResult.AccessTokenType + " " + $authenticationResult.AccessToken

$headers = @{"Authorization"=$authHeader; "Content-Type"="application/json"}

$result = Invoke-RestMethod -Method Get -Uri "https://management.azure.com/subscriptions/$subscriptionId/providers/Microsoft.Authorization/classicAdministrators?api-version=2015-06-01" -Headers $headers

$result.value

Upvotes: 0

Joe
Joe

Reputation: 2540

A service principal cannot be a classic administrator, but it can be an owner. Owner has the same permissions as a classic administrator. So setting the service principal as an owner on the subscription should solve the issue.

Upvotes: 0

Related Questions