Azure Advisor recommendation using Resource Manager REST API

Introduction

Advisor is a digital cloud assistant designed to help you optimize your Azure deployments by following best practices. It analyzes your resource configurations and usage telemetry, offering recommendations to enhance the cost-effectiveness, performance, reliability, and security of your Azure resources.

In this article, we are going to see how to pull the Azure Advisor report, so that you can extend the functionality to download and send an alert to the respective resource owner.

In my last blog, I used Microsoft Graph API to get a Microsoft Entra ID application. Now, we are trying to extend the functionality to get the Azure advisor report and send it to the respective resource owner.

Configure the Application Permission

To read the advisor report, the Microsoft Entra ID application at least needs reader permission at the subscription level.

Step 1. Log in to the Azure portal as a global admin.

Step 2. Search for the subscription, go to a subscription page, select Access Control -> Add -> Add role assignment -> reader -> select the application from members.

 Role Assignment

Resource Manager REST API

The below Azure resource manager REST API should be used to get the advisor recommendation.

https://management.azure.com/subscriptions/{subscriptionId}/providers/Microsoft.Advisor/recommendations?api-version={apiVersion}";

public async Task<List<Properties>> GetAdvisorRecommendation()
{
    var accessToken = await GetAccessToken();
    var subscriptionId = configuration["AzureConfig:SubscriptionId"];
    var apiVersion= configuration["AzureConfig:ApiVersion"];
    var resourceUri = $"https://management.azure.com/subscriptions/{subscriptionId}/providers/Microsoft.Advisor/recommendations?api-version={apiVersion}";
    var response = await CallApiWithAccessToken(resourceUri, accessToken);
    var properties = new List<Properties>();
    foreach (var item in response.Values)
    {
            var resourcegroupArray = item.Properties.ResourceMetadata.ResourceId.Split("/");
            if (resourcegroupArray.Length > 4)
            {
                item.Properties.resoureGroupName = resourcegroupArray[4];
            }
            else
            {
                item.Properties.resoureGroupName = string.Empty;
            }
            properties.Add(new Properties
            {
                Impact = item.Properties.Impact,
                ImpactedField = item.Properties.ImpactedField,
                ImpactedValue = item.Properties.ImpactedValue,
                ShortDescription = item.Properties.ShortDescription,
                resoureGroupName = item.Properties.resoureGroupName,
                Category = item.Properties.Category
            });

    }
    return properties;
}

GetAdvisorRecommendation()

This function pulls the advisor recommendation using the API.

You can write a data file query to return only the cost recommendation, as given below,

https://management.azure.com/subscriptions/{subscriptionId}/providers/Microsoft.Advisor/recommendations?api-version={apiVersion}&$filter=Category eq 'cost'

  private async Task<Advisor> CallApiWithAccessToken(string url, string accessToken)
  {
      var httpClient = _httpClientFactory.CreateClient();
      httpClient.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", accessToken);
      var response = await httpClient.GetAsync(url);
      response.EnsureSuccessStatusCode();
      var content = await response.Content.ReadAsStringAsync();
      string jsonString = content;
      var options = new JsonSerializerOptions
      {
          PropertyNameCaseInsensitive = true
      };
      var result = System.Text.Json.JsonSerializer.Deserialize<Advisor>(jsonString, options);
      if (result == null)
      {
          // Handle the case where the result is null
          // You can throw an exception, return a default value, or take any other appropriate action
          throw new Exception("API response returned null");
      }
      return result;
  }

