SOLVED

How to list all resources covered/not covered by Azure Defender using REST API ?

Copper Contributor

I want to display Defender coverage in custom application like following image::

 

Anil_Guliyaan_0-1627624999247.png

Please help?

4 Replies
best response confirmed by Anil_Guliyaan (Copper Contributor)
Solution

@Anil_Guliyaan 

 

There is a query from the "Inventory Blade" in ASC, you can amend it (I made a start below on a recent project).  This is using ARG rather than the REST api directly.  You should be able to see the api info if you need to use that.

securityresources
//| where subscriptionId == "< insert you id here >"
| where type =~ "microsoft.security/assessments" or type =~ "microsoft.security/softwareInventories"
| extend assessmentStatusCode = case(type =~ "microsoft.security/assessments", tostring(properties.status.code), "")
| extend severity = case(assessmentStatusCode =~ "unhealthy", tolower(tostring(properties.metadata.severity)), tolower(assessmentStatusCode))
| extend exemptionType = case(tolower(type) != "microsoft.security/assessments","N/A", case(properties.status.cause =~ "exempt", "Yes", "No"))
| extend source = case(type =~ "microsoft.security/assessments", tostring(properties.resourceDetails.Source), "")
| extend resourceId = trim(" ", tolower(tostring(case(source =~ "azure", properties.resourceDetails.Id,
                                                                            source =~ "aws", properties.resourceDetails.AzureResourceId,
                                                                            source =~ "gcp", properties.resourceDetails.AzureResourceId,
                                                                            type =~ "microsoft.security/assessments", extract("^(.+)/providers/Microsoft.Security/assessments/.+$",1,id),extract("^(.+)/providers/Microsoft.Security/softwareInventories/.+$",1,id)))))
| extend resourceName = extract(@"(.+)/(.+)", 2, resourceId)
| extend regexResourceId = extract_all(@"/providers/([^/]+)(?:/([^/]+)/[^/]+(?:/([^/]+)/[^/]+)?)?/([^/]+)/[^/]+$", resourceId)
| extend RegexResourceType = regexResourceId[0]
| extend mainType = RegexResourceType[1], extendedType = RegexResourceType[2], resourceType = RegexResourceType[3]
| extend providerName = RegexResourceType[0],
                        mainType = case(mainType !~ "", strcat("/",mainType), ""),
                        extendedType = case(extendedType!~ "", strcat("/",extendedType), ""),
                        resourceType = case(resourceType!~ "", strcat("/",resourceType), "")
