DefenderforCloud.psm1
function Invoke-AzGraphQueryWithPagination { param ( [Parameter(Mandatory=$true)] [string]$Query, [Parameter(Mandatory=$false)] [int]$PageSize = 1000 ) # Initialize variables for pagination $Skip = 0 $AllResults = @() Do { # Execute the query with current pagination settings If($Skip -gt 0) { $Response = Search-AzGraph -Query $Query -First $PageSize -Skip $Skip -UseTenantScope } Else { $Response = Search-AzGraph -Query $Query -First $PageSize -UseTenantScope } # Add response data to all results $AllResults += $Response.Data # Adjust Skip for the next iteration based on the number of records retrieved If($Response.Count -lt $PageSize) { break # Exit loop if fewer records than page size were returned, indicating the last page } Else { $Skip += $PageSize } } while ($true) # Return the collected results return $AllResults } Function Get-ServerPricingDetails { Param( [Parameter(Mandatory=$True)] [string]$PricingURL, [Parameter(Mandatory=$True)] [string]$AccessToken ) Try{ Invoke-RestMethod -Method Get -Uri $PricingURL -Headers @{Authorization = "Bearer $AccessToken"} -ContentType "application/json" -TimeoutSec 120 -ErrorAction STOP } Catch{ $Error[0] } } Function Enable-DefenderPlan { <# .SYNOPSIS Enables the Defender Plan for a given server by setting its pricing details, based on the information retrieved from Get-AzureResources. .DESCRIPTION The `Enable-DefenderPlan` function is designed to work in conjunction with the `Get-AzureResources` function. It takes the output from `Get-AzureResources`—specifically, the server name, subscription ID, and pricing URL—and sets the pricing details to the "Standard" package using an access token for authentication. This function simplifies the process of configuring Defender plans for multiple Azure resources by automating the pricing plan application. .PARAMETER AccessToken The access token used for authentication with the API. This parameter is mandatory. .PARAMETER PricingUrl The URL to the API endpoint where pricing details are updated, obtained from the output of `Get-AzureResources`. This parameter is mandatory. .PARAMETER ServerName The name of the server for which the Defender Plan is being enabled, obtained from the output of `Get-AzureResources`. This parameter is mandatory. .EXAMPLE Connect-AzAccount $AccessToken = Get-AzAccessToken | Select-Object -ExpandProperty token $AzureResources = Get-AzureResources -ResourceType "VM" -QueryType "RG" -ResourceGroupName "YourResourceGroup" -SubscriptionId "YourSubscriptionID" -AccessToken "YourAccessToken" foreach ($Resource in $AzureResources) { Enable-DefenderPlan -AccessToken "YourAccessToken" -PricingUrl $Resource.'Pricing URL' -ServerName $Resource.'Server Name' } This example first retrieves a list of Azure VM resources within a specified resource group and subscription. It then iterates over these resources, enabling the Defender Plan for each server using the retrieved pricing URL and server name. .OUTPUTS PSObject Returns a PowerShell custom object containing details about the pricing plan configuration for the specified server, including the server name, pricing type, inheritance status, enablement time, sub plan, pricing tier, and remaining time for any free trial. .NOTES This function is part of a workflow for managing Defender for Cloud pricing plans across multiple Azure resources. For detailed information on possible pricing plans and their configuration, refer to the Defender for Cloud documentation: https://learn.microsoft.com/en-us/rest/api/defenderforcloud/pricings/update #> Param ( [Parameter(Mandatory=$True)] [string]$AccessToken, [Parameter(Mandatory=$True)] [string]$PricingUrl, [Parameter(Mandatory=$True)] [string]$ServerName ) #Pricing Details for "Standard" Package #Source: https://learn.microsoft.com/en-us/rest/api/defenderforcloud/pricings/update?view=rest-defenderforcloud-2024-01-01&tabs=HTTP#update-pricing-on-resource-(example-for-virtualmachines-plan) $SubPlan = "P1" $PricingBody = @{ "properties" = @{ "pricingTier" = "Standard" "subPlan" = $SubPlan } } | ConvertTo-Json Try{ Write-Verbose -Message "Setting the pricing details for the following URL: $PricingUrl" $SetPricing = Invoke-RestMethod -Method Put -Uri $PricingUrl -Headers @{Authorization = "Bearer $AccessToken"} -Body $PricingBody -ContentType "application/json" -TimeoutSec 120 -ErrorAction STOP $PricingObject = [ORDERED]@{ "Server Name" = $ServerName "Type" = $SetPricing.type "Inherited" = $SetPricing.properties.inherited "enablementTime" = $SetPricing.properties.enablementTime "Sub Plan" = $SetPricing.properties.subPlan "Pricing Tier" = $SetPricing.properties.pricingTier "Free Trial Remaining Time" = $SetPricing.properties.freeTrialRemainingTime } } Catch{ $Error[0] } Return (New-Object -TypeName PSObject -Property $PricingObject) } Function Remove-DefenderPlan { <# .SYNOPSIS Disables the Defender for Cloud pricing plan for a specified server. .DESCRIPTION The `Remove-DefenderPlan` function disables the Defender for Cloud pricing plan for a given server. It requires an access token for authentication and a pricing URL that specifies the target for the pricing plan removal. This function is designed to work in conjunction with the `Get-AzureResources` function, allowing for bulk operations across multiple resources. .PARAMETER AccessToken The access token used for authentication with the API. This parameter is mandatory. .PARAMETER PricingUrl The URL to the API endpoint where pricing details are updated. This parameter is mandatory. .PARAMETER ServerName The name of the server for which the Defender Plan is being disabled. This parameter is mandatory and used for logging purposes. .EXAMPLE # Obtain access token and resource details Connect-AzAccount $AccessToken = Get-AzAccessToken | Select-Object -ExpandProperty token $Servers = Get-AzureResources -ResourceType ARC -QueryType RG -ResourceGroupName "YourResourceGroup" -SubscriptionId "YourSubscriptionID" -AccessToken $AccessToken # Disable Defender for Cloud pricing plans for the retrieved servers $Servers | ForEach-Object { Remove-DefenderPlan -AccessToken $AccessToken -PricingUrl $PSITEM.'Pricing URL' -ServerName $PSITEM.'Server Name' } .NOTES Before executing this function, ensure you are authenticated to Azure with `Connect-AzAccount` and have obtained a valid access token using `Get-AzAccessToken`. This function is part of a larger workflow for managing Azure resources and their associated Defender for Cloud pricing plans. #> Param ( [Parameter(Mandatory=$True)] [string]$AccessToken, [Parameter(Mandatory=$True)] [string]$PricingUrl, [Parameter(Mandatory=$True)] [string]$ServerName ) Try{ Write-Verbose -Message "Disabling the Defender for Cloud pricing for the following URL: $PricingUrl" Invoke-RestMethod -Method Delete -Uri $PricingUrl -Headers @{Authorization = "Bearer $AccessToken"} -ContentType "application/json" -TimeoutSec 120 -ErrorAction STOP } Catch{ $Error[0] } } Function Get-AzureResources { <# .SYNOPSIS Retrieves Azure resource details based on specified criteria. .DESCRIPTION The `Get-AzureResources` function queries Azure resources either by resource group or tags for Azure Arc or VM resources. It requires an active Azure account connection and utilizes a provided access token for authentication. This function is versatile in its application, allowing for detailed queries tailored to the user's specific needs. .PARAMETER ResourceType Specifies the type of Azure resource to query. Valid options are "ARC" for Azure Arc resources and "VM" for Azure virtual machines. This parameter is mandatory. .PARAMETER QueryType Determines the query method. Use "RG" to query by resource group or "TAG" to query by tag name and value. This parameter is mandatory. .PARAMETER ResourceGroupName Specifies the name of the resource group to query. This parameter is mandatory when using QueryType "RG". .PARAMETER TagName Specifies the name of the tag for querying resources. This parameter is mandatory when using QueryType "TAG". .PARAMETER TagValue Specifies the value of the tag for querying resources. This parameter is mandatory when using QueryType "TAG". .PARAMETER SubscriptionId The subscription ID where the resource resides. This parameter is mandatory for both "RG" and "TAG" query types. .PARAMETER AccessToken The access token used for authentication with the Azure API. This parameter is mandatory. .EXAMPLE # Query Azure Arc resources in a specific resource group Get-AzureResources -ResourceType ARC -QueryType RG -ResourceGroupName "YourResourceGroup" -SubscriptionId "YourSubscriptionID" -AccessToken $AccessToken .EXAMPLE # Query Azure VMs in a specific resource group Get-AzureResources -ResourceType VM -QueryType RG -ResourceGroupName "YourResourceGroup" -SubscriptionId "YourSubscriptionID" -AccessToken $AccessToken .EXAMPLE # Query Azure Arc resources by tag name and value Get-AzureResources -ResourceType ARC -QueryType TAG -TagName "Environment" -TagValue "Production" -AccessToken $AccessToken .OUTPUTS PSObject[] Returns an array of PowerShell custom objects, each containing details about the queried Azure resources. These details include the server name, resource group, subscription ID, pricing tier, pricing URL, inheritance status, inherited from, and remaining free trial time. .NOTES Before executing this function, ensure you are connected to your Azure account and have obtained a valid access token: Connect-AzAccount $AccessToken = Get-AzAccessToken | Select-Object -ExpandProperty token This function uses the Azure Resource Graph for query execution and may require appropriate permissions to view resource details across the specified subscription(s). #> [CmdletBinding()] param ( [Parameter(Mandatory=$True, Position=0)] [ValidateSet("ARC", "VM")] [string]$ResourceType, [Parameter(Mandatory=$True, Position=1)] [ValidateSet("RG", "TAG")] [string]$QueryType, [Parameter(Mandatory=$True, ParameterSetName="RG")] [string]$ResourceGroupName, [Parameter(Mandatory=$True, ParameterSetName="TAG")] [string]$TagName, [Parameter(Mandatory=$True, ParameterSetName="TAG")] [string]$TagValue, [Parameter(Mandatory=$True, ParameterSetName="RG")] [string]$SubscriptionId, [Parameter(Mandatory=$True, ParameterSetName="TAG")] [Parameter(Mandatory=$True, ParameterSetName="RG")] [string]$AccessToken ) Begin { $ModuleAvailable = Get-Module -Name Az.ResourceGraph If (-not $moduleAvailable) { Write-Output "The 'Az.ResourceGraph' module is not installed. Please install it using 'Install-Module -Name Az.ResourceGraph'." Exit } } Process{ Switch($ResourceType){ "ARC" { Write-Verbose -Message "Setting Resource Type to Azure Arc" If($QueryType -eq "RG"){ Write-Verbose -Message "Quering all the Azure Arc machines from the $ResourceGroupName and $SubscriptionId" $Query = "resources | where type == 'microsoft.hybridcompute/machines'" $AzureResources = Invoke-AzGraphQueryWithPagination -Query $Query -PageSize 100 | Where-Object {$PSItem.resourceGroup -eq $ResourceGroupName -and $PSItem.subscriptionId -eq $SubscriptionID} Write-Verbose -Message "There are $($AzureResources | Measure-Object | Select-Object -ExpandProperty Count) total objects" } If($QueryType -eq "TAG"){ Write-Verbose -Message "Quering all the Azure Arc machines with the Tag Name: $TagName and Tag Value: $TagValue" $Query = "resources | where type == 'microsoft.hybridcompute/machines' | where tags['$($TagName)'] == '$($TagValue)'" $AzureResources = Invoke-AzGraphQueryWithPagination -Query $Query -PageSize 100 Write-Verbose -Message "There are $($AzureResources | Measure-Object | Select-Object -ExpandProperty Count) total objects" } } "VM" { Write-Verbose -Message "Setting Resource Type to Azure VM" If($QueryType -eq "RG"){ Write-Verbose -Message "Quering all the Azure VMs from the $ResourceGroupName and $SubscriptionId" $Query = "resources | where type == 'microsoft.compute/virtualmachines' | where resourceGroup =~ '$ResourceGroupName' and subscriptionId =~ '$SubscriptionID'" $AzureResources = Invoke-AzGraphQueryWithPagination -Query $Query -PageSize 100 } If($QueryType -eq "TAG"){ $Query = "resources | where type == 'microsoft.compute/virtualmachines' | where tags['$($TagName)'] == '$($TagValue)'" $AzureResources = Invoke-AzGraphQueryWithPagination -Query $Query -PageSize 100 } } } #Read the current Pricing Details $Servers = @() foreach($Server in $AzureResources){ $PricingURL = "https://management.azure.com$($Server.ResourceId)/providers/Microsoft.Security/pricings/virtualMachines?api-version=2024-01-01" Write-Verbose -Message "Reading server $($Server.name) details" $ServerDefenderDetails = Get-ServerPricingDetails -PricingURL $PricingURL -AccessToken $AccessToken $Properties = [ORDERED]@{ "Server Name" = $Server.name "Resource Group" = $Server.resourceGroup "Subscription ID" = $Server.subscriptionId "Pricing Tier" = $ServerDefenderDetails.properties.pricingTier "Pricing URL" = $PricingURL "Inherited" = $ServerDefenderDetails.properties.inherited "Inherited From" = $ServerDefenderDetails.properties.inheritedFrom "Free Trial Remaining Time" = $ServerDefenderDetails.properties.freeTrialRemainingTime } $Servers += New-Object -TypeName PSObject -Property $Properties } #Print out Server Pricing Details Return $Servers }#End Process } Function Get-DefenderForSQLExecutable { <# .SYNOPSIS Gets the path of the Microsoft Defender for SQL executable. .DESCRIPTION This function searches for the latest version of the Microsoft Defender for SQL executable within a specified directory on the local file system. It returns the full path of the executable if found. If the executable is not found, it returns null and writes an error message. .OUTPUTS System.String The full path to the executable or null if not found. .EXAMPLE PS C:\> $executablePath = Get-DefenderForSQLExecutable This example retrieves the path to the latest Defender for SQL executable. #> $BaseDirectory = "C:\Packages\Plugins\Microsoft.Azure.AzureDefenderForSQL.AdvancedThreatProtection.Windows" $LatestVersionDir = Get-ChildItem -Path $BaseDirectory -Directory | Sort-Object Name -Descending | Select-Object -First 1 $ExecutablePath = Join-Path -Path $LatestVersionDir.FullName -ChildPath "bin\Microsoft.SQL.ADS.DefenderForSQL.exe" If (Test-Path -Path $ExecutablePath) { return $executablePath } Else { Write-Error "Defender for SQL executable not found." return $null } } Function Test-DefenderforSQLBruteForceAttack { <# .SYNOPSIS Simulates a Brute Force attack using Microsoft Defender for SQL. .DESCRIPTION This function retrieves the executable path for Defender for SQL and runs a simulation for a Brute Force attack. It requires the Defender for SQL executable to be present and properly configured. .OUTPUTS None directly from the function, but outputs from the executable will be displayed. .EXAMPLE PS C:\> Test-DefenderforSQLBruteForceAttack This example runs a Brute Force attack simulation. #> $Executable = Get-DefenderForSQLExecutable If($Executable) { & $Executable simulate --Attack BruteForce } } Function Test-DefenderForSQLAttack { <# .SYNOPSIS Simulates specified types of attacks on SQL databases using Microsoft Defender for SQL. .DESCRIPTION This function allows for simulation of various attack types on SQL databases including SQL Injection and Login Suspicious App among others. It takes the type of attack as a parameter along with user credentials to execute the simulation. .PARAMETER AttackType Specifies the type of attack to simulate. Must be one of the following: SqlInjection, LoginSuspiciousApp, PrincipalAnomaly, ShellExternalSourceAnomaly, ShellObfuscation. .PARAMETER UserName Specifies the user name to use for the attack simulation. .PARAMETER Password Specifies the password for the user name provided. .OUTPUTS None directly from the function, but outputs from the executable will be displayed. .EXAMPLE PS C:\> Test-DefenderForSQLAttack -AttackType "SqlInjection" -UserName "user" -Password "password" This example simulates a SQL Injection attack. #> [CmdletBinding()] Param( [Parameter(Mandatory=$true)] [ValidateSet("SqlInjection", "LoginSuspiciousApp", "PrincipalAnomaly", "ShellExternalSourceAnomaly", "ShellObfuscation")] [string]$AttackType, [Parameter(Mandatory=$true)] [string]$UserName, [Parameter(Mandatory=$true)] [string]$Password ) $Executable = Get-DefenderForSQLExecutable If($Executable) { & $Executable simulate --Attack $AttackType --UserName $UserName --Password $Password } } Function Test-DefenderForSQLDataExfiltration { <# .SYNOPSIS Simulates a data exfiltration attack using Microsoft Defender for SQL. .DESCRIPTION This function initiates a simulation of a data exfiltration attack. It uses the Defender for SQL executable to perform the simulation, demonstrating how data might be illicitly transferred or extracted from the system. .OUTPUTS None directly from the function, but outputs from the executable will be displayed. .EXAMPLE PS C:\> Test-DefenderForSQLDataExfiltration This example simulates a Data Exfiltration attack. #> $Executable = Get-DefenderForSQLExecutable If($Executable) { & $Executable simulate --Attack DataExfiltration } } |