Private/Test-VMExcluded.ps1
|
function Test-VMExcluded { <# .SYNOPSIS Decides whether a VM inventory record should be excluded from metric collection, returning the exclusion reason (string) or $null to keep it. .DESCRIPTION Pure, Azure-free classifier so the rules are unit-testable. The point is to skip managed / ephemeral / short-running compute (Databricks clusters, VM Scale Set members, Spot nodes, AKS node pools) using only cheap control-plane signals -- before any expensive Get-AzMetric call. .PARAMETER VM A normalized VM record with: Name, ResourceGroup, Location, SkuName, Tags (dict or PSObject), Vmss (scale-set id or empty), Priority, Created (datetime or $null). .PARAMETER IncludeScaleSetMember Keep VMs that belong to a VM Scale Set (Flex). Default: excluded. .PARAMETER IncludeSpot Keep Spot-priority VMs. Default: excluded. .PARAMETER ExcludeResourceGroupPattern Regex(es); a VM whose resource group matches any is excluded. .PARAMETER ExcludeNamePattern Regex(es); a VM whose name matches any is excluded. .PARAMETER ExcludeTag Hashtable of tag rules. A VM is excluded if it carries a matching tag. The value may be '*' (any value for that key) or a specific value (case-insensitive). .PARAMETER MinAgeDays When > 0 and the record has a Created date, exclude VMs created within the last N days (too little history to size). Requires -Now. .PARAMETER Now Reference time (UTC) for the MinAgeDays check. .OUTPUTS [string] reason to exclude, or $null to keep. #> [CmdletBinding()] [OutputType([string])] param( [Parameter(Mandatory)] [object] $VM, [switch] $IncludeScaleSetMember, [switch] $IncludeSpot, [string[]] $ExcludeResourceGroupPattern, [string[]] $ExcludeNamePattern, [hashtable] $ExcludeTag, [int] $MinAgeDays = 0, [datetime] $Now ) if (-not $IncludeScaleSetMember -and $VM.Vmss) { return 'VMSS member' } if (-not $IncludeSpot -and "$($VM.Priority)" -ieq 'Spot') { return 'Spot' } foreach ($pat in $ExcludeResourceGroupPattern) { if ($pat -and $VM.ResourceGroup -match $pat) { return "RG ~ /$pat/" } } foreach ($pat in $ExcludeNamePattern) { if ($pat -and $VM.Name -match $pat) { return "name ~ /$pat/" } } if ($ExcludeTag -and $ExcludeTag.Count -and $VM.Tags) { foreach ($key in $ExcludeTag.Keys) { $val = Get-TagValueCI -Tags $VM.Tags -Key $key if ($null -ne $val) { $want = [string]$ExcludeTag[$key] if ($want -eq '*' -or [string]$val -ieq $want) { return "tag $key=$val" } } } } if ($MinAgeDays -gt 0 -and $VM.Created -and $Now) { if ([datetime]$VM.Created -gt $Now.AddDays(-$MinAgeDays)) { return "younger than ${MinAgeDays}d" } } return $null } function Get-TagValueCI { <# .SYNOPSIS Case-insensitive tag lookup that works for both IDictionary and PSObject tag bags. #> [CmdletBinding()] [OutputType([string])] param( [object] $Tags, [Parameter(Mandatory)] [string] $Key ) if ($null -eq $Tags) { return $null } if ($Tags -is [System.Collections.IDictionary]) { foreach ($k in $Tags.Keys) { if ([string]$k -ieq $Key) { return [string]$Tags[$k] } } return $null } foreach ($p in $Tags.PSObject.Properties) { if ($p.Name -ieq $Key) { return [string]$p.Value } } return $null } |