| extend array = split(resourceId, '/')
| extend typeFullPath = case(array_length(array) == 3,  'subscription', strcat(providerName, mainType, extendedType, resourceType))
| extend typeFullPath = case(array_length(array) == 5, 'resourcegroups', typeFullPath)
| extend resourceType = case(typeFullPath =~ 'resourcegroups' or typeFullPath =~ 'subscription', typeFullPath, tolower(trim("/", resourceType)))
| extend assessmentKey = case(type =~ "microsoft.security/assessments", tostring(name), "")
| extend softwareVendorName = case(type =~ "microsoft.security/softwareInventories", tostring(properties.vendor), "")
| extend softwareName = case(type =~ "microsoft.security/softwareInventories", tostring(properties.softwareName), "")
| extend softwareNameIdentifier = case(type =~ "microsoft.security/softwareInventories", strcat(softwareVendorName, ",", softwareName), "")
| extend environment = case(type =~ "microsoft.security/assessments", properties.resourceDetails["Source"], "")
| extend environment = case(environment =~ "onpremise", tolower("Non-Azure"), tolower(environment))
| extend osTypeProperty = properties.additionalData["OS Type"]
| extend osType = case(isnotempty(osTypeProperty), osTypeProperty, "")
| extend hasAgent = case(assessmentKey == "d1db3318-01ff-16de-29eb-28b344515626" or assessmentKey == "45cfe080-ceb1-a91e-9743-71551ed24e94" or assessmentKey == "720a3e77-0b9a-4fa9-98b6-ddf0fd7e32c1" or assessmentKey == "27ac71b1-75c5-41c2-adc2-858f5db45b08", assessmentStatusCode, "")
| extend workspaceAzureResourceId = case(hasAgent !~ "", properties.additionalData["Reporting workspace azure id"], "")
| extend workspaceName = case(workspaceAzureResourceId !~ "", extract(@"(.+)/(.+)", 2, workspaceAzureResourceId), "")
| extend assessmentDisplayName = case(type =~ "microsoft.security/assessments", case(isnotempty(properties.displayName), properties.displayName, properties.metadata.displayName), "")
| extend assessmentIdentifier = case(type =~ "microsoft.security/assessments", strcat(assessmentKey, "," , assessmentDisplayName, ",", severity), "")
| summarize assessmentsCount = count() , assessmentsIdentifier = make_list(assessmentIdentifier), softwareNamesIdentifier = make_list(softwareNameIdentifier), hasAgent = max(hasAgent), workspaceName = max(workspaceName), environment = max(environment), osType = max(osType), exemptionType = max(exemptionType)  by resourceId, subscriptionId, resourceName, resourceType, typeFullPath, severity
| extend packAssessments = pack(severity, assessmentsCount)
| summarize assessmentsSummary = make_bag(packAssessments), assessmentsIdentifier = make_set(assessmentsIdentifier), softwareNamesIdentifier = make_set(softwareNamesIdentifier), hasAgent = max(hasAgent), workspaceName= max(workspaceName), environment = max(environment), osType= max(osType), exemptionType = max(exemptionType)  by resourceId, subscriptionId, resourceName, resourceType, typeFullPath
| extend agentMonitoring = case(hasAgent =~ "NotApplicable" or hasAgent =~ "", '',
                                                hasAgent =~ "Unhealthy", "notInstalled",
                                                "installed")
| join kind=leftouter (
                    securityresources
                    | where type =~ "microsoft.security/pricings"
                    | project subscriptionId, bundleName = tolower(name), freeTrialRemainingTime = properties.freeTrialRemainingTime, pricingTier = tolower(properties.pricingTier)
                    | extend bundlesPricing = pack(bundleName, pricingTier)
                    | summarize subscriptionPricing = make_bag(bundlesPricing) by subscriptionId
                ) on subscriptionId
