Modules/M365DSCModuleMgmt.psm1
|
$Script:IsPowerShellCore = $PSVersionTable.PSEdition -eq 'Core' $Script:IsPsResourceGetAvailable = $null -ne (Get-Module -Name Microsoft.PowerShell.PSResourceGet -ListAvailable) $Script:M365DSCDependenciesValidated = $false if ($null -eq $Script:M365DSCDependencies) { $Script:M365DSCDependencies = [System.Collections.Generic.Dictionary[System.String, System.Object]]::new([System.StringComparer]::OrdinalIgnoreCase) $dependencies = (Import-PowerShellDataFile "$PSScriptRoot/../Dependencies/Manifest.psd1").Dependencies foreach ($dependency in $dependencies) { # TODO: Review again once ModuleFast can work with additional properties # https://github.com/microsoft/Microsoft365DSC/pull/6726 # https://github.com/ykuijs/M365DSC_CICD/issues/53 if ($dependency.ModuleName -eq 'PnP.PowerShell') { $dependency.DependsOn = @('Microsoft.Graph.Authentication') } $Script:M365DSCDependencies.Add($dependency.ModuleName, $dependency) } $commandToModuleMap = @{} $Script:M365DSCResourceSettings = [System.Collections.Generic.Dictionary[System.String, System.Object]]::new([System.StringComparer]::OrdinalIgnoreCase) foreach ($file in (Get-ChildItem -Path "$PSScriptRoot/../DSCResources" -Filter 'settings.json' -Recurse)) { Write-Verbose -Message "Processing settings.json file at path: $($file.FullName)" $jsonContent = [System.IO.File]::ReadAllText($file.FullName) | ConvertFrom-Json foreach ($commandMap in $jsonContent.commands) { $commandToModuleMap[$commandMap.module] += @($commandMap.cmdlets) } $directoryName = (Split-Path -Path $file.DirectoryName -Leaf).Replace('MSFT_', '') $Script:M365DSCResourceSettings.Add($directoryName, @{ requiredModules = $jsonContent.requiredModules commands = $jsonContent.commands mode = $jsonContent.mode }) } Write-Verbose -Message "Loading current configuration from config.json" $Script:M365DSCValidatedDependencies = [System.Collections.Generic.List[System.String]]::new($Script:M365DSCDependencies.Count) $configAsPsCustomObject = Get-Content -Path "$PSScriptRoot/../config.json" | ConvertFrom-Json $configAsHashtable = @{} foreach ($property in $configAsPsCustomObject.PSObject.Properties) { $configAsHashtable.Add($property.Name, $property.Value) } $Script:CurrentConfiguration = $configAsHashtable $globalRequiredModules = $Script:CurrentConfiguration.requiredModules foreach ($entry in $commandToModuleMap.GetEnumerator()) { $sortedFunctions = @($globalRequiredModules.$($entry.Key)) + @($entry.Value) | Sort-Object -Unique $Script:M365DSCDependencies[$entry.Key].Commands = $sortedFunctions } $Script:M365DSCRequiredModules = @($globalRequiredModules.psobject.Properties.Name) $Script:M365DSCRequiredModulesLoaded = $false } function Get-M365DSCResourceSettings { [CmdletBinding()] param() return $Script:M365DSCResourceSettings } function Get-M365DSCRequiredModules { [CmdletBinding()] param() return $Script:M365DSCRequiredModules } function Set-M365DSCRequiredModulesLoaded { [CmdletBinding()] param( [Parameter(Mandatory = $true)] [System.Boolean]$Value ) $Script:M365DSCRequiredModulesLoaded = $Value } function Test-IsM365DSCRequiredModulesLoaded { [CmdletBinding()] param() return $Script:M365DSCRequiredModulesLoaded } function Get-M365DSCModuleConfiguration { [CmdletBinding()] [OutputType([System.Collections.Hashtable])] param() return $Script:CurrentConfiguration.Clone() } function Set-M365DSCModuleConfiguration { [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [System.String] $Key, [Parameter(Mandatory = $true)] [AllowEmptyCollection()] [AllowEmptyString()] [AllowNull()] [System.Object] $Value ) $Script:CurrentConfiguration.$Key = $Value } <# .DESCRIPTION This function checks if all M365DSC dependencies are present .FUNCTIONALITY Internal #> function Confirm-M365DSCDependencies { [CmdletBinding()] param() if (-not $Script:M365DSCDependenciesValidated -and ($null -eq $Global:M365DSCSkipDependenciesValidation -or -not $Global:M365DSCSkipDependenciesValidation)) { Write-Verbose -Message 'Dependencies were not already validated.' Test-CodePage $result = Update-M365DSCDependencies -ValidateOnly if ($result.Length -gt 0) { $ErrorMessage = "The following dependencies need updating:`r`n" foreach ($invalidDependency in $result) { $ErrorMessage += ' * ' + $invalidDependency.ModuleName + "`r`n" } $ErrorMessage += 'Please run Update-M365DSCDependencies as Administrator. ' $Script:M365DSCDependenciesValidated = $false Add-M365DSCEvent -Message $ErrorMessage -EntryType 'Error' ` -EventID 1 -Source $($MyInvocation.MyCommand.Source) ` -TenantId $tenantIdValue throw $ErrorMessage } else { Write-Verbose -Message 'Dependencies were all successfully validated.' $Script:M365DSCDependenciesValidated = $true } } else { Write-Verbose -Message 'Dependencies were already successfully validated.' } } <# .DESCRIPTION This function checks if a specific module is loaded and validates its version against the required version specified in the M365DSC dependencies manifest. .PARAMETER ModuleName The name of the module to check and validate. .EXAMPLE PS> Confirm-M365DSCLoadedModule -ModuleName 'Microsoft.Graph.Authentication' .FUNCTIONALITY Internal #> function Confirm-M365DSCLoadedModule { [CmdletBinding()] param( [Parameter(Mandatory = $true)] [System.String] $ModuleName ) if ($Script:M365DSCValidatedDependencies.Contains($ModuleName)) { Write-Verbose -Message "Module '$ModuleName' has already been validated." return } $manifestModule = $Script:M365DSCDependencies[$ModuleName] if ($null -ne $manifestModule.DependsOn -and $manifestModule.DependsOn.Count -gt 0) { foreach ($dependency in $manifestModule.DependsOn) { Write-Verbose -Message "Validating dependency '$dependency' for module '$ModuleName'." Confirm-M365DSCLoadedModule -ModuleName $dependency } } $loadedModule = Get-Module -Name $ModuleName if ($null -eq $loadedModule) { Write-Verbose -Message "Module '$ModuleName' is not loaded. Importing it now." $importModuleSplat = @{ Name = $ModuleName RequiredVersion = $manifestModule.RequiredVersion Global = $true Alias = @() Cmdlet = @() Variable = @() DisableNameChecking = $true } if ($manifestModule.Commands.Count -gt 0) { $importModuleSplat.Add('Function', $manifestModule.Commands) $importModuleSplat.Cmdlet = $manifestModule.Commands } if ($ModuleName -eq 'PnP.PowerShell' -and $manifestModule.RequiredVersion -eq '1.12.0' -and $Script:IsPowerShellCore) { $importModuleSplat.Add('UseWindowsPowerShell', $true) } Import-Module @importModuleSplat Write-Verbose -Message "Module '$ModuleName' with version '$($manifestModule.RequiredVersion)' has been imported." } elseif ($loadedModule.Version -ne $manifestModule.RequiredVersion) { Write-Verbose -Message "Module '$ModuleName' is loaded but the version '$($loadedModule.Version)' does not match the required version '$($manifestModule.RequiredVersion)'." Remove-Module -Name $ModuleName -Force -ErrorAction SilentlyContinue Write-Verbose -Message "Unloaded module '$ModuleName' with version '$($loadedModule.Version)'." Import-Module -Name $ModuleName -RequiredVersion $manifestModule.RequiredVersion -Global -Alias @() -Cmdlet @() -Variable @() -DisableNameChecking Write-Verbose -Message "Re-imported module '$ModuleName' with version '$($manifestModule.RequiredVersion)'." } else { Write-Verbose -Message "Module '$ModuleName' is already loaded." } if (-not $Script:M365DSCValidatedDependencies.Contains($ModuleName)) { $Script:M365DSCValidatedDependencies.Add($ModuleName) } } <# .DESCRIPTION This function checks the required dependencies for a specific M365DSC module and validates that they are loaded. .PARAMETER ModuleName The name of the DSC resource for which to check dependencies. .EXAMPLE PS> Confirm-M365DSCModuleDependency -ModuleName 'MSFT_AADApplication' .FUNCTIONALITY Internal #> function Confirm-M365DSCModuleDependency { [CmdletBinding()] param( [Parameter(Mandatory = $true)] [System.String] $ModuleName ) $Global:MaximumFunctionCount = 32767 if ($Global:IsTestEnvironment -or (Get-M365DSCModuleConfiguration).skipModuleDependencyValidation) { Write-Verbose -Message "Skipping module dependency validation in test environment for module '$ModuleName'." return } $modulesToCheck = $Script:M365DSCResourceSettings[$ModuleName.Replace('MSFT_', '')].requiredModules foreach ($module in $modulesToCheck) { Write-Verbose -Message "Validating module dependency: $($module)" Confirm-M365DSCLoadedModule -ModuleName $module } Write-Verbose -Message "All dependencies for module '$ModuleName' have been validated." } <# .DESCRIPTION This function checks if new versions are available for the M365DSC dependencies .EXAMPLE PS> Test-M365DSCDependenciesForNewVersions .FUNCTIONALITY Public #> function Test-M365DSCDependenciesForNewVersions { [CmdletBinding()] param () $i = 1 Import-Module PowerShellGet -Force foreach ($dependency in $Script:M365DSCDependencies.Values.GetEnumerator()) { Write-Progress -Activity 'Scanning Dependencies' -PercentComplete ($i / $Script:M365DSCDependencies.Count * 100) try { $moduleInGallery = Find-Module $dependency.ModuleName [array]$moduleInstalled = Get-Module $dependency.ModuleName -ListAvailable | Select-Object Version if ($moduleInstalled) { $modules = $moduleInstalled | Sort-Object Version -Descending } $moduleInstalled = $modules[0] if (-not $modules -or [Version]($moduleInGallery.Version) -gt [Version]($moduleInstalled[0].Version)) { Write-Host "New version of {$($dependency.ModuleName)} is available {$($moduleInGallery.Version)}" } } catch { Write-Host $_ Write-Host "New version of {$($dependency.ModuleName)} is available" } $i++ } # The progress bar seems to hang sometimes. Make sure it is no longer displayed. Write-Progress -Activity 'Scanning Dependencies' -Completed } <# .DESCRIPTION This function validates there are no updates to the module or it's dependencies and no multiple versions are present on the local system. .EXAMPLE Test-M365DSCModuleValidity .FUNCTIONALITY Public #> function Test-M365DSCModuleValidity { [CmdletBinding()] param() if ($Script:IsM365DSCModuleValidated) { Write-Verbose -Message 'The Microsoft365DSC module has already been validated in this session.' Write-Verbose -Message 'If you have updated the module, please restart your PowerShell session to re-validate.' return } if ($env:AZUREPS_HOST_ENVIRONMENT -like 'AzureAutomation*') { $message = 'Skipping check for newer version of Microsoft365DSC due to Azure Automation Environment restrictions.' Write-Verbose -Message $message return } # Validate if only one installation of the module is present and that it's the latest version available if ($Script:IsPsResourceGetAvailable) { $latestVersion = (Find-PSResource -Name 'Microsoft365DSC' -Repository 'PSGallery').Version | Sort-Object -Descending | Select-Object -First 1 } else { $latestVersion = (Find-Module -Name 'Microsoft365DSC' -Includes 'DSCResource').Version } $localVersion = (Get-Module -Name 'Microsoft365DSC').Version if ($latestVersion -gt $localVersion) { Write-Host "There is a newer version of the 'Microsoft365DSC' module available on the gallery." Write-Host "To update the module and it's dependencies, run the following command:" Write-Host 'Update-M365DSCModule' -ForegroundColor Blue } $Script:IsM365DSCModuleValidated = $true } <# .DESCRIPTION This function uninstalls all previous M365DSC dependencies and older versions of the module. .EXAMPLE Uninstall-M365DSCOutdatedDependencies .FUNCTIONALITY Public #> function Uninstall-M365DSCOutdatedDependencies { [CmdletBinding()] param() try { $InformationPreference = 'Continue' [array]$microsoft365DscModules = Get-Module Microsoft365DSC -ListAvailable $outdatedMicrosoft365DscModules = $microsoft365DscModules | Sort-Object -Property Version | Select-Object -SkipLast 1 foreach ($module in $outdatedMicrosoft365DscModules) { try { Write-Information -MessageData "Uninstalling $($module.Name) Version {$($module.Version)}" if (Test-Path -Path $($module.Path)) { Remove-Item $($module.ModuleBase) -Force -Recurse -ErrorAction Stop } } catch { $message = "Could not uninstall $($module.Name) Version $($module.Version)" if ($_.Exception.Message -like "*Access to the path* is denied*" -and ($Scope -eq "AllUsers") -and -not ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) { $message += ' You need to run this command as a local administrator.' } New-M365DSCLogEntry -Message $message ` -Exception $_ ` -Source $($MyInvocation.MyCommand.Source) Write-Error -Message $message -ErrorAction Continue } } $allDependenciesExceptAuth = $Script:M365DSCDependencies.Values.GetEnumerator().Where({ $_.ModuleName -ne 'Microsoft.Graph.Authentication' }) $i = 1 foreach ($dependency in $allDependenciesExceptAuth) { Write-Progress -Activity 'Scanning Dependencies' -PercentComplete ($i / $allDependenciesExceptAuth.Count * 100) try { if ($dependency.PowerShellCore -and -not $Script:IsPowerShellCore) { Write-Verbose -Message "Skipping module {$($dependency.ModuleName)} as it is managed by PowerShell Core." continue } elseif ($dependency.PowerShellCore -eq $false -and $Script:IsPowerShellCore) { Write-Verbose -Message "Skipping module {$($dependency.ModuleName)} as it is managed by Windows PowerShell." continue } $found = Get-Module $dependency.ModuleName -ListAvailable | Where-Object -FilterScript { $_.Version -ne $dependency.RequiredVersion } foreach ($foundModule in $found) { try { Write-Information -MessageData "Uninstalling $($foundModule.Name) Version {$($foundModule.Version)}" if (Test-Path -Path $($foundModule.Path)) { Remove-Item $($foundModule.ModuleBase) -Force -Recurse -ErrorAction Stop } } catch { $message = "Could not uninstall $($foundModule.Name) Version $($foundModule.Version)" if ($_.Exception.Message -like "*Access to the path* is denied*" -and ($Scope -eq "AllUsers") -and -not ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) { $message += ' You need to run this command as a local administrator.' } New-M365DSCLogEntry -Message $message ` -Exception $_ ` -Source $($MyInvocation.MyCommand.Source) Write-Error -Message $message -ErrorAction Continue } } } catch { Write-Error -Message "Could not uninstall {$($dependency.ModuleName)}" -ErrorAction Continue } $i++ } } catch { New-M365DSCLogEntry -Message 'Error uninstalling outdated dependencies:' ` -Exception $_ ` -Source $($MyInvocation.MyCommand.Source) Write-Error $_ } $authModule = $Script:M365DSCDependencies['Microsoft.Graph.Authentication'] try { Write-Information -MessageData 'Checking Microsoft.Graph.Authentication' $found = Get-Module $authModule.ModuleName -ListAvailable | Where-Object -FilterScript { $_.Version -ne $authModule.RequiredVersion } foreach ($foundModule in $found) { try { Write-Information -MessageData "Uninstalling $($foundModule.Name) version {$($foundModule.Version)}" if (Test-Path -Path $($foundModule.Path)) { Remove-Item $($foundModule.ModuleBase) -Force -Recurse -ErrorAction Stop } } catch { $message = "Could not uninstall $($foundModule.Name) Version $($foundModule.Version)" if ($_.Exception.Message -like "*Access to the path* is denied*" -and ($Scope -eq "AllUsers") -and -not ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) { $message += ' You need to run this command as a local administrator.' } New-M365DSCLogEntry -Message $message ` -Exception $_ ` -Source $($MyInvocation.MyCommand.Source) Write-Error -Message $message -ErrorAction Continue } } } catch { Write-Error -Message "Could not uninstall {$($dependency.ModuleName)}" -ErrorAction Continue } } <# .DESCRIPTION This function installs all missing M365DSC dependencies .PARAMETER Force Specifies that all dependencies should be forcefully imported again. .PARAMETER ValidateOnly Specifies that the function should only return the dependencies that are not installed. .PARAMETER Scope Specifies the scope of the update of the module. The default value is AllUsers(needs to run as elevated user). .PARAMETER Proxy Specifies the proxy server to use for the module installation. .PARAMETER Repository Specifies the PowerShell repository name to use for the installation of the dependencies. .PARAMETER UsePowerShellGet Specifies that Install-Module should be used for the installation of the dependencies instead of Install-PSResource. .EXAMPLE PS> Update-M365DSCDependencies .EXAMPLE PS> Update-M365DSCDependencies -Force .EXAMPLE PS> Update-M365DSCDependencies -Scope CurrentUser .FUNCTIONALITY Public #> function Update-M365DSCDependencies { [CmdletBinding()] param ( [Parameter()] [Switch] $Force, [Parameter()] [Switch] $ValidateOnly, [Parameter()] [ValidateSet("CurrentUser", "AllUsers")] $Scope = "AllUsers", [Parameter()] [System.String] $Proxy, [Parameter()] [System.String] $Repository = 'PSGallery', [Parameter()] [switch] $UsePowerShellGet ) try { $InformationPreference = 'Continue' $i = 1 $returnValue = @() $params = @{} if (-not [System.String]::IsNullOrEmpty($Proxy)) { $params.Add('Proxy', $Proxy) } # Check if PSResourceGet is installed or not if (-not $Script:IsPsResourceGetAvailable) { Write-Warning -Message 'Microsoft.PowerShell.PSResourceGet is not installed, installing it now...' try { Install-Module -Name Microsoft.PowerShell.PSResourceGet -Scope $Scope -AllowClobber @params -Force -ErrorAction Stop -Repository PSGallery $Script:IsPsResourceGetAvailable = $true } catch { Write-Warning -Message "Failed to install Microsoft.PowerShell.PSResourceGet, continuing without it..." } } $scopedIsPsResourceGetAvailable = $Script:IsPsResourceGetAvailable if ($params.ContainsKey('Proxy')) { Write-Information -MessageData "Falling back to Install-Module because Install-PSResource does not support a proxy" $scopedIsPsResourceGetAvailable = $false } foreach ($dependency in $Script:M365DSCDependencies.Values.GetEnumerator()) { Write-Progress -Activity 'Scanning dependencies' -PercentComplete ($i / $Script:M365DSCDependencies.Count * 100) try { if (-not $Force) { if ($dependency.PowerShellCore -and -not $Script:IsPowerShellCore) { Write-Verbose -Message "The dependency {$($dependency.ModuleName)} requires PowerShell Core. Skipping." continue } elseif ($dependency.PowerShellCore -eq $false -and $Script:IsPowerShellCore) { Write-Verbose -Message "The dependency {$($dependency.ModuleName)} requires Windows PowerShell. Skipping." continue } $found = Get-Module $dependency.ModuleName -ListAvailable | Where-Object -FilterScript { $_.Version -eq $dependency.RequiredVersion } } if ((-not $found -or $Force) -and -not $ValidateOnly) { $errorFound = $false try { if ((-not(([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator))) -and ($Scope -eq "AllUsers")) { Write-Error 'Cannot update the dependencies for Microsoft365DSC. You need to run this command as a local administrator.' $errorFound = $true } } catch { Write-Verbose -Message "Couldn't retrieve Windows Principal. One possible cause is that the current environment is not a Windows OS." } if (-not $errorFound) { if (-not $dependency.PowerShellCore -and $Script:IsPowerShellCore) { Write-Warning "The dependency {$($dependency.ModuleName)} does not support PowerShell Core. Please run Update-M365DSCDependencies in Windows PowerShell." continue } elseif ($dependency.PowerShellCore -and -not $Script:IsPowerShellCore) { Write-Warning "The dependency {$($dependency.ModuleName)} requires PowerShell Core. Please run Update-M365DSCDependencies in PowerShell Core." continue } Remove-Module $dependency.ModuleName -Force -ErrorAction SilentlyContinue if ($dependency.ModuleName -like 'Microsoft.Graph*') { Remove-Module 'Microsoft.Graph.Authentication' -Force -ErrorAction SilentlyContinue } Remove-Module $dependency.ModuleName -Force -ErrorAction SilentlyContinue if ($scopedIsPsResourceGetAvailable -and -not $UsePowerShellGet) { Write-Information -MessageData "Using Install-PSResource to install $($dependency.ModuleName) with version {$($dependency.RequiredVersion)}" Install-PSResource -Name $dependency.ModuleName -Version $dependency.RequiredVersion -Scope $Scope -AcceptLicense -SkipDependencyCheck -TrustRepository -Repository $Repository } else { Write-Information -MessageData "Using Install-Module to install $($dependency.ModuleName) with version {$($dependency.RequiredVersion)}" Install-Module $dependency.ModuleName -RequiredVersion $dependency.RequiredVersion -AllowClobber -Force -Scope "$Scope" @Params -Repository $Repository } } } if ($dependency.ExplicitLoading) { Remove-Module $dependency.ModuleName -Force -ErrorAction SilentlyContinue if ($dependency.Prefix) { Import-Module $dependency.ModuleName -Global -Prefix $dependency.Prefix -Force -DisableNameChecking } else { Import-Module $dependency.ModuleName -Global -Force -Alias @() -Cmdlet @() -Variable @() -DisableNameChecking } } if (-not $found -and $validateOnly) { $returnValue += $dependency } } catch { Write-Error -Message "Could not update or import {$($dependency.ModuleName)}: $($_.Exception.Message)" -ErrorAction Continue } $i++ } # The progress bar seems to hang sometimes. Make sure it is no longer displayed. Write-Progress -Activity 'Scanning dependencies' -Completed if ($ValidateOnly) { return $returnValue } } catch { New-M365DSCLogEntry -Message 'Error updating dependencies:' ` -Exception $_ ` -Source $($MyInvocation.MyCommand.Source) Write-Error $_ -ErrorAction Continue } } <# .DESCRIPTION This function updates the module, dependencies and uninstalls outdated dependencies. .PARAMETER Scope Specifies the scope of the update of the module. The default value is AllUsers(needs to run as elevated user). .PARAMETER Proxy Specifies the proxy server to use for the update. .PARAMETER BaseRepository Specifies the PowerShell Repository name to use for the installation of the Microsoft365DSC module. .PARAMETER DependencyRepository Specifies the PowerShell Repository name to use for the installation of the dependencies of the Microsoft365DSC module. .PARAMETER NoUninstall Indicates if outdated dependencies and modules should be uninstalled. .EXAMPLE PS> Update-M365DSCModule .EXAMPLE PS> Update-M365DSCModule -Scope CurrentUser .EXAMPLE PS> Update-M365DSCModule -Scope AllUsers .FUNCTIONALITY Public #> function Update-M365DSCModule { [CmdletBinding()] param( [Parameter()] [ValidateSet("CurrentUser", "AllUsers")] $Scope = "AllUsers", [Parameter()] [System.String] $Proxy, [Parameter()] [System.String] $BaseRepository = 'PSGallery', [Parameter()] [System.String] $DependencyRepository = 'PSGallery', [Parameter()] [switch] $NoUninstall ) $params = @{} $unloadModule = $true if (-not [System.String]::IsNullOrEmpty($proxy)) { $params.Add('Proxy', $Proxy) } try { Update-Module -Name 'Microsoft365DSC' @Params -ErrorAction Stop } catch { if ($_.Exception.Message -like "*Module 'Microsoft365DSC' was not installed by using Install-Module*") { Write-Verbose -Message "The Microsoft365DSC module might have been installed with Install-PSResource" if ($Script:IsPsResourceGetAvailable) { Write-Verbose -Message "Updating the Microsoft365DSC module using Update-PSResource..." try { Update-PSResource -Name 'Microsoft365DSC' -Scope $Scope ` -TrustRepository -AcceptLicense -SkipDependencyCheck ` -Repository $BaseRepository -ErrorAction Stop } catch { if ($_.Exception.Message -like "*No installed packages*") { Write-Verbose -Message "Microsoft365DSC was neither installed using Install-Module nor Install-PSResource. Skipping update check." } else { New-M365DSCLogEntry -Message 'Error Updating Module:' ` -Exception $_ ` -Source $($MyInvocation.MyCommand.Source) throw $_ } } } } elseif ($_.Exception.Message -like "*was not updated because no valid module was found*") { Write-Verbose -Message "No valid module was found to update." $unloadModule = $false } } try { if ($unloadModule) { Write-Verbose -Message "Unloading all instances of the Microsoft365DSC module from the current PowerShell session." Remove-Module Microsoft365DSC -Force } Write-Verbose -Message "Retrieving all versions of the Microsoft365DSC installed on the machine." [Array]$instances = Get-Module Microsoft365DSC -ListAvailable | Sort-Object -Property Version -Descending if ($instances.Length -gt 0) { Write-Verbose -Message "Loading version {$($instances[0].Version.ToString())} of the Microsoft365DSC module from {$($instances[0].ModuleBase)}" Import-Module Microsoft365DSC -RequiredVersion $instances[0].Version.ToString() -Force } } catch { New-M365DSCLogEntry -Message 'Error Updating Module:' ` -Exception $_ ` -Source $($MyInvocation.MyCommand.Source) throw $_ } Update-M365DSCDependencies -Scope $Scope -Proxy $Proxy -Repository $DependencyRepository if (-not $NoUninstall) { Uninstall-M365DSCOutdatedDependencies } } Export-ModuleMember -Function @( 'Confirm-M365DSCDependencies', 'Confirm-M365DSCLoadedModule', 'Confirm-M365DSCModuleDependency', 'Get-M365DSCModuleConfiguration', 'Get-M365DSCRequiredModules', 'Get-M365DSCResourceSettings', 'Set-M365DSCModuleConfiguration', 'Set-M365DSCRequiredModulesLoaded', 'Test-IsM365DSCRequiredModulesLoaded', 'Test-M365DSCDependenciesForNewVersions', 'Test-M365DSCModuleValidity', 'Uninstall-M365DSCOutdatedDependencies', 'Update-M365DSCDependencies', 'Update-M365DSCModule' ) |