CIS-M365-Benchmark.psm1

#Requires -Version 5.1

<#
.SYNOPSIS
    CIS Microsoft 365 Foundations Benchmark v5.0.0 Module

.DESCRIPTION
    PowerShell module for auditing Microsoft 365 environments against CIS Benchmark v5.0.0.
    Provides cmdlets for running compliance checks and generating reports with zero false positives.

.NOTES
    Version: 2.4.2
    Author: Mohammed Siddiqui
    Copyright: (c) 2025 Mohammed Siddiqui. MIT License.
#>


# Store the path to the main script for execution
$Script:ComplianceCheckerPath = Join-Path $PSScriptRoot "CIS-M365-Compliance-Checker.ps1"

# Automatic prerequisite installation on module import
$Script:PrerequisiteCheckRun = $false

function Script:Install-PrerequisitesAutomatically {
    $requiredModules = @(
        @{ Name = "Microsoft.Graph"; MinVersion = "2.0.0" }
        @{ Name = "ExchangeOnlineManagement"; MinVersion = $null }
        @{ Name = "Microsoft.Online.SharePoint.PowerShell"; MinVersion = $null }
        @{ Name = "MicrosoftTeams"; MinVersion = $null }
        @{ Name = "MSOnline"; MinVersion = $null }
    )

    $missing = @()
    $needsUpdate = @()
    $needsImport = @()
    $psVersion = $PSVersionTable.PSVersion.Major

    foreach ($module in $requiredModules) {
        $moduleName = $module.Name
        $minVersion = $module.MinVersion
        $installed = Get-Module -ListAvailable -Name $moduleName | Sort-Object Version -Descending | Select-Object -First 1

        if (-not $installed) {
            $missing += $module
        }
        elseif ($minVersion -and $installed.Version -lt [Version]$minVersion) {
            $needsUpdate += $module
        }
        else {
            # Don't auto-import Microsoft.Graph - let Connect-MgGraph handle it
            if ($moduleName -ne "Microsoft.Graph") {
                $loaded = Get-Module -Name $moduleName
                if (-not $loaded) {
                    $needsImport += $moduleName
                }
            }
        }
    }

    # Update outdated modules - CRITICAL for Microsoft.Graph
    if ($needsUpdate.Count -gt 0) {
        Write-Host ""
        Write-Host "================================================================" -ForegroundColor Yellow
        Write-Host " CIS M365 Benchmark - Updating Prerequisites" -ForegroundColor Yellow
        Write-Host "================================================================" -ForegroundColor Yellow
        Write-Host ""
        Write-Host "Updating $($needsUpdate.Count) outdated module(s)..." -ForegroundColor Yellow
        Write-Host ""

        foreach ($module in $needsUpdate) {
            Write-Host " Updating $($module.Name)..." -NoNewline -ForegroundColor White

            # Special handling for Microsoft.Graph - MUST uninstall old versions first
            if ($module.Name -eq "Microsoft.Graph") {
                try {
                    Write-Host ""
                    Write-Host " Removing outdated Microsoft.Graph versions..." -ForegroundColor Gray
                    Get-InstalledModule -Name "Microsoft.Graph" -AllVersions -ErrorAction SilentlyContinue |
                        Where-Object { $_.Version -lt [Version]"2.0.0" } |
                        ForEach-Object {
                            Write-Host " Uninstalling v$($_.Version)..." -NoNewline -ForegroundColor Gray
                            Uninstall-Module -Name "Microsoft.Graph" -RequiredVersion $_.Version -Force -ErrorAction SilentlyContinue
                            Write-Host " [OK]" -ForegroundColor Green
                        }

                    Write-Host " Installing latest Microsoft.Graph..." -NoNewline -ForegroundColor Gray
                    Install-Module -Name "Microsoft.Graph" -Scope CurrentUser -Force -AllowClobber -ErrorAction Stop -WarningAction SilentlyContinue | Out-Null
                    Write-Host " [OK]" -ForegroundColor Green
                    Write-Host " Microsoft.Graph update complete" -ForegroundColor Green
                    $needsImport += $module.Name
                }
                catch {
                    Write-Host " [FAILED]: $_" -ForegroundColor Red
                }
            }
            else {
                # For other modules, use standard update
                try {
                    Update-Module -Name $module.Name -Force -ErrorAction Stop -WarningAction SilentlyContinue | Out-Null
                    Write-Host " [OK]" -ForegroundColor Green
                    $needsImport += $module.Name
                }
                catch {
                    try {
                        Install-Module -Name $module.Name -Scope CurrentUser -Force -AllowClobber -ErrorAction Stop -WarningAction SilentlyContinue | Out-Null
                        Write-Host " [REINSTALLED]" -ForegroundColor Green
                        $needsImport += $module.Name
                    }
                    catch {
                        Write-Host " [FAILED]" -ForegroundColor Yellow
                    }
                }
            }
        }
        Write-Host ""
    }

    # Install missing modules
    if ($missing.Count -gt 0) {
        Write-Host ""
        Write-Host "================================================================" -ForegroundColor Cyan
        Write-Host " CIS M365 Benchmark - Auto-Installing Prerequisites" -ForegroundColor Cyan
        Write-Host "================================================================" -ForegroundColor Cyan
        Write-Host ""
        Write-Host "Installing $($missing.Count) missing module(s)..." -ForegroundColor Yellow
        Write-Host "PowerShell Version: $psVersion" -ForegroundColor Gray
        Write-Host ""

        foreach ($module in $missing) {
            try {
                Write-Host " Installing $($module.Name)..." -NoNewline -ForegroundColor White
                Install-Module -Name $module.Name -Scope CurrentUser -Force -AllowClobber -ErrorAction Stop -WarningAction SilentlyContinue -Repository PSGallery | Out-Null
                Write-Host " [OK]" -ForegroundColor Green
                $needsImport += $module.Name
            }
            catch {
                Write-Host " [FAILED]: $_" -ForegroundColor Red
            }
        }

        Write-Host ""
        Write-Host "================================================================" -ForegroundColor Cyan
        Write-Host " Installation complete!" -ForegroundColor Cyan
        Write-Host "================================================================" -ForegroundColor Cyan
        Write-Host ""
    }

    # Import modules that aren't loaded
    if ($needsImport.Count -gt 0) {
        Write-Host "Loading prerequisite modules..." -ForegroundColor Cyan

        foreach ($moduleName in $needsImport) {
            try {
                Write-Host " Loading $moduleName..." -NoNewline -ForegroundColor Gray
                if ($psVersion -ge 7) {
                    Import-Module -Name $moduleName -ErrorAction Stop -WarningAction SilentlyContinue -DisableNameChecking -SkipEditionCheck -Force | Out-Null
                }
                else {
                    Import-Module -Name $moduleName -ErrorAction Stop -WarningAction SilentlyContinue -DisableNameChecking -Force | Out-Null
                }
                Write-Host " [OK]" -ForegroundColor Green
            }
            catch {
                # Don't block module loading if a prerequisite fails to import
                # It will be caught later when the user runs Invoke-CISBenchmark
                Write-Host " [WARNING]" -ForegroundColor Yellow
            }
        }
        Write-Host ""
    }
}