| extend hasNoSoftwareData = case(array_length(softwareNamesIdentifier) == 1, case(set_has_element(softwareNamesIdentifier, ""), true, false), false)
| extend softwareNamesIdentifier = case(hasNoSoftwareData, softwareNamesIdentifier, set_difference(softwareNamesIdentifier, pack_array("")))
| extend AssessmentsHigh = case(isnull(assessmentsSummary.high), 0 , toint(assessmentsSummary.high))
| extend AssessmentsMedium = case(isnull(assessmentsSummary.medium), 0 , toint(assessmentsSummary.medium))
| extend AssessmentsLow = case(isnull(assessmentsSummary.low), 0 , toint(assessmentsSummary.low))
| extend unhealthyAssessmentsCount = AssessmentsHigh + AssessmentsMedium + AssessmentsLow
| extend virtualmachines = case(isnull(subscriptionPricing), '' , subscriptionPricing.virtualmachines)
| extend virtualmachines = case(virtualmachines == 'free', 'off', 'on')
| extend sqlservers = case(isnull(subscriptionPricing), '' , subscriptionPricing.sqlservers)
| extend sqlservers = case(sqlservers == 'free', 'off', 'on')
| extend kubernetesservice = case(isnull(subscriptionPricing), '' , subscriptionPricing.kubernetesservice)
| extend kubernetesservice = case(kubernetesservice == 'free', 'off', 'on')
| extend containerregistry = case(isnull(subscriptionPricing), '' , subscriptionPricing.containerregistry)
| extend containerregistry = case(containerregistry == 'free', 'off', 'on')
| extend connectedcontainerregistry = case(isnull(subscriptionPricing), '' , subscriptionPricing.connectedcontainerregistry)
| extend connectedcontainerregistry = case(connectedcontainerregistry == 'free', 'off', 'on')
| extend sqlservervirtualmachines = case(isnull(subscriptionPricing), '' , subscriptionPricing.sqlservervirtualmachines)
| extend sqlservervirtualmachines = case(sqlservervirtualmachines == 'free', 'off', 'on')
| extend appservices = case(isnull(subscriptionPricing), '' , subscriptionPricing.appservices)
| extend appservices = case(appservices == 'free', 'off', 'on')
| extend storageaccounts = case(isnull(subscriptionPricing), '' , subscriptionPricing.storageaccounts)
| extend storageaccounts = case(storageaccounts == 'free', 'off', 'on')
| extend keyvaults = case(isnull(subscriptionPricing), '' , subscriptionPricing.keyvaults)
| extend keyvaults = case(keyvaults == 'free', 'off', 'on')
| extend opensourcerelationaldatabases = case(isnull(subscriptionPricing), '' , subscriptionPricing.opensourcerelationaldatabases)
| extend opensourcerelationaldatabases = case(opensourcerelationaldatabases == 'free', 'off', 'on')
| extend calculatedSubscriptionPricing = case(resourceType =~ "subscription" and isempty(subscriptionPricing) == false , iff(subscriptionPricing has "free" and subscriptionPricing has "standard", "partial", iff(subscriptionPricing has "free", "off", "on")), "")
| extend resourcePricing = case(typeFullPath =~ "microsoft.classiccompute/virtualmachines", virtualmachines, typeFullPath =~ "microsoft.compute/virtualmachines", virtualmachines, typeFullPath =~ "microsoft.hybridcompute/machines", virtualmachines, typeFullPath =~ "microsoft.sql/servers", sqlservers, typeFullPath =~ "microsoft.containerservice/managedclusters", kubernetesservice, typeFullPath =~ "microsoft.kubernetes/connectedclusters", kubernetesservice, typeFullPath =~ "microsoft.containerregistry/registries", containerregistry, typeFullPath =~ "microsoft.security/connectedcontainerregistries", connectedcontainerregistry, typeFullPath =~ "microsoft.sqlvirtualmachine/sqlvirtualmachines", sqlservervirtualmachines, typeFullPath =~ "microsoft.web/sites", appservices, typeFullPath =~ "microsoft.storage/storageaccounts", storageaccounts, typeFullPath =~ "microsoft.compute/virtualmachinescalesets", virtualmachines, typeFullPath =~ "microsoft.keyvault/vaults", keyvaults, typeFullPath =~ "microsoft.dbforpostgresql/servers", opensourcerelationaldatabases, typeFullPath =~ "microsoft.dbformysql/servers", opensourcerelationaldatabases, typeFullPath =~ "microsoft.dbformariadb/servers", opensourcerelationaldatabases, calculatedSubscriptionPricing)
| extend pricing = case(resourceType =~ "subscription" , calculatedSubscriptionPricing , resourcePricing)
| project resourceType, exemptionType, typeFullPath, resourceId, resourceName, subscriptionId, environment, osType, workspaceName, agentMonitoring, assessmentsIdentifier, assessmentsSummary, subscriptionPricing, unhealthyAssessmentsCount, pricing, softwareNamesIdentifier
| extend resourceGroup = tolower(tostring(split(resourceId, "/")[4]))
| order by unhealthyAssessmentsCount, subscriptionId, resourceType, resourceId
| where isnotempty(resourceId)
| extend resourceType = iff(resourceType == 'servers','SQL Server',resourceType)
| extend resourceType = iff(resourceType == 'machines','Hybrid Server',resourceType)
| summarize on_=countif(pricing == "on"), off_=countif(isempty(pricing)), part_=countif(pricing == "partial") by resourceType | order by on_ desc

  
Screenshot 2021-07-30 162029.png

Thanks @CliveWatson ,

 

for your quick reply

 

Can we use ARG query in REST API calls? any help?

