modules/shared/RbacTier.ps1
|
#Requires -Version 7.4 <# .SYNOPSIS Per-wrapper opt-in elevated RBAC tier mechanism (vNEXT v1.2.0, #234). .DESCRIPTION azure-analyzer wrappers default to a Reader-only RBAC tier. A small number of advanced inspections (e.g. Karpenter Provisioner discovery inside the AKS data plane) need cluster-data-plane reads that the ARM Reader role does not expose. This module implements an explicit, off-by-default opt-in: * Tier 'Reader' - the default. Covers all ARM/Graph reads that the standard Reader / per-domain read-only roles already grant. * Tier 'ClusterUser' - opt-in. Covers the additional kubeconfig retrieval performed by the Karpenter inspection branch (Azure Kubernetes Service Cluster User Role at the AKS cluster scope). The opt-in is per-wrapper invocation, NOT orchestrator-wide. A wrapper that exposes -EnableElevatedRbac calls Set-RbacTier 'ClusterUser' at the very top of its body and Reset-RbacTier in finally{}. The state is held in a script-scope variable on this module, so unrelated wrappers running in the same process default back to 'Reader'. The per-wrapper scoping decision is documented in .squad/decisions/inbox/atlas-issue-234-complete-* and in docs/consumer/permissions/aks-karpenter-cost.md. .NOTES All assertion failures throw an error with the [InsufficientRbac] prefix and an actionable remediation that points the consumer at the -EnableElevatedRbac flag on the calling wrapper. #> [CmdletBinding()] param () Set-StrictMode -Version Latest $ErrorActionPreference = 'Stop' $script:RbacTierAllowed = @('Reader', 'ClusterUser') $script:RbacTierCurrent = 'Reader' function Get-RbacTier { <# .SYNOPSIS Return the currently active RBAC tier ('Reader' or 'ClusterUser'). #> [CmdletBinding()] param () return $script:RbacTierCurrent } function Set-RbacTier { <# .SYNOPSIS Set the active RBAC tier for the current wrapper invocation. .PARAMETER Tier One of 'Reader' or 'ClusterUser'. #> [CmdletBinding()] param ( [Parameter(Mandatory)] [ValidateSet('Reader', 'ClusterUser')] [string] $Tier ) $script:RbacTierCurrent = $Tier Write-Verbose ("[rbactier] active tier set to '{0}'" -f $Tier) } function Reset-RbacTier { <# .SYNOPSIS Restore the default Reader tier. Wrappers MUST call this in finally{} after they Set-RbacTier 'ClusterUser'. #> [CmdletBinding()] param () $script:RbacTierCurrent = 'Reader' } function Test-RbacTierSatisfies { <# .SYNOPSIS Return $true when the active tier is at or above the required tier. .PARAMETER Required One of 'Reader' or 'ClusterUser'. #> [CmdletBinding()] param ( [Parameter(Mandatory)] [ValidateSet('Reader', 'ClusterUser')] [string] $Required ) $rank = @{ Reader = 0; ClusterUser = 1 } return $rank[$script:RbacTierCurrent] -ge $rank[$Required] } function Assert-RbacTier { <# .SYNOPSIS Throw [InsufficientRbac] when the active tier is below the required tier. .PARAMETER Required One of 'Reader' or 'ClusterUser'. .PARAMETER OptInFlag Name of the wrapper switch that enables the elevated tier (defaults to '-EnableElevatedRbac'). Surfaced in the remediation text so the consumer knows exactly which flag to pass. .PARAMETER Capability Short human-readable name of the gated capability (e.g. 'Karpenter Provisioner inspection'). Surfaced in the error. #> [CmdletBinding()] param ( [Parameter(Mandatory)] [ValidateSet('Reader', 'ClusterUser')] [string] $Required, [string] $OptInFlag = '-EnableElevatedRbac', [string] $Capability = 'this elevated capability' ) if (Test-RbacTierSatisfies -Required $Required) { return } $remediation = "Pass $OptInFlag to the wrapper to opt in to the '$Required' tier (Azure Kubernetes Service Cluster User Role at the AKS cluster scope). The opt-in is OFF by default; see docs/consumer/permissions/aks-karpenter-cost.md." $message = "[InsufficientRbac] $Capability requires RBAC tier '$Required' but the active tier is '$script:RbacTierCurrent'. $remediation" throw $message } |