Search code examples
azureazure-resource-managerazure-sdk-.netazure-rest-api

Call the Azure Resource Graph API from .NET Application


I am trying to author an application that allows a user to query a list of websites from their Azure subscription from within my application. I do not want to store their credentials in my app (nor should they want me to), but instead I would ask them them to register an Application from within their Azure AD instance, and then have them store the ClientID and TenantID generated in my app. I would give them a set of instructions on how to do this from their Azure Portal that uses the most restrictive permissions possible, and that they could turn off at anytime. They need to feel comfortable doing this, as I would.

I am trying to follow the plot of this article: Using Azure resource graph with .net SDK

It gets me very close, but when I run the app I get a "Forbidden" response from the Azure Resource Graph, so the call is getting through but is being rejected. I have also tried to add various API Permissions using my own Azure Portal for testing. The article says that I need to create a service principal using:

az ad sp create-for-rbac -n "MyApp" --role contributor --sdk-auth

Which I have not done, yet.

So my questions are:

  • Is this my problem?

  • Do I need to ask my user to create a service principal, and if so, what value goes into the "MyApp" variable in the example above?

  • Is there a better approach to doing this that expects less of the user?

I appreciate any guidance or article referrals anyone can provide.

Many thanks....


Solution

  • If you want to call Azure resource graph Rest API with service principal, you must assign Azure RABC Role to the sp. So you must run the following command

    # it will create a service pricipal and assign a contributor rolen to the sp
    az ad sp create-for-rbac -n "MyApp"  --scope "/subscriptions/<subscription id>" --sdk-auth
    
     
    # if you have a service principal, please run the following script
    az ad sp list --display-name "{name}" --query [].objectId --output tsv
    az role assignment create --assignee <sp object id> --role "Contributor " --scope "/subscriptions/<subscription id>"
    

    Meanwhile, the detailed steps about how to run the sample are as below

    1. Run the following script.
    az login
    az ad sp create-for-rbac -n "MyApp"  --scope "/subscriptions/<subscription id>" --sdk-auth
    

    enter image description here

    1. Configure Code. Please note that if you want to call the resource in the different subscriptions, you need to create different service principal by changing the scope value in step1
    public  async static Task Test() {
    
    
                CustomLoginCredentials creds = new CustomLoginCredentials();
    
                var resourceGraphClient = new ResourceGraphClient(creds);
    
                var queryReq = new QueryRequest {
    
                    Subscriptions = new List<string> { "<the subscriptionId you copy>" },
                    Query = "where type == 'microsoft.web/sites'"
    
                };
                var result = await resourceGraphClient.ResourcesAsync(queryReq);
                Console.WriteLine(result.Count);
            }
    
    class CustomLoginCredentials : ServiceClientCredentials {
            private static string tenantId = "<the tenantId you copy >";
            private static string clientId = "<the clientId you copy>";
            private static string cert = "<the clientSecre tyou copy>";
            private string AuthenticationToken { get; set; }
            public override void InitializeServiceClient<T>(ServiceClient<T> client)
            {
                var authenticationContext =
                    new AuthenticationContext("https://login.microsoftonline.com/"+tenantId);
                var credential = new ClientCredential(clientId: clientId, clientSecret: cert);
    
                var result = authenticationContext.AcquireTokenAsync(resource: "https://management.azure.com/",
                    clientCredential: credential).Result;
    
                if (result == null)
                {
                    throw new InvalidOperationException("Failed to obtain the JWT token");
                }
    
                AuthenticationToken = result.AccessToken;
            }
            public override async Task ProcessHttpRequestAsync(HttpRequestMessage request, CancellationToken cancellationToken)
            {
                if (request == null)
                {
                    throw new ArgumentNullException("request");
                }
    
                if (AuthenticationToken == null)
                {
                    throw new InvalidOperationException("Token Provider Cannot Be Null");
                }
    
    
    
                request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", AuthenticationToken);
    
    
    
                await base.ProcessHttpRequestAsync(request, cancellationToken);
    
            }
    

    For more details, please refer to

    https://odetocode.com/blogs/scott/archive/2018/02/01/setting-up-service-principals-to-use-the-azure-management-apis.aspx

    https://learn.microsoft.com/en-us/cli/azure/ad/sp?view=azure-cli-latest#az-ad-sp-create


    update

    According to your needs, I think you can create a web application to call Azure Resource Graph API. If you do that, you can provide the url of web app then your customers can login your application and call the API by themselves.

    1. Register Azure AD web application enter image description here

    2.Configure permissions enter image description here

    1. Create client secret enter image description here

    2. Configure code. Please refer to the sample