4

Background

I am making a restful API call to Microsoft's Azure Consumption endpoint as detailed below.

https://docs.microsoft.com/en-gb/rest/api/consumption/reservationrecommendations/list

However I am always presented with the below error.

Authentication token doesn't have enrollment level access.

{
  "error": {
    "code": "401",
    "message": "Authentication token doesn't have enrollment level access. 
  }
}

The token is valid and can be used to access the other endpoints under the consumption API. The "Try It" test link on the Azure page actually returns a 200, however when I make the call I get a 401.

Question

Can anyone shed any light on this error message? I cannot find any help on this error anywhere.

Code

Authentication

https://docs.microsoft.com/en-gb/azure/active-directory/develop/v1-oauth2-client-creds-grant-flow#first-case-access-token-request-with-a-shared-secret

 private static string GetAccessToken(string clientId, string clientSecret, string tenantId)
    {

        HttpClient client = new HttpClient();
        client.DefaultRequestHeaders.TryAddWithoutValidation("Content-Type", "application/json;");

        string hostname = $"https://login.microsoftonline.com/{tenantId}/oauth2/token";

        var content = new FormUrlEncodedContent(new[]
        {
            new KeyValuePair<string, string>("grant_type", "client_credentials"),
            new KeyValuePair<string, string>("client_id", clientId),
            new KeyValuePair<string, string>("client_secret", clientSecret),
            new KeyValuePair<string, string>("resource", "https://management.azure.com/")
        });

        HttpResponseMessage httpResponse = client.PostAsync(hostname, content).Result;
        var responseString = httpResponse.Content.ReadAsStringAsync();

        if (httpResponse.StatusCode == HttpStatusCode.OK)
        {
            dynamic tokenObject = JsonConvert.DeserializeObject(responseString.Result);

            return tokenObject.access_token;
        }
        else
        {
            return null;
        }
    }

API Call

 public static dynamic GetReservationRecommendations(Params parameters)
 {
   var token = GetAccessToken(parameters.ClientId, parameters.ClientSecret, parameters.TenantId);

     HttpClient client = new HttpClient();
     client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("bearer", token);
     client.DefaultRequestHeaders.TryAddWithoutValidation("Content-Type", "application/json;");

     string hostname = $"https://management.azure.com/subscriptions/{parameters.SubscriptionId}/providers/Microsoft.Consumption/reservationRecommendations?api-version=2018-10-01";

     HttpResponseMessage httpResponse = client.GetAsync(hostname).Result;
     var responseString = httpResponse.Content.ReadAsStringAsync();

     if (httpResponse.StatusCode == HttpStatusCode.OK)
     {
         return responseString.Result;
     }
     else
     {
         return null;
     }
 }
Dan Cundy
  • 2,245
  • 1
  • 27
  • 57
  • Does it work with `Try it` in the link you mentioned? – Joy Wang Nov 30 '18 at 01:18
  • Could you show more details code about how to generate token? – Joey Cai Nov 30 '18 at 06:53
  • @JoyWang yes it does. – Dan Cundy Dec 03 '18 at 13:28
  • @JoeyCai added code snippet. – Dan Cundy Dec 03 '18 at 13:28
  • 2
    Seems like the users authenticating with this token doesn't the correct permissions to perform this action – Luc Dec 03 '18 at 14:05
  • @DanCundy I've seen odd behavior in the past where consumption metrics were only available to our original user that had registered our Azure subscription, despite them ostensibly having the same permissions as our other admins. This was for the Enterprise Access portal frontend though, so it might not apply, but it seems like there's some additional layer of access beyond that in Azure user management. – UpQuark Dec 05 '18 at 19:51
  • @UpQuark Yes its very strange behaviour. I've turned to using the Billing API with the EA credentials for the minute, but would prefer to use the consumption API for all. – Dan Cundy Dec 05 '18 at 20:12

1 Answers1

0

Reasoning for Error

The application identity you are using to acquire token does not have enough permissions for Consumption API - Reservation Recommendations List

Try it test link works but code doesn't

AFAIK Try it link will ask you to sign in with an account in browser first. So it makes use of the user identity and not the application identity. So it's possible that user that you are testing with, has high enough permissions/role but the code of course is using clientId and clientSecret, so it can still fail unless application gets all required permissions.

Required permissions

  1. This API makes use of ARM permissions, so your application service principal needs to be given permissions. At least "Cost Management Reader" role. (You may have done these since you mention some other endpoints work for you)

    In Azure Portal, go to Subscriptions > Your Subscription > IAM

    enter image description here

    Then add the role assignment for service principal of your application

    enter image description here

  2. Looking at error message "Authentication token doesn't have enrollment level access.", I think your Azure Subscription is under EA (i.e. Enterprise Agreement). I say this because I was able to repro the exact same error message only in an EA subscription and not in another regular Pay-as-you-go subscription, when service principal already had "Cost Management Reader" role. If your subscription is under EA, then follow the further steps as well.

    So permissions need to be granted in Azure portal and Enterprise Portal (EA portal). Look at this Microsoft Documentation for exact details. Assign access to Cost Management data

    enter image description here

    enter image description here

    Please follow this documentation for EA portal related steps. Enable access to costs in the EA portal enter image description here

Rohit Saigal
  • 7,931
  • 2
  • 9
  • 25
  • I am unsure this is the problem exactly, as I have both highlighted sections in the EA portal enabled. my application service principal is also a reader of billing and has access to the management API. I'm investigating giving the application service principal an owner with appropriate privileges, but haven't got around to testing this idea yet. – Dan Cundy Dec 10 '18 at 11:18