Advisor. cs

 public class Advisor
 {
     [JsonPropertyName("value")]
     public List<Value> Values { get; set; }
 }
 public class Value
 {
     [JsonPropertyName("properties")]
     public Properties Properties { get; set; }
     [JsonPropertyName("id")]
     public string Id { get; set; }
     [JsonPropertyName("type")]
     public string Type { get; set; }
     [JsonPropertyName("name")]
     public string Name { get; set; }
 }
 public class Properties
 {
     [JsonPropertyName("category")]
     public string Category { get; set; }
     [JsonPropertyName("impact")]
     public string Impact { get; set; }
     [JsonPropertyName("impactedField")]
     public string ImpactedField { get; set; }
     [JsonPropertyName("impactedValue")]
     public string ImpactedValue { get; set; }
     [JsonPropertyName("lastUpdated")]
     public DateTime LastUpdated { get; set; }
     [JsonPropertyName("recommendationTypeId")]
     public string RecommendationTypeId { get; set; }
     [JsonPropertyName("shortDescription")]
     public ShortDescription ShortDescription { get; set; }
     [JsonPropertyName("extendedProperties")]
     public ExtendedProperties ExtendedProperties { get; set; }
     [JsonPropertyName("resourceMetadata")]
     public ResourceMetadata ResourceMetadata { get; set; }
     public string resoureGroupName{ get; set; }
 }
 public class ShortDescription
 {
     [JsonPropertyName("problem")]
     public string Problem { get; set; }

     [JsonPropertyName("solution")]
     public string Solution { get; set; }
 }
 public class ExtendedProperties
 {
     [JsonPropertyName("recommendationControl")]
     public string RecommendationControl { get; set; }
     [JsonPropertyName("maturityLevel")]
     public string MaturityLevel { get; set; }
     [JsonPropertyName("language")]
     public string Language { get; set; }
     [JsonPropertyName("version")]
     public string Version { get; set; }
     [JsonPropertyName("recommendedActionLearnMore")]
     public string RecommendedActionLearnMore { get; set; }
     [JsonPropertyName("releaseNotes")]
     public string ReleaseNotes { get; set; }

     [JsonPropertyName("region")]
     public string Region { get; set; }
 }
 public class ResourceMetadata
 {
     [JsonPropertyName("resourceId")]
     public string ResourceId { get; set; }
 }

appsettings.json

{
  "AzureAd": {
    "Instance": "https://login.microsoftonline.com/",
    "Domain": "[your domain name]",
    "TenantId": "[your tenant id]",
    "ClientId": "[your entra Id application client id]",
    "CallbackPath": "/signin-oidc",
    "Scopes": "user.read",
    "ClientCertificates": []
  },
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
 
  "AllowedHosts": "*",
  "MicrosoftGraph": {
    "BaseUrl": "https://graph.microsoft.com/v1.0",
    "Scopes": "user.read application.read.all"
  },
  "AzureConfig": {
    "SubscriptionId": "[your subscription id]",
    "authority": "https://login.microsoftonline.com/common/oauth2/authorize",
    "ApiVersion": "2023-01-01",
    "resource": "https://management.azure.com/.default"
  }

  }
    private async Task<string> GetAccessToken()
    {
        var clientId = configuration["AzureAd:ClientId"];
        var clientSecret = configuration["AzureAd:ClientSecret"];
        var authority = configuration["AzureAd:authority"]; 
        var resource = configuration["MicrosoftGraph:resource"]; 
        var authorityURI = new Uri($"{configuration["AzureAd:Instance"]}{configuration["AzureAd:TenantId"]}");

        var confidentialClientApplication = ConfidentialClientApplicationBuilder
            .Create(clientId)
            .WithClientSecret(clientSecret)
            .WithAuthority(authorityURI)
            .Build();

        var authResult = await confidentialClientApplication
            .AcquireTokenForClient(new[] { resource })
            .ExecuteAsync();

        return authResult.AccessToken;
        //  return accessToken;
    }
  1. GetAccessToken(): user-defined function will get an access token using client credential flow, make sure the resource - https://management.azure.com/.default has been defined to set an access token with the scope to access Azure managed services
  2. resourceURI: The endpoint used to retrieve the advisor report.
  3. CallApiWithAccessToken(): This user function is used to get an advisor report using a resource URI with the access token.
    ResourceURI

Summary

We saw how to use the Azure resource manager REST API to pull the advisor recommendation and to filter the API using the odata query to retrieve a particular category. From here you can extended its functionality and build your own custom system to montior and automate it to reduce more manually intervention and efforts.


Similar Articles