ARG is using the REST Api (under the covers), but you are using KQL in Azure Resource Graph(ARG). If you want to use the REST Api, you need to use a tools like Postman or even a Azure Monitor Workbook that can run KQL, ARG and ARM (for REST api queries). ARG supports a subset of the full REST api in Azure, so you may be able to get the data you need with ARG or maybe need to use ARG and api calls.
Thanks @CliveWatson ,
I am able to use ARG query through API calls.
1 best response

Accepted Solutions
best response confirmed by Anil_Guliyaan (Copper Contributor)
Solution

@Anil_Guliyaan 

 

There is a query from the "Inventory Blade" in ASC, you can amend it (I made a start below on a recent project).  This is using ARG rather than the REST api directly.  You should be able to see the api info if you need to use that.

securityresources
//| where subscriptionId == "< insert you id here >"
| where type =~ "microsoft.security/assessments" or type =~ "microsoft.security/softwareInventories"
| extend assessmentStatusCode = case(type =~ "microsoft.security/assessments", tostring(properties.status.code), "")
| extend severity = case(assessmentStatusCode =~ "unhealthy", tolower(tostring(properties.metadata.severity)), tolower(assessmentStatusCode))
| extend exemptionType = case(tolower(type) != "microsoft.security/assessments","N/A", case(properties.status.cause =~ "exempt", "Yes", "No"))
| extend source = case(type =~ "microsoft.security/assessments", tostring(properties.resourceDetails.Source), "")
| extend resourceId = trim(" ", tolower(tostring(case(source =~ "azure", properties.resourceDetails.Id,
                                                                            source =~ "aws", properties.resourceDetails.AzureResourceId,
                                                                            source =~ "gcp", properties.resourceDetails.AzureResourceId,
                                                                            type =~ "microsoft.security/assessments", extract("^(.+)/providers/Microsoft.Security/assessments/.+$",1,id),extract("^(.+)/providers/Microsoft.Security/softwareInventories/.+$",1,id)))))
| extend resourceName = extract(@"(.+)/(.+)", 2, resourceId)
| extend regexResourceId = extract_all(@"/providers/([^/]+)(?:/([^/]+)/[^/]+(?:/([^/]+)/[^/]+)?)?/([^/]+)/[^/]+$", resourceId)
| extend RegexResourceType = regexResourceId[0]
| extend mainType = RegexResourceType[1], extendedType = RegexResourceType[2], resourceType = RegexResourceType[3]
| extend providerName = RegexResourceType[0],
                        mainType = case(mainType !~ "", strcat("/",mainType), ""),
                        extendedType = case(extendedType!~ "", strcat("/",extendedType), ""),
                        resourceType = case(resourceType!~ "", strcat("/",resourceType), "")