# Function to fix Microsoft.Graph version issues
function Script:Fix-MicrosoftGraphVersion {
    try {
        $graphModule = Get-Module -ListAvailable -Name Microsoft.Graph | Sort-Object Version -Descending | Select-Object -First 1

        if ($graphModule -and $graphModule.Version -lt [Version]"2.0.0") {
            Write-Host ""
            Write-Host "================================================================" -ForegroundColor Yellow
            Write-Host " Updating Microsoft.Graph to fix authentication issues" -ForegroundColor Yellow
            Write-Host "================================================================" -ForegroundColor Yellow
            Write-Host ""
            Write-Host "Current version: $($graphModule.Version)" -ForegroundColor Gray
            Write-Host "Required version: 2.0.0 or higher" -ForegroundColor Gray
            Write-Host ""
            Write-Host "Updating Microsoft.Graph module..." -ForegroundColor Yellow

            try {
                # Uninstall old versions first
                Get-InstalledModule -Name Microsoft.Graph -AllVersions -ErrorAction SilentlyContinue |
                    Where-Object { $_.Version -lt [Version]"2.0.0" } |
                    ForEach-Object {
                        Write-Host " Removing old version $($_.Version)..." -NoNewline -ForegroundColor Gray
                        Uninstall-Module -Name Microsoft.Graph -RequiredVersion $_.Version -Force -ErrorAction SilentlyContinue
                        Write-Host " [OK]" -ForegroundColor Green
                    }

                # Install latest version
                Write-Host " Installing latest Microsoft.Graph..." -NoNewline -ForegroundColor White
                Install-Module -Name Microsoft.Graph -Scope CurrentUser -Force -AllowClobber -ErrorAction Stop -WarningAction SilentlyContinue
                Write-Host " [OK]" -ForegroundColor Green

                Write-Host ""
                Write-Host "================================================================" -ForegroundColor Green
                Write-Host " Microsoft.Graph updated successfully!" -ForegroundColor Green
                Write-Host "================================================================" -ForegroundColor Green
                Write-Host ""
                Write-Host "Please restart PowerShell and run Invoke-CISBenchmark again." -ForegroundColor Yellow
                Write-Host ""
                return $true
            }
            catch {
                Write-Host " [FAILED]" -ForegroundColor Red
                Write-Host ""
                Write-Host "Unable to auto-update Microsoft.Graph. Please update manually:" -ForegroundColor Yellow
                Write-Host " Install-Module -Name Microsoft.Graph -Force -AllowClobber" -ForegroundColor White
                Write-Host ""
                return $false
            }
        }
    }
    catch {
        # Silently continue if version check fails
    }
    return $false
}

