From 6098c26ce50e0e49c8a105c8f26fdf1eaa0163f8 Mon Sep 17 00:00:00 2001 From: DrIOS <58635327+DrIOSX@users.noreply.github.com> Date: Sat, 19 Apr 2025 19:20:20 -0500 Subject: [PATCH] fix: Minimum version in module install --- CHANGELOG.md | 4 +- source/Private/Assert-ModuleAvailability.ps1 | 75 ++++++++++++-------- source/Public/Invoke-M365SecurityAudit.ps1 | 3 + 3 files changed, 53 insertions(+), 29 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dd98db5..f7c831a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,8 +6,8 @@ The format is based on and uses the types of changes according to [Keep a Change ### Added -- Link to App Authentication documentation in `New-M365SecurityAuditAuthObject` help file. - TestDefinitions-v4.0.0.csv file to the helper folder for version choices. +- Link to App Authentication documentation in `New-M365SecurityAuditAuthObject` help file. - Test Definition Placeholders - Steps to function to account for new logic and create an updated test definition object when version 4.0.0 is selected. - Test-AdministrativeAccountCompliance4 function for v4.0.0 rec# 1.1.1 test. @@ -26,6 +26,8 @@ The format is based on and uses the types of changes according to [Keep a Change - Fixed Pnp PowerShell MgGraph assembly load error with workaround to load the MgGraph assembly as soon as it's imported with a call to Get-MgGroup. - Phish policy test to return if highest priority policy conforms to the benchmark. +- Module assertion to check for minimum version of required modules. +- Module assertion to not import the module if it already exists. ## [v0.1.28] - 2025-01-14 diff --git a/source/Private/Assert-ModuleAvailability.ps1 b/source/Private/Assert-ModuleAvailability.ps1 index 0499bfc..569895a 100644 --- a/source/Private/Assert-ModuleAvailability.ps1 +++ b/source/Private/Assert-ModuleAvailability.ps1 @@ -7,44 +7,63 @@ function Assert-ModuleAvailability { [string[]]$SubModules = @() ) process { + # If $script:PnpAuth = $true, check for powershell version 7.x or higher or throw error + if ($script:PnpAuth -and $PSVersionTable.PSVersion.Major -lt 7) { + throw 'PnP.PowerShell module requires PowerShell 7.x or higher.' + } try { - $module = Get-Module -ListAvailable -Name $ModuleName | Where-Object { $_.Version -ge $RequiredVersion } - if ($null -eq $module) { - Write-Verbose "Installing $ModuleName module..." - Install-Module -Name $ModuleName -RequiredVersion $RequiredVersion -Force -AllowClobber -Scope CurrentUser | Out-Null - } - elseif ($module.Version -lt $RequiredVersion) { - Write-Verbose "Updating $ModuleName module to required version..." - Update-Module -Name $ModuleName -RequiredVersion $RequiredVersion -Force | Out-Null - } - else { - Write-Verbose "$ModuleName module is already at required version or newer." - } - if ($ModuleName -eq "Microsoft.Graph") { - # "Preloading Microsoft.Graph assembly to prevent type-loading issues..." - Write-Verbose "Preloading Microsoft.Graph assembly to prevent type-loading issues..." + switch ($ModuleName) { + 'Microsoft.Graph' { + if ($SubModules.Count -eq 0) { throw 'SubModules cannot be empty for Microsoft.Graph module.' } try { - # Run a harmless cmdlet to preload the assembly + foreach ($subModule in $SubModules) { + if (Get-Module -Name "$ModuleName.$subModule" -ListAvailable -ErrorAction SilentlyContinue) { + Write-Verbose "Submodule $ModuleName.$subModule already loaded." + } + else { + Write-Verbose "Importing submodule $ModuleName.$subModule..." + Import-Module "$ModuleName.$subModule" -MinimumVersion $RequiredVersion -ErrorAction Stop | Out-Null + } + } + # Loading assembly to avoid conflict with other modules Get-MgGroup -Top 1 -ErrorAction SilentlyContinue | Out-Null } - catch { - Write-Verbose "Could not preload Microsoft.Graph assembly. Error: $_" + catch [System.IO.FileNotFoundException] { + # Write the error class in verbose + Write-Verbose "Error importing submodule $ModuleName.$subModule`: $($_.Exception.GetType().FullName)" + Write-Verbose "Submodule $ModuleName.$subModule not found. Installing the module..." + foreach ($subModule in $SubModules) { + Write-Verbose "Installing submodule $ModuleName.$subModule..." + Install-Module -Name "$ModuleName.$subModule" -MinimumVersion $RequiredVersion -Force -AllowClobber -Scope CurrentUser | Out-Null + Write-Verbose "Successfully installed $ModuleName.$subModule module." + } + # Loading assembly to avoid conflict with other modules + Get-MgGroup -Top 1 -ErrorAction SilentlyContinue | Out-Null } } - if ($SubModules.Count -gt 0) { - foreach ($subModule in $SubModules) { - Write-Verbose "Importing submodule $ModuleName.$subModule..." - Get-Module "$ModuleName.$subModule" | Import-Module -RequiredVersion $RequiredVersion -ErrorAction Stop | Out-Null + default { + if (Get-Module -Name $ModuleName -ListAvailable -ErrorAction SilentlyContinue) { + Write-Verbose "$ModuleName module already loaded." + return + } + $module = Import-Module $ModuleName -PassThru -ErrorAction SilentlyContinue | Where-Object { $_.Version -ge $RequiredVersion } + if ($null -eq $module) { + Write-Verbose "Installing $ModuleName module..." + Install-Module -Name $ModuleName -MinimumVersion $RequiredVersion -Force -AllowClobber -Scope CurrentUser | Out-Null + } + elseif ($module.Version -lt $RequiredVersion) { + Write-Verbose "Updating $ModuleName module to required version..." + Update-Module -Name $ModuleName -MinimumVersion $RequiredVersion -Force | Out-Null + } + else { + Write-Verbose "$ModuleName module is already at required version or newer." } } - else { - Write-Verbose "Importing module $ModuleName..." - Import-Module -Name $ModuleName -RequiredVersion $RequiredVersion -ErrorAction Stop -WarningAction SilentlyContinue | Out-Null - } + } } catch { - throw "Assert-ModuleAvailability:`n$_" + Write-Verbose 'Assert-ModuleAvailability Error:' + throw $_.Exception.Message } } - } \ No newline at end of file diff --git a/source/Public/Invoke-M365SecurityAudit.ps1 b/source/Public/Invoke-M365SecurityAudit.ps1 index 2f091c8..b6f79ac 100644 --- a/source/Public/Invoke-M365SecurityAudit.ps1 +++ b/source/Public/Invoke-M365SecurityAudit.ps1 @@ -224,6 +224,9 @@ function Invoke-M365SecurityAudit { Assert-ModuleAvailability -ModuleName $module.ModuleName -RequiredVersion $module.RequiredVersion -SubModules $module.SubModules } } + elseif ($script:PnpAuth = $true) { + Get-MgGroup -Top 1 -ErrorAction SilentlyContinue | Out-Null + } # Load test definitions from CSV $testDefinitionsPath = Join-Path -Path $PSScriptRoot -ChildPath 'helper\TestDefinitions.csv' $testDefinitions = Import-Csv -Path $testDefinitionsPath