Src/Private/Get-AbrIntuneScripts.ps1
|
function Get-AbrIntuneScripts { <# .SYNOPSIS Documents Intune PowerShell scripts, Shell scripts and Proactive Remediations. .DESCRIPTION InfoLevel 1: Summary table per script type (name, run-as, enforcement, assignments) InfoLevel 2: Per-script detail section with overview + decoded script content .NOTES Version: 0.2.1 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 & Remediations' { $TotalRemediations = 0 $UnsignedScripts = 0 Paragraph "The following section documents scripts and Proactive Remediations deployed via Microsoft Intune in tenant $TenantId." BlankLine #region Helper: decode Base64 script content # Graph returns scriptContent as Base64 -- decode to plain text for the report # Truncate long scripts to keep the report readable function Get-DecodedScriptContent { param([string]$Base64Content, [int]$MaxLines = 40) if (-not $Base64Content) { return '--' } try { $decoded = [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($Base64Content)) $lines = $decoded -split "`n" if ($lines.Count -gt $MaxLines) { $truncated = ($lines | Select-Object -First $MaxLines) -join "`n" return "$truncated`n`n... [Script truncated -- $($lines.Count) lines total. See JSON backup for full content.]" } return $decoded.Trim() } catch { return '[Could not decode script content]' } } #endregion #region Windows PowerShell Scripts 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..." $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) { $UnsignedScripts += @($PSScripts | Where-Object { -not $_.enforceSignatureCheck }).Count 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 -CheckMemberCount:$script:CheckEmptyGroups $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 { '--' } 'Included Groups' = $assignResolved.IncludedGroups 'Excluded Groups' = $assignResolved.ExcludedGroups 'Last Modified' = if ($Script.lastModifiedDateTime) { ([datetime]$Script.lastModifiedDateTime).ToString('yyyy-MM-dd') } else { '--' } } $PSObj.Add([pscustomobject](ConvertTo-HashToYN $psInObj)) | Out-Null } $null = (& { if ($HealthCheck.Intune.EndpointSecurity) { $null = ($PSObj | Where-Object { $_.'Included Groups' -eq '--' } | Set-Style -Style Warning | Out-Null) $null = ($PSObj | Where-Object { $_.'Enforce Sig. Check' -eq 'No' } | Set-Style -Style Warning | Out-Null) }}) $PSTableParams = @{ Name = "Windows PowerShell Scripts - $TenantId"; ColumnWidths = 20, 11, 9, 11, 7, 20, 14, 8 } if ($Report.ShowTableCaptions) { $PSTableParams['Caption'] = "- $($PSTableParams.Name)" } $PSObj | Table @PSTableParams if (Get-IntuneExcelSheetEnabled -SheetKey 'PowerShellScripts') { $script:ExcelSheets['PowerShell Scripts'] = $PSObj } if (Get-IntuneBackupSectionEnabled -SectionKey 'Scripts') { $script:BackupData['PowerShellScripts'] = $PSScripts } #region InfoLevel 2 -- per-script detail + content if ($InfoLevel.Scripts -ge 2) { foreach ($Script in ($PSScripts | Sort-Object displayName)) { $assignResolved = Resolve-IntuneAssignments -Assignments $Script.assignments Section -Style Heading4 $Script.displayName { BlankLine # Overview $ovObj = [System.Collections.ArrayList]::new() $ovObj.Add([pscustomobject]@{ Setting = 'Script Name'; Value = $Script.displayName }) | Out-Null $ovObj.Add([pscustomobject]@{ Setting = 'Description'; Value = if ($Script.description) { $Script.description } else { '--' } }) | Out-Null $ovObj.Add([pscustomobject]@{ Setting = 'Run As Account'; Value = if ($Script.runAsAccount) { $Script.runAsAccount } else { '--' } }) | Out-Null $ovObj.Add([pscustomobject]@{ Setting = 'Run As 32-bit'; Value = if ($Script.runAs32Bit) { 'Yes' } else { 'No' } }) | Out-Null $ovObj.Add([pscustomobject]@{ Setting = 'Enforce Sig. Check'; Value = if ($Script.enforceSignatureCheck) { 'Yes' } else { 'No' } }) | Out-Null $ovObj.Add([pscustomobject]@{ Setting = 'Retry Count'; Value = if ($null -ne $Script.retryCount) { "$($Script.retryCount)" } else { '--' } }) | Out-Null $ovObj.Add([pscustomobject]@{ Setting = 'Filename'; Value = if ($Script.fileName) { $Script.fileName } else { '--' } }) | Out-Null $ovObj.Add([pscustomobject]@{ Setting = 'Included Groups'; Value = $assignResolved.IncludedGroups }) | Out-Null $ovObj.Add([pscustomobject]@{ Setting = 'Excluded Groups'; Value = $assignResolved.ExcludedGroups }) | Out-Null $ovObj.Add([pscustomobject]@{ Setting = 'Scope Tags'; Value = if ($script:ResolveScopeTagNames -and $Script.roleScopeTagIds) { Get-IntuneScopeTagNames -ScopeTagIds $Script.roleScopeTagIds } else { 'Default' } }) | Out-Null $ovObj.Add([pscustomobject]@{ Setting = 'Last Modified'; Value = if ($Script.lastModifiedDateTime) { ([datetime]$Script.lastModifiedDateTime).ToString('yyyy-MM-dd') } else { '--' } }) | Out-Null $OvParams = @{ Name = "Script Detail - $($Script.displayName)"; ColumnWidths = 30, 70 } if ($Report.ShowTableCaptions) { $OvParams['Caption'] = "- $($OvParams.Name)" } $ovObj | Table @OvParams # Script content if ($Script.scriptContent) { BlankLine Paragraph 'Script Content:' BlankLine $decoded = Get-DecodedScriptContent -Base64Content $Script.scriptContent -MaxLines 40 Paragraph $decoded } } } } #endregion } } } 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 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..." $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)) { $assignResolved = Resolve-IntuneAssignments -Assignments $Script.assignments -CheckMemberCount:$script:CheckEmptyGroups $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 { '--' } 'Included Groups' = $assignResolved.IncludedGroups 'Excluded Groups' = $assignResolved.ExcludedGroups '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 = 20, 12, 14, 8, 20, 16, 10 } 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 } } #region InfoLevel 2 if ($InfoLevel.Scripts -ge 2) { foreach ($Script in ($ShellScripts | Sort-Object displayName)) { $assignResolved = Resolve-IntuneAssignments -Assignments $Script.assignments Section -Style Heading4 $Script.displayName { BlankLine $ovObj = [System.Collections.ArrayList]::new() $ovObj.Add([pscustomobject]@{ Setting = 'Script Name'; Value = $Script.displayName }) | Out-Null $ovObj.Add([pscustomobject]@{ Setting = 'Description'; Value = if ($Script.description) { $Script.description } else { '--' } }) | Out-Null $ovObj.Add([pscustomobject]@{ Setting = 'Run As Account'; Value = if ($Script.runAsAccount) { $Script.runAsAccount } else { '--' } }) | Out-Null $ovObj.Add([pscustomobject]@{ Setting = 'Execution Frequency'; Value = if ($Script.executionFrequency) { $Script.executionFrequency } else { '--' } }) | Out-Null $ovObj.Add([pscustomobject]@{ Setting = 'Retry Count'; Value = if ($null -ne $Script.retryCount) { "$($Script.retryCount)" } else { '--' } }) | Out-Null $ovObj.Add([pscustomobject]@{ Setting = 'Filename'; Value = if ($Script.fileName) { $Script.fileName } else { '--' } }) | Out-Null $ovObj.Add([pscustomobject]@{ Setting = 'Included Groups'; Value = $assignResolved.IncludedGroups }) | Out-Null $ovObj.Add([pscustomobject]@{ Setting = 'Excluded Groups'; Value = $assignResolved.ExcludedGroups }) | Out-Null $ovObj.Add([pscustomobject]@{ Setting = 'Last Modified'; Value = if ($Script.lastModifiedDateTime) { ([datetime]$Script.lastModifiedDateTime).ToString('yyyy-MM-dd') } else { '--' } }) | Out-Null $OvParams = @{ Name = "Shell Script Detail - $($Script.displayName)"; ColumnWidths = 30, 70 } if ($Report.ShowTableCaptions) { $OvParams['Caption'] = "- $($OvParams.Name)" } $ovObj | Table @OvParams if ($Script.scriptContent) { BlankLine Paragraph 'Script Content:' BlankLine $decoded = Get-DecodedScriptContent -Base64Content $Script.scriptContent -MaxLines 40 Paragraph $decoded } } } } #endregion } } } 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 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..." $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)) { $assignResolved = Resolve-IntuneAssignments -Assignments $Rem.assignments -CheckMemberCount:$script:CheckEmptyGroups $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 'Included Groups' = $assignResolved.IncludedGroups 'Excluded Groups' = $assignResolved.ExcludedGroups '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 = 18, 12, 11, 8, 10, 18, 15, 8 } 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 } #region InfoLevel 2 -- per-remediation detail + both scripts if ($InfoLevel.Scripts -ge 2) { foreach ($Rem in ($Remediations | Sort-Object displayName)) { $assignResolved = Resolve-IntuneAssignments -Assignments $Rem.assignments Section -Style Heading4 $Rem.displayName { BlankLine # Overview $ovObj = [System.Collections.ArrayList]::new() $ovObj.Add([pscustomobject]@{ Setting = 'Remediation Name'; Value = $Rem.displayName }) | Out-Null $ovObj.Add([pscustomobject]@{ Setting = 'Description'; Value = if ($Rem.description) { $Rem.description } else { '--' } }) | Out-Null $ovObj.Add([pscustomobject]@{ Setting = 'Publisher'; Value = if ($Rem.publisher) { $Rem.publisher } else { '--' } }) | Out-Null $ovObj.Add([pscustomobject]@{ Setting = 'Version'; Value = if ($Rem.version) { $Rem.version } else { '--' } }) | Out-Null $ovObj.Add([pscustomobject]@{ Setting = 'Run As Account'; Value = if ($Rem.runAsAccount) { $Rem.runAsAccount } else { '--' } }) | Out-Null $ovObj.Add([pscustomobject]@{ Setting = 'Run As 32-bit'; Value = if ($Rem.runAs32Bit) { 'Yes' } else { 'No' } }) | Out-Null $ovObj.Add([pscustomobject]@{ Setting = 'Enforce Sig. Check'; Value = if ($Rem.enforceSignatureCheck) { 'Yes' } else { 'No' } }) | Out-Null $ovObj.Add([pscustomobject]@{ Setting = 'Included Groups'; Value = $assignResolved.IncludedGroups }) | Out-Null $ovObj.Add([pscustomobject]@{ Setting = 'Excluded Groups'; Value = $assignResolved.ExcludedGroups }) | Out-Null $ovObj.Add([pscustomobject]@{ Setting = 'Last Modified'; Value = if ($Rem.lastModifiedDateTime) { ([datetime]$Rem.lastModifiedDateTime).ToString('yyyy-MM-dd') } else { '--' } }) | Out-Null $OvParams = @{ Name = "Remediation Detail - $($Rem.displayName)"; ColumnWidths = 30, 70 } if ($Report.ShowTableCaptions) { $OvParams['Caption'] = "- $($OvParams.Name)" } $ovObj | Table @OvParams # Detection script if ($Rem.detectionScriptContent) { BlankLine Paragraph 'Detection Script:' BlankLine $decoded = Get-DecodedScriptContent -Base64Content $Rem.detectionScriptContent -MaxLines 40 Paragraph $decoded } # Remediation script if ($Rem.remediationScriptContent) { BlankLine Paragraph 'Remediation Script:' BlankLine $decoded = Get-DecodedScriptContent -Base64Content $Rem.remediationScriptContent -MaxLines 40 Paragraph $decoded } } } } #endregion } } } 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' } } |