advisor/advisor.psm1
using module ../utils/utils.psd1 <# .SYNOPSIS Retrieves high availability recommendations from Azure Advisor. .DESCRIPTION The Get-WAFAdvisorRecommendations function queries Azure Advisor for recommendations related to high availability. It uses Azure Resource Graph to fetch and join relevant resource data. .PARAMETER SubscriptionIds The subscription IDs for which to retrieve recommendations. .PARAMETER AdditionalRecommendationIds Additional recommendation IDs to include in the query. In the WARA we use this to include Advisor recommendations that are not categorized as high availability but are still relevant. .PARAMETER HighAvailability Switch to filter recommendations related to high availability. .PARAMETER Security Switch to filter recommendations related to security. .INPUTS None. You cannot pipe objects to this function. .OUTPUTS System.Object. The function returns a list of recommendations. .EXAMPLE $subId = "22222222-2222-2222-2222-222222222222" Get-WAFAdvisorRecommendation -SubscriptionIds $subId -HighAvailability .EXAMPLE $subId = "22222222-2222-2222-2222-222222222222" $AddtionalRecommendationIds = @()"82219546-1110-4f5d-a1c2-7defb204663c","693e2dbf-cdec-47a2-8e54-79752cd7e3fc") Get-WAFAdvisorRecommendation -SubscriptionIds $subId -HighAvailability -AdditionalRecommendationIds $AddtionalRecommendationIds .NOTES Author: Kyle Poineal Date: 2024-12-12 #> function Get-WAFAdvisorRecommendation { [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [AllowEmptyCollection()] [array] $SubscriptionIds, [Parameter(Mandatory = $false)] [AllowEmptyCollection()] [array] $AdditionalRecommendationIds, [switch] $HighAvailability, [switch] $Security, [switch] $Cost, [switch] $Performance, [switch] $OperationalExcellence ) # Initialize an array to hold the selected categories $categories = @() # Add categories based on the selected switches switch ($PSBoundParameters.Keys) { 'HighAvailability' { $categories += 'HighAvailability' } 'Security' { $categories += 'Security' } 'Cost' { $categories += 'Cost' } 'Performance' { $categories += 'Performance' } 'OperationalExcellence' { $categories += 'OperationalExcellence' } } # Convert the categories array to a comma-separated string $categoriesString = $categories -join "','" $AdditionalRecommendationIdsString = $AdditionalRecommendationIds -join "','" $advquery = ` "advisorresources | where type == 'microsoft.advisor/recommendations' | where tostring(properties.category) in ('$categoriesString') or properties.recommendationTypeId in ('$AdditionalRecommendationIdsString') | where properties.tracked !~ 'true' | extend resId = tolower(tostring(properties.resourceMetadata.resourceId)) | join kind=leftouter (resources | project ['resId']=tolower(id), subscriptionId, resourceGroup, location, type) on resId | extend id = iff(properties.impactedField =~ 'microsoft.subscriptions/subscriptions', strcat('/subscriptions/', subscriptionId), resId1) | extend subscriptionId = coalesce(subscriptionId,subscriptionId1) | extend resourceGroup = iff(properties.impactedField =~ 'microsoft.subscriptions/subscriptions', 'N/A', resourceGroup) | extend location = iff(properties.impactedField =~ 'microsoft.subscriptions/subscriptions', 'global', coalesce(location,location1)) | project recommendationId = properties.recommendationTypeId, type = tolower(properties.impactedField), name = properties.impactedValue, id, subscriptionId, resourceGroup, location, category = properties.category, impact = properties.impact, description = properties.shortDescription.solution | order by ['id']" <# $advquery = ` "advisorresources | where type == 'microsoft.advisor/recommendations' and tostring(properties.category) in ('$categoriesString') | extend resId = tolower(tostring(properties.resourceMetadata.resourceId)) | join kind=leftouter (resources | project ['resId']=tolower(id), subscriptionId, resourceGroup ,location) on resId | project recommendationId = properties.recommendationTypeId, type = tolower(properties.impactedField), name = properties.impactedValue, id = resId1, subscriptionId = subscriptionId1,resourceGroup = resourceGroup, location = location1, category = properties.category, impact = properties.impact, description = properties.shortDescription.solution | order by ['id']" #> $queryResults = Invoke-WAFQuery -Query $advquery -SubscriptionId $SubscriptionIds $return = Build-WAFAdvisorObject -AdvQueryResult $queryResults return $return } <# .SYNOPSIS Builds a list of advisory objects from Azure Advisor query results. .DESCRIPTION The Build-WAFAdvisorObject function processes the results of an Azure Advisor query and constructs a list of advisory objects. Each advisory object contains details such as recommendation ID, type, name, resource ID, subscription ID, resource group, location, category, impact, and description. .PARAMETER AdvQueryResult An array of query results from Azure Advisor. .EXAMPLE $advQueryResult = Get-WAFAdvisorRecommendations -Subid "12345" .NOTES Author: Kyle Poineal Date: 2024-12-12 #> <# .SYNOPSIS Builds a list of Advisor resource objects from Azure Advisor query results. .DESCRIPTION The `Build-WAFAdvisorObject` function processes the results of an Azure Advisor query and constructs a list of `advisorResourceObj` objects. Each object contains detailed information about an Advisor recommendation, including IDs, resource details, category, impact, and descriptions. .PARAMETER AdvQueryResult An array of query results from Azure Advisor. .INPUTS System.Object[]. You can pipe an array of Advisor query results to this function. .OUTPUTS advisorResourceObj[]. The function returns an array of `advisorResourceObj` instances representing Advisor recommendations. .EXAMPLE $advQueryResult = Get-WAFAdvisorRecommendation -SubscriptionIds "12345" -HighAvailability $advisorObjects = Build-WAFAdvisorObject -AdvQueryResult $advQueryResult This example builds Advisor resource objects from the query results. .NOTES Author: Kyle Poineal Date: 2024-12-12 #> function Build-WAFAdvisorObject { [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [AllowEmptyCollection()] [PSCustomObject[]] $AdvQueryResult ) # Initialize an array to hold the processed objects $return = $AdvQueryResult.ForEach({ [advisorResourceObj]::new($_) }) # Return the processed objects return $return } <# .SYNOPSIS Represents an Azure Advisor recommendation resource object. .DESCRIPTION The `advisorResourceObj` class encapsulates the details of an Azure Advisor recommendation resource. It contains properties such as recommendation ID, type, name, resource ID, subscription ID, resource group, location, category, impact, and description. .PARAMETER Recommendation A recommendation object returned from Azure Advisor. The attributes of the object are used to populate the properties of the `advisorResourceObj` instance. [string] $recommendationId [string] $type [string] $name [string] $id [string] $subscriptionId [string] $resourceGroup [string] $location [string] $category [string] $impact [string] $description .INPUTS None. You cannot pipe input to this class. .OUTPUTS advisorResourceObj. An instance representing an Advisor recommendation. .EXAMPLE $advisorRecommendation = [advisorResourceObj]::new($recommendation) This example creates a new instance of `advisorResourceObj` using a recommendation object. .NOTES Author: Kyle Poineal Date: 2024-12-12 #> class advisorResourceObj { <# Define the class. Try constructors, properties, or methods. #> [string] $recommendationId [string] $type [string] $name [string] $id [string] $subscriptionId [string] $resourceGroup [string] $location [string] $category [string] $impact [string] $description # Default Contructor that takes a PSObject as input # Right now this is just a simple assignment of properties, but can be expanded to include more complex logic in the future. advisorResourceObj([PSObject]$psObject) { $this.RecommendationId = $psObject.recommendationId $this.Type = $psObject.type $this.Name = $psObject.name $this.Id = $psObject.id $this.SubscriptionId = $psObject.subscriptionId $this.ResourceGroup = $psObject.resourceGroup $this.Location = $psObject.location $this.Category = $psObject.category $this.Impact = $psObject.impact $this.Description = $psObject.description } } <# .SYNOPSIS Retrieves metadata from Azure Advisor. .DESCRIPTION The Get-WAFAdvisorMetadata function retrieves metadata from Azure Advisor using the Azure REST API. It uses an access token to authenticate and fetch the metadata. .INPUTS None. You cannot pipe objects to this function. .OUTPUTS System.Object. The function returns the supported values from the Advisor metadata. .EXAMPLE $AdvisorMetadata = Get-WAFAdvisorMetadata .NOTES Author: Kyle Poineal Date: 2024-12-12 #> Function Get-WAFAdvisorMetadata { # Get an access token for the Azure REST API $securetoken = Get-AzAccessToken -AsSecureString -ResourceUrl "https://management.azure.com/" -WarningAction SilentlyContinue # Convert the secure token to a plain text token $token = ConvertFrom-SecureString -SecureString $securetoken.token -AsPlainText # Create the authorization headers $authHeaders = @{ 'Authorization' = 'Bearer ' + $token } # Define the URI for the Advisor metadata $AdvisorMetadataURI = 'https://management.azure.com/providers/Microsoft.Advisor/metadata?api-version=2023-01-01' # Invoke the REST API to get the metadata $r = Invoke-RestMethod -Uri $AdvisorMetadataURI -Headers $authHeaders -Method Get # Return the supported values from the metadata return $r.value.properties[0].supportedValues } |