azureazure-resource-graph

Azure resource graph SDK for compliance score


I am trying to run the compliance score by resource type query using Resource Graph SDK .net. These are the nuget packages I have added.

<PackageReference Include="Azure.ResourceManager" Version="1.13.2" />
<PackageReference Include="Azure.ResourceManager.Purview" Version="1.1.0" />
<PackageReference Include="Azure.ResourceManager.ResourceGraph" Version="1.0.1" />

I have created a service class and then calling that method in my controller to get the API response. Here is the service class

public class ComplianceService
{
    private readonly ArmClient _armClient;
    public ComplianceService(string tenantId, string clientId, string clientSecret)
    {
        var credential = new ClientSecretCredential(tenantId, clientId, clientSecret);
        _armClient = new ArmClient(credential);
    }
    public async Task<QueryResponse> GetComplianceScoreAsync()
    {
        var subscriptionIds = new List<string>();
        await foreach (var sub in _armClient.GetSubscriptions().GetAllAsync())
        {
            subscriptionIds.Add(sub.Data.SubscriptionId);
        }

        if (subscriptionIds.Count == 0)
        {
            throw new Exception("No subscriptions found.");
        }

    var query = @"My query";

        var request = new ResourceQueryContent(query)
        {
            Subscriptions = { subscriptionIds.ToString() }
        };

    .................

        return response;
    }
}

Now I am stuck on the part where I don't know how to call the query and get the response. Also, I want to get all the subscriptions dynamically. I am using .Net core 8+ I am new to running Azure SDK's and running resource graph queries. What can I try next?


Solution

  • there are some issues with your code, here is the updated code

    using Azure.Identity;
    using Azure.ResourceManager;
    using Azure.ResourceManager.ResourceGraph;
    using Azure.ResourceManager.ResourceGraph.Models;
    using Azure.ResourceManager.Resources;
    
    public class ComplianceService
    {
        private readonly ArmClient _armClient;
        
        public ComplianceService(string tenantId, string clientId, string clientSecret)
        {
            var credential = new ClientSecretCredential(tenantId, clientId, clientSecret);
            _armClient = new ArmClient(credential);
        }
        
        public async Task<ResourceQueryResult> GetComplianceScoreAsync()
        {
            // Get the tenant resource - this is the key to accessing subscriptions and running queries
            var tenant = _armClient.GetTenants().GetAllAsync().GetAsyncEnumerator().Current;
            
            // Get all subscriptions dynamically
            var subscriptionIds = new List<string>();
            await foreach (var subscription in tenant.GetSubscriptions().GetAllAsync())
            {
                subscriptionIds.Add(subscription.Data.SubscriptionId);
            }
    
            if (subscriptionIds.Count == 0)
            {
                throw new Exception("No subscriptions found.");
            }
    
            // Your compliance score query
            var query = @"
                PolicyResources
                | where type =~ 'Microsoft.PolicyInsights/PolicyStates'
                | extend complianceState = tostring(properties.complianceState)
                | extend resourceType = tostring(properties.resourceType)
                | where complianceState in ('Compliant', 'NonCompliant')
                | summarize 
                    Total = count(),
                    Compliant = countif(complianceState == 'Compliant'),
                    NonCompliant = countif(complianceState == 'NonCompliant')
                    by resourceType
                | extend CompliancePercentage = round((Compliant * 100.0) / Total, 2)
                | project resourceType, Total, Compliant, NonCompliant, CompliancePercentage
                | order by resourceType asc";
    
            // Create the query request
            var request = new ResourceQueryContent(query);
            
            // Add each subscription ID individually to the request
            foreach (var subscriptionId in subscriptionIds)
            {
                request.Subscriptions.Add(subscriptionId);
            }
            
            // Optional: Add other query options
            request.Options = new ResourceQueryRequestOptions
            {
                Top = 1000, // Limit results if needed
                Skip = 0    // For pagination
            };
    
            try
            {
                // Execute the query using the tenant resource
                var response = await tenant.GetResourcesAsync(request);
                return response.Value;
            }
            catch (Exception ex)
            {
                throw new Exception($"Failed to execute Resource Graph query: {ex.Message}", ex);
            }
        }
        
        // Alternative method to get subscriptions only (useful for testing)
        public async Task<List<string>> GetAllSubscriptionIdsAsync()
        {
            var tenant = _armClient.GetTenants().GetAllAsync().GetAsyncEnumerator().Current;
            var subscriptionIds = new List<string>();
            
            await foreach (var subscription in tenant.GetSubscriptions().GetAllAsync())
            {
                subscriptionIds.Add(subscription.Data.SubscriptionId);
            }
            
            return subscriptionIds;
        }
    }
    

    Make sure you have setup proper controller

    [ApiController]
    [Route("api/[controller]")]
    public class ComplianceController : ControllerBase
    {
        [HttpGet("score")]
        public async Task<IActionResult> GetComplianceScore()
        {
            try
            {
                var service = new ComplianceService(
                    tenantId: "your-tenant-id",
                    clientId: "your-client-id", 
                    clientSecret: "your-client-secret"
                );
                
                var result = await service.GetComplianceScoreAsync();
                
                // Process the results
                var complianceData = new List<object>();
                
                foreach (var row in result.Data.Rows)
                {
                    complianceData.Add(new
                    {
                        ResourceType = row[0]?.ToString(),
                        Total = Convert.ToInt32(row[1]),
                        Compliant = Convert.ToInt32(row[2]),
                        NonCompliant = Convert.ToInt32(row[3]),
                        CompliancePercentage = Convert.ToDouble(row[4])
                    });
                }
                
                return Ok(new { 
                    TotalRecords = result.TotalRecords,
                    Data = complianceData 
                });
            }
            catch (Exception ex)
            {
                return BadRequest($"Error: {ex.Message}");
            }
        }
    }