# Automatically install missing prerequisites when module is first imported
if (-not $Script:PrerequisiteCheckRun) {
    Script:Install-PrerequisitesAutomatically
    $Script:PrerequisiteCheckRun = $true
}

<#
.SYNOPSIS
    Connects to Microsoft 365 services for CIS Benchmark compliance checks.

.DESCRIPTION
    Authenticates to Microsoft Graph with the necessary permissions for running
    CIS Microsoft 365 Foundations Benchmark compliance checks. Attempts browser-based
    authentication first, then falls back to device code authentication if needed.

.PARAMETER Scopes
    Optional custom Microsoft Graph permission scopes. If not specified, uses
    the default scopes required for CIS benchmark checks.

.EXAMPLE
    Connect-CISBenchmark

    Authenticates to Microsoft 365. Opens a browser window or displays a device code for authentication.

.EXAMPLE
    Connect-CISBenchmark -Scopes "Organization.Read.All", "User.Read.All"

    Connects with custom permission scopes.

.OUTPUTS
    PSCustomObject
    Returns the Microsoft Graph context if successful.

.NOTES
    This function must be run before Invoke-CISBenchmark to establish authentication.
    Uses interactive browser-based authentication compatible with the latest Microsoft.Graph SDK.

.LINK
    https://github.com/mohammedsiddiqui6872/CIS-Microsoft-365-Foundations-Benchmark-v5.0.0
#>

function Connect-CISBenchmark {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory=$false)]
        [string[]]$Scopes = @(
            "Organization.Read.All",
            "Directory.Read.All",
            "Policy.Read.All",
            "UserAuthenticationMethod.Read.All",
            "RoleManagement.Read.All",
            "User.Read.All",
            "Group.Read.All",
            "Application.Read.All"
        ),

        # Use device code flow for authentication
        [switch]$UseDeviceCode
    )

    Write-Host "`nConnecting to Microsoft Graph" -ForegroundColor Yellow

    try {
        # Ensure Microsoft.Graph.Authentication is loaded before calling Connect-MgGraph
        if (-not (Get-Module -Name Microsoft.Graph.Authentication)) {
            Import-Module Microsoft.Graph.Authentication -ErrorAction Stop
        }

        $params = @{
            Scopes        = $Scopes
            NoWelcome     = $true
            ContextScope  = 'Process'
        }

        if ($UseDeviceCode) {
            $params['UseDeviceCode'] = $true
        }

        Connect-MgGraph @params

        $context = Get-MgContext

        if ($context -and $context.TenantId) {
            Write-Host "`nSuccessfully connected to Microsoft Graph!" -ForegroundColor Green
            Write-Host " Tenant ID: $($context.TenantId)" -ForegroundColor White
            Write-Host " Account: $($context.Account)" -ForegroundColor White
            Write-Host "`nYou can now run: Invoke-CISBenchmark`n" -ForegroundColor Yellow
            return $context
        }
        else {
            throw "Connection established but unable to retrieve context"
        }
    }
    catch [Management.Automation.CommandNotFoundException] {
        Write-Host "`nThe Microsoft Graph PowerShell module is not installed." -ForegroundColor Red
        Write-Host "Please install it using: Install-Module Microsoft.Graph -Scope CurrentUser`n" -ForegroundColor Yellow
        throw
    }
    catch {
        Write-Host "`nFailed to connect to Microsoft Graph" -ForegroundColor Red
        Write-Host "Error: $_`n" -ForegroundColor Red
        throw
    }
}

