Src/Private/Get-AbrIntuneScripts.ps1

function Get-AbrIntuneScripts {
    <#
    .SYNOPSIS
    Documents Intune PowerShell scripts and Shell scripts deployed via Intune.
    .DESCRIPTION
        Collects and reports on:
          - Windows PowerShell scripts (name, run as, enforcement, assignments)
          - macOS Shell scripts (name, run as, frequency, assignments)
          - Proactive Remediations (detection + remediation script pairs)
    .NOTES
        Version: 0.1.0
        Author: Pai Wei Sing
    #>

    [CmdletBinding()]
    param (
        [Parameter(Position = 0, Mandatory)]
        [string]$TenantId
    )

    begin {
        Write-PScriboMessage -Message "Collecting Intune Scripts for $TenantId."
        Show-AbrDebugExecutionTime -Start -TitleMessage 'Scripts'
    }

    process {
        Section -Style Heading2 'Scripts' {
            $TotalRemediations = 0
            $UnsignedScripts   = 0
            Paragraph "The following section documents scripts deployed via Microsoft Intune in tenant $TenantId."
            BlankLine

            #region Windows PowerShell Scripts
            # Licence gate: deviceManagementScripts requires Intune Plan 1
            if ($script:TenantHasIntuneP1 -eq $false) {
                Write-Host ' - Skipping Windows PowerShell Scripts (Intune Plan 1 not detected).' -ForegroundColor Yellow
                Write-AbrDebugLog 'Windows PowerShell Scripts skipped -- no Intune P1 licence' 'WARN' 'SCRIPTS'
            } else {
            try {
                Write-Host " - Retrieving Windows PowerShell scripts..."
                # /beta required -- deviceManagementScripts is not available in v1.0
                $PSScriptsResp = Invoke-MgGraphRequest -Method GET `
                    -Uri "$($script:GraphEndpoint)/beta/deviceManagement/deviceManagementScripts?$expand=assignments" `
                    -ErrorAction SilentlyContinue
                $PSScripts = $PSScriptsResp.value

                if ($PSScripts -and @($PSScripts).Count -gt 0) {
                    Section -Style Heading3 'Windows PowerShell Scripts' {
                        BlankLine
                        $PSObj = [System.Collections.ArrayList]::new()
                        foreach ($Script in ($PSScripts | Sort-Object displayName)) {
                            $assignResolved = Resolve-IntuneAssignments -Assignments $Script.assignments
                            $AssignedTo = $assignResolved.AssignmentSummary

                            $psInObj = [ordered] @{
                                'Script Name'           = $Script.displayName
                                'Run As Account'        = if ($Script.runAsAccount) { $Script.runAsAccount } else { '--' }
                                'Run As 32-bit'         = $Script.runAs32Bit
                                'Enforce Sig. Check'    = $Script.enforceSignatureCheck
                                'Retry Count'           = if ($null -ne $Script.retryCount) { $Script.retryCount } else { '--' }
                                'Assignments'           = $AssignedTo
                                'Last Modified'         = if ($Script.lastModifiedDateTime) { ([datetime]$Script.lastModifiedDateTime).ToString('yyyy-MM-dd') } else { '--' }
                            }
                            $PSObj.Add([pscustomobject](ConvertTo-HashToYN $psInObj)) | Out-Null
                        }

                        $PSTableParams = @{ Name = "Windows PowerShell Scripts - $TenantId"; ColumnWidths = 24, 14, 11, 12, 10, 17, 12 }
                        if ($Report.ShowTableCaptions) { $PSTableParams['Caption'] = "- $($PSTableParams.Name)" }
                        # Count scripts without signature enforcement
                        $UnsignedScripts += @($PSScripts | Where-Object { -not $_.enforceSignatureCheck }).Count
                        $PSObj | Table @PSTableParams

                        if (Get-IntuneExcelSheetEnabled -SheetKey 'PowerShellScripts') {
                            $script:ExcelSheets['PowerShell Scripts'] = $PSObj
                        }
                        if (Get-IntuneBackupSectionEnabled -SectionKey 'Scripts') { $script:BackupData['PowerShellScripts'] = $PSScripts }
                    }
                }
            } catch {
                if (Test-AbrGraphForbidden -ErrorRecord $_) {
                    Write-AbrPermissionError -Section 'Windows PowerShell Scripts' -RequiredRole 'Intune Service Administrator or Global Administrator'
                } else {
                    Write-AbrSectionError -Section 'Windows PowerShell Scripts' -Message "$($_.Exception.Message)"
                }
            }
            } # end licence gate
            #endregion

            #region macOS Shell Scripts
            # Licence gate: deviceShellScripts requires Intune Plan 1
            if ($script:TenantHasIntuneP1 -eq $false) {
                Write-Host ' - Skipping macOS Shell Scripts (Intune Plan 1 not detected).' -ForegroundColor Yellow
                Write-AbrDebugLog 'macOS Shell Scripts skipped -- no Intune P1 licence' 'WARN' 'SCRIPTS'
            } else {
            try {
                Write-Host " - Retrieving macOS Shell scripts..."
                # /beta required -- deviceShellScripts is not available in v1.0
                $ShellScriptsResp = Invoke-MgGraphRequest -Method GET `
                    -Uri "$($script:GraphEndpoint)/beta/deviceManagement/deviceShellScripts?$expand=assignments" `
                    -ErrorAction SilentlyContinue
                $ShellScripts = $ShellScriptsResp.value

                if ($ShellScripts -and @($ShellScripts).Count -gt 0) {
                    Section -Style Heading3 'macOS Shell Scripts' {
                        BlankLine
                        $ShellObj = [System.Collections.ArrayList]::new()
                        foreach ($Script in ($ShellScripts | Sort-Object displayName)) {
                            $AssignedTo = if ($Script.assignments -and @($Script.assignments).Count -gt 0) {
                                "$(@($Script.assignments).Count) assignment(s)"
                            } else { 'Not assigned' }

                            $shellInObj = [ordered] @{
                                'Script Name'        = $Script.displayName
                                'Run As Account'     = if ($Script.runAsAccount) { $Script.runAsAccount } else { '--' }
                                'Execution Frequency'= if ($Script.executionFrequency) { $Script.executionFrequency } else { '--' }
                                'Retry Count'        = if ($null -ne $Script.retryCount) { $Script.retryCount } else { '--' }
                                'Assignments'        = $AssignedTo
                                'Last Modified'      = if ($Script.lastModifiedDateTime) { ([datetime]$Script.lastModifiedDateTime).ToString('yyyy-MM-dd') } else { '--' }
                            }
                            $ShellObj.Add([pscustomobject]$shellInObj) | Out-Null
                        }

                        $ShellTableParams = @{ Name = "macOS Shell Scripts - $TenantId"; ColumnWidths = 26, 16, 18, 10, 18, 12 }
                        if ($Report.ShowTableCaptions) { $ShellTableParams['Caption'] = "- $($ShellTableParams.Name)" }
                        $ShellObj | Table @ShellTableParams

                        if (Get-IntuneExcelSheetEnabled -SheetKey 'ShellScripts') {
                            $script:ExcelSheets['Shell Scripts'] = $ShellObj
                        }
                        if (Get-IntuneBackupSectionEnabled -SectionKey 'Scripts') {
                            if ($script:BackupData['ShellScripts']) { $script:BackupData['ShellScripts'] += $ShellScripts } else { $script:BackupData['ShellScripts'] = $ShellScripts }
                        }
                    }
                }
            } catch {
                if (Test-AbrGraphForbidden -ErrorRecord $_) {
                    Write-AbrPermissionError -Section 'macOS Shell Scripts' -RequiredRole 'Intune Service Administrator or Global Administrator'
                } else {
                    Write-AbrSectionError -Section 'macOS Shell Scripts' -Message "$($_.Exception.Message)"
                }
            }
            } # end licence gate
            #endregion

            #region Proactive Remediations
            # Licence gate: deviceHealthScripts (Proactive Remediations) requires Intune Plan 2 / Suite
            if ($script:TenantHasIntuneP2 -eq $false) {
                Write-Host ' - Skipping Proactive Remediations (Intune Plan 2 / Suite not detected).' -ForegroundColor Yellow
                Write-AbrDebugLog 'Proactive Remediations skipped -- no Intune P2/Suite licence' 'WARN' 'SCRIPTS'
            } else {
            try {
                Write-Host " - Retrieving Proactive Remediations..."
                # /beta required -- deviceHealthScripts (Proactive Remediations) is not available in v1.0
                $RemediationsResp = Invoke-MgGraphRequest -Method GET `
                    -Uri "$($script:GraphEndpoint)/beta/deviceManagement/deviceHealthScripts?$expand=assignments" `
                    -ErrorAction SilentlyContinue
                $Remediations = $RemediationsResp.value

                $null = ($TotalRemediations = @($Remediations).Count)
                if ($TotalRemediations -gt 0) {
                    Section -Style Heading3 'Proactive Remediations' {
                        BlankLine
                        $RemObj = [System.Collections.ArrayList]::new()
                        foreach ($Rem in ($Remediations | Sort-Object displayName)) {
                            $AssignedTo = if ($Rem.assignments -and @($Rem.assignments).Count -gt 0) {
                                "$(@($Rem.assignments).Count) assignment(s)"
                            } else { 'Not assigned' }

                            $remInObj = [ordered] @{
                                'Remediation Name'      = $Rem.displayName
                                'Publisher'             = if ($Rem.publisher) { $Rem.publisher } else { '--' }
                                'Run As Account'        = if ($Rem.runAsAccount) { $Rem.runAsAccount } else { '--' }
                                'Run As 32-bit'         = $Rem.runAs32Bit
                                'Enforce Sig. Check'    = $Rem.enforceSignatureCheck
                                'Assignments'           = $AssignedTo
                                'Last Modified'         = if ($Rem.lastModifiedDateTime) { ([datetime]$Rem.lastModifiedDateTime).ToString('yyyy-MM-dd') } else { '--' }
                            }
                            $RemObj.Add([pscustomobject](ConvertTo-HashToYN $remInObj)) | Out-Null
                        }

                        $RemTableParams = @{ Name = "Proactive Remediations - $TenantId"; ColumnWidths = 22, 16, 14, 11, 12, 15, 10 }
                        if ($Report.ShowTableCaptions) { $RemTableParams['Caption'] = "- $($RemTableParams.Name)" }
                        $RemObj | Table @RemTableParams

                        if (Get-IntuneExcelSheetEnabled -SheetKey 'ProactiveRemediations') {
                            $script:ExcelSheets['Proactive Remediations'] = $RemObj
                        }
                        if (Get-IntuneBackupSectionEnabled -SectionKey 'Scripts') { $script:BackupData['ProactiveRemediations'] = $Remediations }
                    }
                }
            } catch {
                if (Test-AbrGraphForbidden -ErrorRecord $_) {
                    Write-AbrPermissionError -Section 'Proactive Remediations' -RequiredRole 'Intune Service Administrator or Global Administrator'
                } else {
                    Write-AbrSectionError -Section 'Proactive Remediations' -Message "$($_.Exception.Message)"
                }
            }
            } # end licence gate
            #endregion

            #region ACSC E8 Assessment
            if ($script:IncludeACSCe8) {
                BlankLine
                Paragraph "ACSC Essential Eight Maturity Level Assessment -- Scripts and Remediations:"
                BlankLine
                try {
                    $_v = @{ TotalRemediations = $TotalRemediations; UnsignedScripts = $UnsignedScripts }
                    $E8Checks = Build-AbrIntuneComplianceChecks -Definitions (Get-AbrIntuneE8Checks -Section 'Scripts') -Framework E8 -CallerVariables $_v
                    New-AbrIntuneE8AssessmentTable -Checks $E8Checks -Name 'Scripts' -TenantId $TenantId
                    if ($E8Checks) { $null = $script:E8AllChecks.AddRange([object[]](@($E8Checks | Select-Object @{N='Section';E={'Scripts'}}, ML, Control, Status, Detail))) }
                } catch { Write-AbrSectionError -Section 'E8 Scripts Assessment' -Message "$($_.Exception.Message)" }
            }
            #endregion

            #region CIS Assessment
            if ($script:IncludeCISBaseline) {
                BlankLine
                Paragraph "CIS Microsoft 365 Foundations Benchmark Assessment -- Scripts:"
                BlankLine
                try {
                    $_v = @{ UnsignedScripts = $UnsignedScripts }
                    $CISChecks = Build-AbrIntuneComplianceChecks -Definitions (Get-AbrIntuneCISChecks -Section 'Scripts') -Framework CIS -CallerVariables $_v
                    New-AbrIntuneCISAssessmentTable -Checks $CISChecks -Name 'Scripts' -TenantId $TenantId
                    if ($CISChecks) { $null = $script:CISAllChecks.AddRange([object[]](@($CISChecks | Select-Object @{N='Section';E={'Scripts'}}, CISControl, Level, Status, Detail))) }
                } catch { Write-AbrSectionError -Section 'CIS Scripts Assessment' -Message "$($_.Exception.Message)" }
            }
            #endregion
        }
    }

    end { Show-AbrDebugExecutionTime -End -TitleMessage 'Scripts' }
}