Public/Get-CISControlList.ps1

function Get-CISControlList {
    <#
    .SYNOPSIS
        Lists all CIS Azure Benchmark v5.0.0 controls with filtering options.

    .DESCRIPTION
        Returns the full list of 152 CIS controls with metadata. Useful for discovering
        available controls, filtering by section or assessment status, and planning scans.

    .PARAMETER Section
        Filter by section name or number.

    .PARAMETER AssessmentStatus
        Filter by assessment status: 'Automated', 'Manual', or 'All'.

    .PARAMETER ProfileLevel
        Filter by maximum profile level (1 or 2).

    .PARAMETER ControlId
        Get specific controls by ID.

    .PARAMETER Severity
        Filter by severity level.

    .EXAMPLE
        Get-CISControlList | Measure-Object
        # Returns 152

    .EXAMPLE
        Get-CISControlList -AssessmentStatus Automated
        # Returns all 79 automated controls

    .EXAMPLE
        Get-CISControlList -Section 'Identity Services' -Severity Critical,High
    #>

    [CmdletBinding()]
    param(
        [Parameter()]
        [string[]]$Section,

        [Parameter()]
        [ValidateSet('Automated', 'Manual', 'All')]
        [string]$AssessmentStatus = 'All',

        [Parameter()]
        [ValidateSet(1, 2)]
        [int]$ProfileLevel = 2,

        [Parameter()]
        [string[]]$ControlId,

        [Parameter()]
        [ValidateSet('Critical', 'High', 'Medium', 'Low', 'Informational')]
        [string[]]$Severity
    )

    $defPath = Join-Path (Join-Path (Join-Path $PSScriptRoot '..') 'Data') 'ControlDefinitions.psd1'
    if (-not (Test-Path $defPath)) {
        Write-Error "Control definitions file not found: $defPath"
        return
    }

    $definitions = Import-PowerShellDataFile -Path $defPath
    $controls = $definitions.Controls

    # Apply filters
    $controls = $controls | Where-Object { $_.ProfileLevel -le $ProfileLevel }

    if ($AssessmentStatus -ne 'All') {
        $controls = $controls | Where-Object { $_.AssessmentStatus -eq $AssessmentStatus }
    }

    if ($Section) {
        $controls = $controls | Where-Object {
            $ctrl = $_
            $Section | Where-Object {
                $ctrl.Section -like "*$_*" -or
                $ctrl.ControlId -like "$_*" -or
                $ctrl.Subsection -like "*$_*"
            }
        }
    }

    if ($ControlId) {
        $controls = $controls | Where-Object { $_.ControlId -in $ControlId }
    }

    if ($Severity) {
        $controls = $controls | Where-Object { $_.Severity -in $Severity }
    }

    # Return as clean objects
    $controls | ForEach-Object {
        [PSCustomObject]@{
            ControlId        = $_.ControlId
            Title            = $_.Title
            Section          = $_.Section
            Subsection       = $_.Subsection
            AssessmentStatus = $_.AssessmentStatus
            ProfileLevel     = $_.ProfileLevel
            Severity         = $_.Severity
            CheckPattern     = $_.CheckPattern
        }
    } | Sort-Object {
        # Custom numerical sort: split on dots and compare each segment as integers
        $digits = ($_.ControlId -replace '[^\d.]', '' -replace '\.+$', '' -replace '^\.+', '')
        $segments = $digits -split '\.' | ForEach-Object {
            if ($_ -match '^\d+$') { [int]$_ } else { 0 }
        }
        # Pad to 6 segments so all IDs compare at consistent depth
        while ($segments.Count -lt 6) { $segments += 0 }
        # Return a formatted string that sorts lexicographically as integers would
        ($segments | ForEach-Object { $_.ToString('D10') }) -join '.'
    }
}