Add module pinning
Pull Request (PR) description
This PR implements module pinning during module load in Microsoft365DSC. Now it is possible to have n versions of a module installed. As long as the required version from the manifest is installed, it will always load the one specified there. If a module is loaded with the wrong version, it will be unloaded and reimported in the session.
For this to work, two new cmdlets were introduced:
Confirm-M365DSCModuleDependency- A public cmdlet which fetches the required dependencies for a module from thesettings.jsonand passes them toConfirm-M365DSCLoadedModule.Confirm-M365DSCLoadedModule- An internal cmdlet which checks the passed in modules if they are loaded with the correct version and if not, it reloads them with the correct version.
In all settings.json files, an entry requiredModules was added which contains the modules (e.g. Microsoft.Graph.Authentication, MicrosoftTeams etc.) that have to be loaded before running the DSC resource. The import of the modules is done by running Confirm-M365DSCModuleDependency -ModuleName <DSC_Resource> when the DSC resource is loaded. This ensures that the required modules are loaded on startup and before a *-TargetResource function is executed.
To improve performance, already validated modules are excluded from a re-check. If this proves to be problematic, it can be disabled in a future release. Also, it is possible to disable the entire check using a global variable: SkipModuleValidation. If this is set to $True, it will skip the entire module import process. This is especially useful if we're running tests in an isolated matter.
The following code was used to generate the settings.json files. It leverages the PowerShell Abstract Syntax Tree to find all of the cmdlets, process their corresponding modules and adds them to the settings.json files.
$MaximumFunctionCount = 32767
$m365dscModules = (Import-PowerShellDataFile -Path D:\M365DSC\Microsoft365DSC\Modules\Microsoft365DSC\Dependencies\Manifest.psd1).Dependencies.ModuleName
$dscResourcesWithModules = Get-ChildItem -Path D:\M365DSC\Microsoft365DSC\Modules\Microsoft365DSC\DSCResources\ -Recurse -Filter *.psm1 | Foreach-Object -Process {
Write-Host "Processing $($_.FullName)" -ForegroundColor Cyan
$ast = [System.Management.Automation.Language.Parser]::ParseFile($_.FullName, [ref]$null, [ref]$null)
$targetResourceFunctions = $ast.FindAll(
{
param($Item)
return (
($Item -is [System.Management.Automation.Language.FunctionDefinitionAst]) -and
($Item.Name -like '*-TargetResource')
)
}, $true
)
$commands = $targetResourceFunctions | ForEach-Object -Process {
$_.FindAll(
{
param($Item)
return (
($Item -is [System.Management.Automation.Language.CommandAst])
)
}, $true
)
} | Foreach-Object -Process {
$_.CommandElements[0]
} | Select-Object -ExpandProperty Value -Unique
$modules = @()
foreach ($command in $commands) {
$module = Get-Command -Name $command -ErrorAction SilentlyContinue | Select-Object -ExpandProperty ModuleName
if ($module -in $m365dscModules) {
if ($module -like "Microsoft.Graph.*" -and -not $modules -contains "Microsoft.Graph.Authentication") {
$modules += "Microsoft.Graph.Authentication"
}
$modules += $module
}
}
$modules = $modules | Sort-Object -Unique
@{
ResourceName = $_.BaseName
Modules = [array]$modules
}
}
$dscResourcesWithModules
foreach ($dscResource in $dscResourcesWithModules) {
Write-Host "Resource: $($dscResource.ResourceName)" -ForegroundColor Yellow
$settingsJson = Get-Content -Path "D:\M365DSC\Microsoft365DSC\Modules\Microsoft365DSC\DSCResources\$($dscResource.ResourceName)\settings.json" -Raw | ConvertFrom-Json -AsHashtable
$settingsJson.Add('requiredModules', [array]$dscResource.Modules)
$settingsJson | ConvertTo-Json -Depth 10 | Set-Content -Path "D:\M365DSC\Microsoft365DSC\Modules\Microsoft365DSC\DSCResources\$($dscResource.ResourceName)\settings.json"
}
This Pull Request (PR) fixes the following issues
- Fixes #6168
Task list
- [x] Added an entry to the change log under the Unreleased section of the file CHANGELOG.md. Entry should say what was changed and how that affects users (if applicable), and reference the issue being resolved (if applicable).
- [ ] Resource parameter descriptions added/updated in the schema.mof.
- [ ] Resource documentation added/updated in README.md.
- [x] Resource settings.json file contains all required permissions.
- [ ] Examples appropriately added/updated.
- [x] Unit tests added/updated.
- [x] New/changed code adheres to DSC Community Style Guidelines.
@Borgquite For visibility. Pinning module dependencies during DSC resource load is now working with this PR.
@FabienTschanz Fantastic! If this works, I'm sure it will help a bunch of people out!