| extend array = split(resourceId, '/')
| extend typeFullPath = case(array_length(array) == 3,  'subscription', strcat(providerName, mainType, extendedType, resourceType))
| extend typeFullPath = case(array_length(array) == 5, 'resourcegroups', typeFullPath)
| extend resourceType = case(typeFullPath =~ 'resourcegroups' or typeFullPath =~ 'subscription', typeFullPath, tolower(trim("/", resourceType)))
| extend assessmentKey = case(type =~ "microsoft.security/assessments", tostring(name), "")
| extend softwareVendorName = case(type =~ "microsoft.security/softwareInventories", tostring(properties.vendor), "")
| extend softwareName = case(type =~ "microsoft.security/softwareInventories", tostring(properties.softwareName), "")
| extend softwareNameIdentifier = case(type =~ "microsoft.security/softwareInventories", strcat(softwareVendorName, ",", softwareName), "")
| extend environment = case(type =~ "microsoft.security/assessments", properties.resourceDetails["Source"], "")
| extend environment = case(environment =~ "onpremise", tolower("Non-Azure"), tolower(environment))
| extend osTypeProperty = properties.additionalData["OS Type"]
| extend osType = case(isnotempty(osTypeProperty), osTypeProperty, "")
| extend hasAgent = case(assessmentKey == "d1db3318-01ff-16de-29eb-28b344515626" or assessmentKey == "45cfe080-ceb1-a91e-9743-71551ed24e94" or assessmentKey == "720a3e77-0b9a-4fa9-98b6-ddf0fd7e32c1" or assessmentKey == "27ac71b1-75c5-41c2-adc2-858f5db45b08", assessmentStatusCode, "")
| extend workspaceAzureResourceId = case(hasAgent !~ "", properties.additionalData["Reporting workspace azure id"], "")
| extend workspaceName = case(workspaceAzureResourceId !~ "", extract(@"(.+)/(.+)", 2, workspaceAzureResourceId), "")
| extend assessmentDisplayName = case(type =~ "microsoft.security/assessments", case(isnotempty(properties.displayName), properties.displayName, properties.metadata.displayName), "")
| extend assessmentIdentifier = case(type =~ "microsoft.security/assessments", strcat(assessmentKey, "," , assessmentDisplayName, ",", severity), "")
| summarize assessmentsCount = count() , assessmentsIdentifier = make_list(assessmentIdentifier), softwareNamesIdentifier = make_list(softwareNameIdentifier), hasAgent = max(hasAgent), workspaceName = max(workspaceName), environment = max(environment), osType = max(osType), exemptionType = max(exemptionType)  by resourceId, subscriptionId, resourceName, resourceType, typeFullPath, severity
| extend packAssessments = pack(severity, assessmentsCount)
| summarize assessmentsSummary = make_bag(packAssessments), assessmentsIdentifier = make_set(assessmentsIdentifier), softwareNamesIdentifier = make_set(softwareNamesIdentifier), hasAgent = max(hasAgent), workspaceName= max(workspaceName), environment = max(environment), osType= max(osType), exemptionType = max(exemptionType)  by resourceId, subscriptionId, resourceName, resourceType, typeFullPath
| extend agentMonitoring = case(hasAgent =~ "NotApplicable" or hasAgent =~ "", '',
                                                hasAgent =~ "Unhealthy", "notInstalled",
                                                "installed")
| join kind=leftouter (
                    securityresources
                    | where type =~ "microsoft.security/pricings"
                    | project subscriptionId, bundleName = tolower(name), freeTrialRemainingTime = properties.freeTrialRemainingTime, pricingTier = tolower(properties.pricingTier)
                    | extend bundlesPricing = pack(bundleName, pricingTier)
                    | summarize subscriptionPricing = make_bag(bundlesPricing) by subscriptionId
                ) on subscriptionId
