Public/Get-CIEMRequiredPermission.ps1

function Get-CIEMRequiredPermission {
    <#
    .SYNOPSIS
        Gets the required permissions for running CIEM security checks.

    .DESCRIPTION
        Aggregates all unique permissions required across all enabled checks.
        Returns permissions grouped by type: Microsoft Graph API permissions,
        Azure Resource Manager RBAC actions, and Key Vault data plane permissions.

    .PARAMETER Service
        Filter to permissions required for a specific service (Entra, IAM, KeyVault, Storage).

    .PARAMETER CheckId
        Filter to permissions required for specific check IDs.

    .OUTPUTS
        [PSCustomObject] Object containing:
        - Graph: Array of Microsoft Graph API permissions (e.g., "User.Read.All")
        - ARM: Array of Azure Resource Manager RBAC actions (e.g., "Microsoft.Storage/storageAccounts/read")
        - KeyVaultDataPlane: Array of Key Vault data plane permissions (e.g., "secrets/list")
        - Summary: Human-readable summary text

    .EXAMPLE
        Get-CIEMRequiredPermission
        # Returns all permissions required for all checks

    .EXAMPLE
        Get-CIEMRequiredPermission -Service Entra
        # Returns permissions required for Entra ID checks only

    .EXAMPLE
        Get-CIEMRequiredPermission -CheckId 'entra_security_defaults_enabled', 'entra_global_admin_in_less_than_five_users'
        # Returns permissions for specific checks
    #>

    [CmdletBinding()]
    [OutputType([PSCustomObject])]
    param(
        [Parameter()]
        [ValidateSet('Entra', 'IAM', 'KeyVault', 'Storage')]
        [string]$Service,

        [Parameter()]
        [string[]]$CheckId
    )

    $ErrorActionPreference = 'Stop'

    # Get checks based on filters
    $getCheckParams = @{}
    if ($Service) { $getCheckParams.Service = $Service }

    $checks = Get-CIEMCheck @getCheckParams

    if ($CheckId) {
        $checks = $checks | Where-Object { $CheckId -contains $_.id }
    }

    if (-not $checks) {
        Write-Warning "No checks found matching the specified criteria."
        [PSCustomObject]@{
            Graph             = @()
            ARM               = @()
            KeyVaultDataPlane = @()
            Summary           = "No checks found."
        }
    }
    else {
        # Aggregate unique permissions
        $graphPermissions = @()
        $armPermissions = @()
        $kvPermissions = @()

        foreach ($check in $checks) {
            # Use safe property access since not all checks have all permission types
            $perms = $check.permissions
            if ($perms.PSObject.Properties['graph'] -and $perms.graph) {
                $graphPermissions += $perms.graph
            }
            if ($perms.PSObject.Properties['arm'] -and $perms.arm) {
                $armPermissions += $perms.arm
            }
            if ($perms.PSObject.Properties['keyvaultDataPlane'] -and $perms.keyvaultDataPlane) {
                $kvPermissions += $perms.keyvaultDataPlane
            }
        }

        # Get unique and sort
        $graphPermissions = $graphPermissions | Select-Object -Unique | Sort-Object
        $armPermissions = $armPermissions | Select-Object -Unique | Sort-Object
        $kvPermissions = $kvPermissions | Select-Object -Unique | Sort-Object

        # Build summary
        $summaryParts = @()
        $summaryParts += "Permissions required for $($checks.Count) check(s):"

        if ($graphPermissions.Count -gt 0) {
            $summaryParts += ""
            $summaryParts += "Microsoft Graph API Permissions (Application):"
            foreach ($perm in $graphPermissions) {
                $summaryParts += " - $perm"
            }
        }

        if ($armPermissions.Count -gt 0) {
            $summaryParts += ""
            $summaryParts += "Azure Resource Manager RBAC Actions:"
            foreach ($perm in $armPermissions) {
                $summaryParts += " - $perm"
            }
            $summaryParts += ""
            $summaryParts += " Tip: Assign the 'Reader' role at the subscription/management group level to cover most ARM permissions."
        }

        if ($kvPermissions.Count -gt 0) {
            $summaryParts += ""
            $summaryParts += "Key Vault Data Plane Permissions:"
            foreach ($perm in $kvPermissions) {
                $summaryParts += " - $perm"
            }
            $summaryParts += ""
            $summaryParts += " Tip: Assign 'Key Vault Reader' role and configure access policy or RBAC for data plane access."
        }

        [PSCustomObject]@{
            Graph             = @($graphPermissions)
            ARM               = @($armPermissions)
            KeyVaultDataPlane = @($kvPermissions)
            CheckCount        = $checks.Count
            Summary           = $summaryParts -join "`n"
        }
    }
}