<#
.SYNOPSIS
    Invokes CIS Microsoft 365 Foundations Benchmark compliance checks.

.DESCRIPTION
    Runs automated compliance checks against your Microsoft 365 tenant based on
    CIS Microsoft 365 Foundations Benchmark v5.0.0. Generates HTML and CSV reports
    showing pass/fail status for all 130 controls across 9 sections.

.PARAMETER TenantDomain
    Your Microsoft 365 tenant domain (e.g., contoso.onmicrosoft.com)

.PARAMETER SharePointAdminUrl
    Your SharePoint admin URL (e.g., https://contoso-admin.sharepoint.com)

.PARAMETER OutputPath
    Directory path where reports will be saved. Default: Current directory

.PARAMETER ProfileLevel
    CIS profile level to check: 'L1', 'L2', or 'All'. Default: 'All'
    - L1: Level 1 controls (baseline security)
    - L2: Level 2 controls (enhanced security)
    - All: Both L1 and L2 controls

.PARAMETER Format
    Output format: 'Both', 'HTML', or 'CSV'. Default: 'Both'

.PARAMETER Sections
    Specific benchmark sections to check (e.g., @('1','2','3') or @('1.1','2.1'))
    If not specified, all sections are checked.

.EXAMPLE
    Connect-CISBenchmark
    Invoke-CISBenchmark

    First authenticate, then run all compliance checks with auto-detected tenant information.

.EXAMPLE
    Connect-CISBenchmark
    Invoke-CISBenchmark -ProfileLevel "L1"

    Runs only Level 1 (baseline) compliance checks with auto-detected tenant information.

.EXAMPLE
    Connect-CISBenchmark
    Invoke-CISBenchmark -TenantDomain "contoso.onmicrosoft.com" -SharePointAdminUrl "https://contoso-admin.sharepoint.com"

    Runs all compliance checks with manually specified tenant information.

.EXAMPLE
    Invoke-CISBenchmark -Format "HTML" -OutputPath "C:\CIS-Reports"

    Generates only HTML report and saves it to specified directory. Tenant info auto-detected.

.EXAMPLE
    Invoke-CISBenchmark -Sections @('1','2','5')

    Runs only Section 1 (M365 Admin), Section 2 (Defender), and Section 5 (Entra ID) checks.

.EXAMPLE
    Invoke-CISBenchmark -Verbose

    Runs all checks with verbose output showing detailed progress.

.OUTPUTS
    System.Management.Automation.PSCustomObject
    Returns a summary object containing:
    - TotalControls: Total number of controls checked
    - Passed: Number of passing controls
    - Failed: Number of failing controls
    - Manual: Number of controls requiring manual review
    - Errors: Number of controls with errors
    - ComplianceRate: Percentage of automated controls that passed
    - ReportPaths: Array of generated report file paths

.NOTES
    Required Modules:
    - Microsoft.Graph
    - ExchangeOnlineManagement
    - Microsoft.Online.SharePoint.PowerShell
    - MicrosoftTeams
    - MSOnline (optional)

    Required Permissions:
    - Global Reader (recommended) or equivalent read permissions
    - See PERMISSIONS.md for detailed permission requirements

    Authentication:
    - Run Connect-CISBenchmark first to authenticate
    - Or the function will attempt auto-authentication if not already connected

.LINK
    https://github.com/mohammedsiddiqui6872/CIS-Microsoft-365-Foundations-Benchmark-v5.0.0

.LINK
    https://www.cisecurity.org/benchmark/microsoft_365
#>

function Invoke-CISBenchmark {
    [CmdletBinding()]
    [OutputType([PSCustomObject])]
    param(
        [Parameter(Mandatory=$false, Position=0, HelpMessage="Your M365 tenant domain (e.g., contoso.onmicrosoft.com). If not provided, will be auto-detected.")]
        [string]$TenantDomain,

        [Parameter(Mandatory=$false, Position=1, HelpMessage="SharePoint admin URL (e.g., https://contoso-admin.sharepoint.com). If not provided, will be auto-detected.")]
        [ValidatePattern('^https://.*-admin\.sharepoint\.com/?$')]
        [string]$SharePointAdminUrl,

        [Parameter(Mandatory=$false)]
        [ValidateScript({Test-Path -Path $_ -PathType Container -IsValid})]
        [string]$OutputPath = ".",

        [Parameter(Mandatory=$false)]
        [ValidateSet('L1','L2','All')]
        [string]$ProfileLevel = 'All',

        [Parameter(Mandatory=$false)]
        [ValidateSet('Both','HTML','CSV')]
        [string]$Format = 'Both',

        [Parameter(Mandatory=$false)]
        [string[]]$Sections
    )

    begin {
        Write-Verbose "Starting CIS Microsoft 365 Foundations Benchmark v5.0.0 Compliance Check"

        # Auto-detect tenant domain and SharePoint URL if not provided
        if ([string]::IsNullOrEmpty($TenantDomain) -or [string]::IsNullOrEmpty($SharePointAdminUrl)) {
            Write-Host ""
            Write-Host "================================================================" -ForegroundColor Cyan
            Write-Host " Auto-Detecting Microsoft 365 Tenant Information" -ForegroundColor Cyan
            Write-Host "================================================================" -ForegroundColor Cyan
            Write-Host ""

            try {
                # Check for existing Microsoft Graph connection
                Write-Host "Checking Microsoft Graph connection..." -ForegroundColor Gray
                $graphContext = Get-MgContext -ErrorAction SilentlyContinue

                if (-not $graphContext) {
                    Write-Host ""
                    Write-Host "Not authenticated to Microsoft Graph." -ForegroundColor Yellow
                    Write-Host ""
                    Write-Host "Please run: Connect-CISBenchmark" -ForegroundColor Cyan
                    Write-Host ""
                    Write-Host "Attempting automatic authentication..." -ForegroundColor Yellow

                    try {
                        Connect-MgGraph -Scopes "Organization.Read.All","Directory.Read.All" -ErrorAction Stop | Out-Null
                        $graphContext = Get-MgContext
                        Write-Host "Authentication successful!" -ForegroundColor Green
                    }
                    catch {
                        Write-Host ""
                        Write-Host "================================================================" -ForegroundColor Red
                        Write-Host " Authentication Failed" -ForegroundColor Red
                        Write-Host "================================================================" -ForegroundColor Red
                        Write-Host ""
                        Write-Host "Please authenticate first by running:" -ForegroundColor Yellow
                        Write-Host " Connect-CISBenchmark" -ForegroundColor Cyan
                        Write-Host ""
                        Write-Host "Or provide tenant information manually:" -ForegroundColor Yellow
                        Write-Host " Invoke-CISBenchmark -TenantDomain 'tenant.onmicrosoft.com' -SharePointAdminUrl 'https://tenant-admin.sharepoint.com'" -ForegroundColor Cyan
                        Write-Host ""
                        throw "Authentication required. Run Connect-CISBenchmark first or provide tenant parameters manually."
                    }
                }
                else {
                    Write-Host "Using existing Microsoft Graph connection" -ForegroundColor Green
                    Write-Host " Account: $($graphContext.Account)" -ForegroundColor Gray
                }

                # Get organization details
                Write-Host "Retrieving tenant information..." -ForegroundColor Gray
                $orgDetails = Get-MgOrganization -ErrorAction Stop | Select-Object -First 1

                if ([string]::IsNullOrEmpty($TenantDomain)) {
                    # Get the primary verified domain or onmicrosoft.com domain
                    $verifiedDomains = $orgDetails.VerifiedDomains | Where-Object { $_.Name -like "*.onmicrosoft.com" }
                    if ($verifiedDomains) {
                        $TenantDomain = @($verifiedDomains)[0].Name
                    } else {
                        $TenantDomain = @($orgDetails.VerifiedDomains)[0].Name
                    }
                    Write-Host " Detected Tenant Domain: $TenantDomain" -ForegroundColor Green
                }

                if ([string]::IsNullOrEmpty($SharePointAdminUrl)) {
                    # Construct SharePoint admin URL from tenant name
                    $tenantName = $TenantDomain.Split('.')[0]
                    $SharePointAdminUrl = "https://$tenantName-admin.sharepoint.com"
                    Write-Host " Detected SharePoint Admin URL: $SharePointAdminUrl" -ForegroundColor Green
                }

                Write-Host ""
            }
            catch {
                Write-Host ""
                Write-Host "Failed to auto-detect tenant information: $_" -ForegroundColor Red
                Write-Host ""
                Write-Host "Please authenticate first:" -ForegroundColor Yellow
                Write-Host " Connect-CISBenchmark" -ForegroundColor Cyan
                Write-Host ""
                Write-Host "Or provide the tenant information manually:" -ForegroundColor Yellow
                Write-Host " Invoke-CISBenchmark -TenantDomain 'tenant.onmicrosoft.com' -SharePointAdminUrl 'https://tenant-admin.sharepoint.com'" -ForegroundColor Cyan
                Write-Host ""
                throw "Auto-detection failed. Run Connect-CISBenchmark or provide tenant parameters manually."
            }
        }

        Write-Verbose "Tenant: $TenantDomain"
        Write-Verbose "SharePoint URL: $SharePointAdminUrl"
        Write-Verbose "Profile Level: $ProfileLevel"

        # Check and fix Microsoft.Graph version if needed
        $graphFixed = Script:Fix-MicrosoftGraphVersion
        if ($graphFixed) {
            Write-Host "Microsoft.Graph has been updated. Please restart PowerShell and run this command again." -ForegroundColor Yellow
            return
        }

        # Ensure output directory exists
        if (-not (Test-Path -Path $OutputPath)) {
            Write-Verbose "Creating output directory: $OutputPath"
            New-Item -Path $OutputPath -ItemType Directory -Force | Out-Null
        }
    }

    process {
        try {
            # Clean SharePoint URL - remove trailing slash if present
            $cleanSharePointUrl = $SharePointAdminUrl.TrimEnd('/')

            # Build parameters for the script
            $scriptParams = @{
                TenantDomain = $TenantDomain
                SharePointAdminUrl = $cleanSharePointUrl
                OutputPath = $OutputPath
                ProfileLevel = $ProfileLevel
            }

            Write-Verbose "Executing CIS compliance checker script..."

            # Execute the compliance checker script
            & $Script:ComplianceCheckerPath @scriptParams

            Write-Verbose "Generating summary report..."

            # Get the most recent report files
            $htmlReport = Get-ChildItem -Path $OutputPath -Filter "CIS-M365-Compliance-Report_*.html" -ErrorAction SilentlyContinue |
                Sort-Object LastWriteTime -Descending |
                Select-Object -First 1

            $csvReport = Get-ChildItem -Path $OutputPath -Filter "CIS-M365-Compliance-Report_*.csv" -ErrorAction SilentlyContinue |
                Sort-Object LastWriteTime -Descending |
                Select-Object -First 1

            # Parse CSV to get summary statistics
            if ($csvReport) {
                $reportData = Import-Csv -Path $csvReport.FullName
                $passed = ($reportData | Where-Object { $_.Result -eq 'Pass' }).Count
                $failed = ($reportData | Where-Object { $_.Result -eq 'Fail' }).Count
                $manual = ($reportData | Where-Object { $_.Result -eq 'Manual' }).Count
                $errors = ($reportData | Where-Object { $_.Result -eq 'Error' }).Count
                $total = $reportData.Count

                $complianceRate = if ($total -gt 0 -and ($total - $manual) -gt 0) {
                    [math]::Round(($passed / ($total - $manual)) * 100, 2)
                } else {
                    0
                }

                # Return summary object
                $summary = [PSCustomObject]@{
                    TenantDomain = $TenantDomain
                    ProfileLevel = $ProfileLevel
                    TotalControls = $total
                    Passed = $passed
                    Failed = $failed
                    Manual = $manual
                    Errors = $errors
                    ComplianceRate = $complianceRate
                    Timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
                    HtmlReport = if ($htmlReport) { $htmlReport.FullName } else { $null }
                    CsvReport = if ($csvReport) { $csvReport.FullName } else { $null }
                }

                return $summary
            }
            else {
                Write-Warning "No CSV report found. Check output path: $OutputPath"
            }

        }
        catch {
            Write-Error "Failed to execute CIS benchmark: $_"
            throw
        }
    }

    end {
        Write-Verbose "CIS benchmark check completed"
    }
}

<#
.SYNOPSIS
    Gets information about CIS benchmark controls.

.DESCRIPTION
    Retrieves details about specific CIS Microsoft 365 Foundations Benchmark controls,
    including control number, title, profile level, and description.

.PARAMETER ControlNumber
    Specific control number(s) to retrieve (e.g., "1.1.1", "2.1.3")
    If not specified, returns all controls.

.PARAMETER Section
    Return all controls in a specific section (e.g., "1", "2", "5")

.PARAMETER ProfileLevel
    Filter by profile level: 'L1', 'L2', or 'All'

.EXAMPLE
    Get-CISBenchmarkControl -ControlNumber "1.1.1"

    Gets details for control 1.1.1

.EXAMPLE
    Get-CISBenchmarkControl -Section "5"

    Gets all controls in Section 5 (Entra ID)

.EXAMPLE
    Get-CISBenchmarkControl -ProfileLevel "L1"

    Gets all Level 1 controls

.OUTPUTS
    System.Management.Automation.PSCustomObject
    Control information including number, title, section, profile level, and automation status
#>

function Get-CISBenchmarkControl {
    [CmdletBinding(DefaultParameterSetName='All')]
    [OutputType([PSCustomObject[]])]
    param(
        [Parameter(ParameterSetName='ByControl', Position=0)]
        [string[]]$ControlNumber,

        [Parameter(ParameterSetName='BySection')]
        [ValidateSet('1','2','3','4','5','6','7','8','9')]
        [string]$Section,

        [Parameter()]
        [ValidateSet('L1','L2','All')]
        [string]$ProfileLevel = 'All'
    )

    # Define control metadata (this would ideally be loaded from a data file)
    $controls = @(
        [PSCustomObject]@{ControlNumber="1.1.1"; Title="Ensure Administrative accounts are cloud-only"; Section="1"; ProfileLevel="L1"; Automated=$true}
        [PSCustomObject]@{ControlNumber="1.1.2"; Title="Ensure two emergency access accounts have been defined"; Section="1"; ProfileLevel="L1"; Automated=$false}
        [PSCustomObject]@{ControlNumber="1.1.3"; Title="Ensure that between two and four global admins are designated"; Section="1"; ProfileLevel="L1"; Automated=$true}
        # ... (Additional controls would be defined here or loaded from JSON/XML)
    )

    # Filter based on parameters
    $results = $controls

    if ($PSCmdlet.ParameterSetName -eq 'ByControl' -and $ControlNumber) {
        $results = $results | Where-Object { $_.ControlNumber -in $ControlNumber }
    }

    if ($PSCmdlet.ParameterSetName -eq 'BySection' -and $Section) {
        $results = $results | Where-Object { $_.Section -eq $Section }
    }

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

    return $results
}

<#
.SYNOPSIS
    Tests if required PowerShell modules are installed.

.DESCRIPTION
    Checks if all required Microsoft 365 PowerShell modules are installed
    and reports their versions and installation status.

.EXAMPLE
    Test-CISBenchmarkPrerequisites

    Checks all required modules and displays their status.

.OUTPUTS
    System.Management.Automation.PSCustomObject
    Module name, version, installation status, and required status
#>

function Test-CISBenchmarkPrerequisites {
    [CmdletBinding()]
    [OutputType([PSCustomObject[]])]
    param()

    $requiredModules = @(
        @{Name="Microsoft.Graph"; Required=$true}
        @{Name="ExchangeOnlineManagement"; Required=$true}
        @{Name="Microsoft.Online.SharePoint.PowerShell"; Required=$true}
        @{Name="MicrosoftTeams"; Required=$true}
        @{Name="MSOnline"; Required=$false}
    )

    $results = @()

    foreach ($module in $requiredModules) {
        $installed = Get-Module -ListAvailable -Name $module.Name | Select-Object -First 1

        $results += [PSCustomObject]@{
            Module = $module.Name
            Installed = $null -ne $installed
            Version = if ($installed) { $installed.Version.ToString() } else { "Not Installed" }
            Required = $module.Required
            Status = if ($null -ne $installed) { "[OK] Installed" } elseif ($module.Required) { "[!] Required - Not Installed" } else { "[*] Optional - Not Installed" }
        }
    }

    return $results
}

<#
.SYNOPSIS
    Displays information about the CIS M365 Benchmark module.

.DESCRIPTION
    Shows module version, available commands, and useful links.

.EXAMPLE
    Get-CISBenchmarkInfo

    Displays module information.
#>

function Get-CISBenchmarkInfo {
    [CmdletBinding()]
    param()

    $module = Get-Module -Name CIS-M365-Benchmark

    Write-Host ""
    Write-Host "================================================================" -ForegroundColor Cyan
    Write-Host " CIS Microsoft 365 Foundations Benchmark v5.0.0" -ForegroundColor Cyan
    Write-Host " PowerShell Module v$($module.Version)" -ForegroundColor Cyan
    Write-Host "================================================================" -ForegroundColor Cyan
    Write-Host ""
    Write-Host "Available Commands:" -ForegroundColor Yellow
    Write-Host " - Connect-CISBenchmark - Authenticate to Microsoft 365" -ForegroundColor White
    Write-Host " - Invoke-CISBenchmark - Run compliance checks" -ForegroundColor White
    Write-Host " - Get-CISBenchmarkControl - Get control information" -ForegroundColor White
    Write-Host " - Test-CISBenchmarkPrerequisites - Check required modules" -ForegroundColor White
    Write-Host " - Get-CISBenchmarkInfo - Display this information" -ForegroundColor White
    Write-Host ""
    Write-Host "Quick Start:" -ForegroundColor Yellow
    Write-Host " 1. Authenticate:" -ForegroundColor Gray
    Write-Host " Connect-CISBenchmark" -ForegroundColor Green
    Write-Host ""
    Write-Host " 2. Run compliance checks:" -ForegroundColor Gray
    Write-Host " Invoke-CISBenchmark" -ForegroundColor Green
    Write-Host ""
    Write-Host " Or manually specify tenant:" -ForegroundColor Gray
    Write-Host " Invoke-CISBenchmark -TenantDomain 'contoso.onmicrosoft.com' \" -ForegroundColor Green
    Write-Host " -SharePointAdminUrl 'https://contoso-admin.sharepoint.com'" -ForegroundColor Green
    Write-Host ""
    Write-Host "Help:" -ForegroundColor Yellow
    Write-Host " Get-Help Invoke-CISBenchmark -Full" -ForegroundColor White
    Write-Host ""
    Write-Host "Links:" -ForegroundColor Yellow
    Write-Host " - Documentation: https://github.com/mohammedsiddiqui6872/CIS-M365-Benchmark" -ForegroundColor White
    Write-Host " - CIS Benchmark: https://www.cisecurity.org/benchmark/microsoft_365" -ForegroundColor White
    Write-Host " - PowerShell Gallery: https://www.powershellgallery.com/packages/CIS-M365-Benchmark" -ForegroundColor White
    Write-Host ""
    Write-Host "================================================================" -ForegroundColor Cyan
    Write-Host ""
}

# Export module members
Export-ModuleMember -Function @(
    'Connect-CISBenchmark',
    'Invoke-CISBenchmark',
    'Get-CISBenchmarkControl',
    'Test-CISBenchmarkPrerequisites',
    'Get-CISBenchmarkInfo'
)