| extend hasNoSoftwareData = case(array_length(softwareNamesIdentifier) == 1, case(set_has_element(softwareNamesIdentifier, ""), true, false), false)
| extend softwareNamesIdentifier = case(hasNoSoftwareData, softwareNamesIdentifier, set_difference(softwareNamesIdentifier, pack_array("")))
| extend AssessmentsHigh = case(isnull(assessmentsSummary.high), 0 , toint(assessmentsSummary.high))
| extend AssessmentsMedium = case(isnull(assessmentsSummary.medium), 0 , toint(assessmentsSummary.medium))
| extend AssessmentsLow = case(isnull(assessmentsSummary.low), 0 , toint(assessmentsSummary.low))
| extend unhealthyAssessmentsCount = AssessmentsHigh + AssessmentsMedium + AssessmentsLow
| extend virtualmachines = case(isnull(subscriptionPricing), '' , subscriptionPricing.virtualmachines)
| extend virtualmachines = case(virtualmachines == 'free', 'off', 'on')
| extend sqlservers = case(isnull(subscriptionPricing), '' , subscriptionPricing.sqlservers)
| extend sqlservers = case(sqlservers == 'free', 'off', 'on')
| extend kubernetesservice = case(isnull(subscriptionPricing), '' , subscriptionPricing.kubernetesservice)
| extend kubernetesservice = case(kubernetesservice == 'free', 'off', 'on')
| extend containerregistry = case(isnull(subscriptionPricing), '' , subscriptionPricing.containerregistry)
| extend containerregistry = case(containerregistry == 'free', 'off', 'on')
| extend connectedcontainerregistry = case(isnull(subscriptionPricing), '' , subscriptionPricing.connectedcontainerregistry)
| extend connectedcontainerregistry = case(connectedcontainerregistry == 'free', 'off', 'on')
| extend sqlservervirtualmachines = case(isnull(subscriptionPricing), '' , subscriptionPricing.sqlservervirtualmachines)
| extend sqlservervirtualmachines = case(sqlservervirtualmachines == 'free', 'off', 'on')
| extend appservices = case(isnull(subscriptionPricing), '' , subscriptionPricing.appservices)
| extend appservices = case(appservices == 'free', 'off', 'on')
| extend storageaccounts = case(isnull(subscriptionPricing), '' , subscriptionPricing.storageaccounts)
| extend storageaccounts = case(storageaccounts == 'free', 'off', 'on')
| extend keyvaults = case(isnull(subscriptionPricing), '' , subscriptionPricing.keyvaults)
| extend keyvaults = case(keyvaults == 'free', 'off', 'on')
| extend opensourcerelationaldatabases = case(isnull(subscriptionPricing), '' , subscriptionPricing.opensourcerelationaldatabases)
| extend opensourcerelationaldatabases = case(opensourcerelationaldatabases == 'free', 'off', 'on')
| extend calculatedSubscriptionPricing = case(resourceType =~ "subscription" and isempty(subscriptionPricing) == false , iff(subscriptionPricing has "free" and subscriptionPricing has "standard", "partial", iff(subscriptionPricing has "free", "off", "on")), "")
| extend resourcePricing = case(typeFullPath =~ "microsoft.classiccompute/virtualmachines", virtualmachines, typeFullPath =~ "microsoft.compute/virtualmachines", virtualmachines, typeFullPath =~ "microsoft.hybridcompute/machines", virtualmachines, typeFullPath =~ "microsoft.sql/servers", sqlservers, typeFullPath =~ "microsoft.containerservice/managedclusters", kubernetesservice, typeFullPath =~ "microsoft.kubernetes/connectedclusters", kubernetesservice, typeFullPath =~ "microsoft.containerregistry/registries", containerregistry, typeFullPath =~ "microsoft.security/connectedcontainerregistries", connectedcontainerregistry, typeFullPath =~ "microsoft.sqlvirtualmachine/sqlvirtualmachines", sqlservervirtualmachines, typeFullPath =~ "microsoft.web/sites", appservices, typeFullPath =~ "microsoft.storage/storageaccounts", storageaccounts, typeFullPath =~ "microsoft.compute/virtualmachinescalesets", virtualmachines, typeFullPath =~ "microsoft.keyvault/vaults", keyvaults, typeFullPath =~ "microsoft.dbforpostgresql/servers", opensourcerelationaldatabases, typeFullPath =~ "microsoft.dbformysql/servers", opensourcerelationaldatabases, typeFullPath =~ "microsoft.dbformariadb/servers", opensourcerelationaldatabases, calculatedSubscriptionPricing)
| extend pricing = case(resourceType =~ "subscription" , calculatedSubscriptionPricing , resourcePricing)
| project resourceType, exemptionType, typeFullPath, resourceId, resourceName, subscriptionId, environment, osType, workspaceName, agentMonitoring, assessmentsIdentifier, assessmentsSummary, subscriptionPricing, unhealthyAssessmentsCount, pricing, softwareNamesIdentifier
| extend resourceGroup = tolower(tostring(split(resourceId, "/")[4]))
| order by unhealthyAssessmentsCount, subscriptionId, resourceType, resourceId
| where isnotempty(resourceId)
| extend resourceType = iff(resourceType == 'servers','SQL Server',resourceType)
| extend resourceType = iff(resourceType == 'machines','Hybrid Server',resourceType)
| summarize on_=countif(pricing == "on"), off_=countif(isempty(pricing)), part_=countif(pricing == "partial") by resourceType | order by on_ desc

  
Screenshot 2021-07-30 162029.png

View solution in original post