Import-IsolatedModule imports known dependency-sensitive PowerShell modules through
curated PowerForge isolation profiles.
The feature is intended for PowerShell 7+ sessions where one service module's binary
dependencies cannot safely share the default load context with another module or host.
Instead of trying to control import order in the default context, PowerForge copies the
profiled module to a temporary workspace, patches the configured script or manifest
surface, loads selected assemblies through a module-scoped AssemblyLoadContext, and
imports the generated wrapper into the current session.
This is a maintained-profile model, not a generic arbitrary-module loader. Each supported module has explicit rules for what to copy, patch, preload, import, and expose.
Import the module that owns Import-IsolatedModule, then import one or more supported
profiles:
Import-Module PSPublishModule
$exo = Import-IsolatedModule -Profile ExchangeOnlineManagement -PassThru
$teams = Import-IsolatedModule -Profile MicrosoftTeams -PassThru
$graph = Import-IsolatedModule -Profile MicrosoftGraphAuthentication -PassThru
$exo, $teams, $graph |
Format-Table ProfileName, ModuleName, ContextName, IsolatedImportPathAfter the isolated import, use the profiled module's normal commands:
Connect-ExchangeOnline -ShowBanner:$false
Connect-MicrosoftTeams
Connect-MgGraph -NoWelcomeThe isolated import only prepares and imports the command surface. It does not authenticate to Exchange, Teams, Graph, or any other service.
Import-IsolatedModule -Profile ExchangeOnlineManagement
Connect-ExchangeOnline -ShowBanner:$false
Get-ConnectionInformation |
Format-List UserPrincipalName, ConnectionUri, ModuleName
Get-EXOMailbox -ResultSize 5 |
Select-Object DisplayName, PrimarySmtpAddress
Disconnect-ExchangeOnline -Confirm:$falseThe profile currently:
- resolves
ExchangeOnlineManagementfromPSModulePathwhen-Pathis omitted, - requires
ExchangeOnlineManagement3.9.0 or newer, - copies the module into
%TEMP%\PowerForge\IsolatedModules\ExchangeOnlineManagementunless-WorkRootis supplied, - patches
netCore\ExchangeOnlineManagement.psm1, - patches the copied
ExchangeOnlineManagement.psd1manifest so EXO keeps its upstream module name, version, and export metadata, - loads
Microsoft.Exchange.Management.RestApiClient.dllandMicrosoft.Exchange.Management.ExoPowershellGalleryModule.dllthrough the sameExchangeOnlineManagement.ALCcontext, - exposes public types from
Microsoft.Exchange.Management.*andMicrosoft.Online.CSE.RestApiPowerShellModule.*as type accelerators so EXO script type literals keep working.
Import-IsolatedModule -Profile MicrosoftTeams
Connect-MicrosoftTeams
Get-Team |
Select-Object -First 10 DisplayName, GroupId, Visibility
Disconnect-MicrosoftTeamsThe profile currently:
- resolves
MicrosoftTeamsfromPSModulePathwhen-Pathis omitted, - requires
MicrosoftTeams7.8.0 or newer, - copies the module into
%TEMP%\PowerForge\IsolatedModules\MicrosoftTeamsunless-WorkRootis supplied, - generates
MicrosoftTeams.ALC.psm1and a patchedMicrosoftTeams.ALC.psd1manifest that preserves the upstream export contract, - loads the Teams connect, Teams cmdlet, policy administration, and ConfigAPI binary
surfaces through the same
MicrosoftTeams.ALCcontext, - rewrites selected copied submodule script imports so they load binary modules through the shared context,
- imports the required Teams submodule manifests after the isolated binary load,
- exposes public types from
Microsoft.Teams.*as type accelerators.
Import-IsolatedModule -Profile MicrosoftGraphAuthentication
Connect-MgGraph -NoWelcome
Get-MgContext |
Format-List Account, TenantId, Scopes
Disconnect-MgGraphThe profile currently:
- resolves
Microsoft.Graph.AuthenticationfromPSModulePathwhen-Pathis omitted, - requires
Microsoft.Graph.Authentication2.36.0 or newer, - copies the module into
%TEMP%\PowerForge\IsolatedModules\MicrosoftGraphAuthenticationunless-WorkRootis supplied, - generates
Microsoft.Graph.Authentication.ALC.psm1and a patchedMicrosoft.Graph.Authentication.ALC.psd1manifest, - clears copied
NestedModulesso Graph binaries are not imported through the default loader before the isolated wrapper runs, - loads Graph, Kiota, MSAL, Azure.Identity, and supporting dependency assemblies into
Microsoft.Graph.Authentication.ALC, - imports
Microsoft.Graph.Authentication.Core.dllandMicrosoft.Graph.Authentication.dllthrough the same isolated context, - removes copied Authenticode signature blocks from the patched script because the generated wrapper necessarily changes the original script,
- replaces Graph's path-based binary command discovery with explicit cmdlet and alias exports for the supported command surface,
- exposes public types from
Microsoft.Graph.*as type accelerators.
When -Path is omitted, the command resolves the profile's module name with:
Get-Module -ListAvailable -Name <profile module name>If several versions are visible on PSModulePath, the highest version is selected.
Use -Path when you want a specific installed copy, a saved module payload, or an
alternate manifest name in the module base:
$module = Get-Module -ListAvailable ExchangeOnlineManagement |
Sort-Object Version -Descending |
Select-Object -First 1
Import-IsolatedModule -Profile ExchangeOnlineManagement -Path $module.ModuleBaseDirectory paths are treated as module bases:
Import-IsolatedModule `
-Profile MicrosoftGraphAuthentication `
-Path 'C:\Modules\Microsoft.Graph.Authentication\2.37.0'Manifest paths are accepted. The manifest file is used for version validation and manifest patching, while its parent directory is treated as the module base:
Import-IsolatedModule `
-Profile MicrosoftGraphAuthentication `
-Path 'C:\Modules\Microsoft.Graph.Authentication\2.37.0\Microsoft.Graph.Authentication.psd1'Alternate manifest names are also supported when the manifest lives beside the module payload that the profile expects:
Import-IsolatedModule `
-Profile MicrosoftTeams `
-Path 'C:\Lab\ContosoTeams\7.9.0\ContosoTeams.psd1'In that example, C:\Lab\ContosoTeams\7.9.0 is the module base. The profile still expects
the configured Teams script and binary layout below that directory. A manifest stored away
from the module payload is not enough by itself; the parent directory of the manifest must
contain the module files that the selected profile knows how to patch.
Non-manifest file paths are resolved to their parent directory. The service then tries to
find a manifest for validation and patching from the profile's configured manifest path,
then from a single .psd1 in the module base, then from <moduleBase>\<moduleBaseName>.psd1.
By default, Import-IsolatedModule imports the generated wrapper but does not change
PSModulePath. That keeps the isolated import narrow: commands are available in the
current runspace, but later module-name resolution can still find the originally installed
module first.
Use -PreferIsolatedModulePath when a downstream module imports the profiled module by
name and should bind to the generated isolated copy:
Import-Module Az.Storage
$exo = Import-IsolatedModule `
-Profile ExchangeOnlineManagement `
-PreferIsolatedModulePath `
-PassThru
Connect-ExchangeOnline -ShowBanner:$false
Import-Module Contoso.ExchangeWorker
Invoke-ContosoExchangeWorker
$exo |
Format-List ProfileName, IsolatedModuleResolutionPath, PreferIsolatedModulePathThe switch prepends the generated profile work path to process-scoped PSModulePath after
the isolated import succeeds. In the Exchange example, a later
Import-Module ExchangeOnlineManagement by name can resolve the generated copy under
%TEMP%\PowerForge\IsolatedModules\ExchangeOnlineManagement\<guid> before the original
installed module. Profiles that use an ALC-specific manifest name, such as
MicrosoftTeams.ALC.psd1, also write a module-name manifest in the generated copy so
PowerShell name-based resolution still points at the patched ALC root module.
Use this only when you need that resolution behavior. It is intentionally opt-in because it changes module-name resolution for the whole current PowerShell process:
Get-Module -ListAvailable <profile module>may show the generated copy first,- the most recently prepended isolated import wins when several generated copies exist,
- explicit file-path imports still use the path provided by the caller,
- the change is process-scoped and ends when the PowerShell process exits.
Import-IsolatedModule fails before importing when a required contract is missing.
Use Test-IsolatedModuleProfile to run the same profile resolution and preflight path
checks without copying or importing the generated wrapper.
Runtime validation:
- Windows PowerShell 5.1 is rejected because
AssemblyLoadContextrequires PowerShell 7+ on CoreCLR.
Profile validation:
- unknown profile names fail and list the available profiles,
- profile names are resolved case-insensitively.
Module discovery validation:
- without
-Path, the module must be visible throughPSModulePath, - with
-Path, the supplied file or directory must exist, - if the profile declares a minimum version, a parseable module manifest with
ModuleVersionmust be available, - the resolved version must be greater than or equal to the profile minimum.
Profile-layout validation:
- the configured source script path must exist under the module base,
- profiles that preserve a manifest must have a source manifest available,
- the source manifest must contain a
RootModuleentry that can be patched, - profile-declared dependency assemblies and binary module imports must exist relative to the profiled script module directory,
- copied script binary imports and additional required files must exist relative to the module base.
Work-root behavior:
- if
-WorkRootis omitted, the generated copy is created under%TEMP%\PowerForge\IsolatedModules\<profile>\<guid>, - if
-WorkRootis supplied, the directory is created when missing, - each import uses a new GUID child folder,
- generated copies can remain locked until the PowerShell process exits.
Validate a profile source before importing:
$validation = Test-IsolatedModuleProfile -Profile MicrosoftGraphAuthentication
$validation | Format-List ProfileName, ModuleName, SourceModuleBase, IsValid
$validation.Paths | Format-Table Category, RelativePath, Exists
$validation.Issues | Format-Table Severity, Category, MessageReturn only a Boolean result:
Test-IsolatedModuleProfile -Profile MicrosoftTeams -QuietUse -PassThru to get the generated import details:
$result = Import-IsolatedModule -Profile MicrosoftGraphAuthentication -PassThru
$result |
Format-List ProfileName, ModuleName, SourceModuleBase, ContextName,
IsolatedImportPath, IsolatedScriptPath, IsolatedManifestPath, WorkPathInspect the exported commands:
Get-Command -Module Microsoft.Graph.Authentication.ALC |
Sort-Object CommandType, Name |
Format-Table CommandType, Name, ModuleNameInspect assemblies loaded into the profile context:
[System.Runtime.Loader.AssemblyLoadContext]::All |
Where-Object Name -eq $result.ContextName |
ForEach-Object {
$_.Assemblies |
Sort-Object { $_.GetName().Name } |
Select-Object @{Name = 'Name'; Expression = { $_.GetName().Name } },
@{Name = 'Version'; Expression = { $_.GetName().Version } },
Location
}Confirm that a profile did not place Graph assemblies in the default context:
[System.Runtime.Loader.AssemblyLoadContext]::Default.Assemblies |
Where-Object { $_.GetName().Name -like 'Microsoft.Graph*' } |
Select-Object @{Name = 'Name'; Expression = { $_.GetName().Name } }, LocationUse -WorkRoot when you want deterministic generated files for inspection:
$workRoot = 'C:\Temp\PowerForge-Isolated'
$result = Import-IsolatedModule `
-Profile MicrosoftTeams `
-WorkRoot $workRoot `
-PassThru
Get-ChildItem -LiteralPath $result.WorkPath -Recurse |
Select-Object -First 40 FullName
Get-Content -LiteralPath $result.IsolatedScriptPath -TotalCount 80Use -WhatIf to preview without creating or importing the generated wrapper:
Import-IsolatedModule -Profile ExchangeOnlineManagement -WhatIfSupported profiles can be imported in the same PowerShell 7+ process:
$profiles = foreach ($profile in @(
'ExchangeOnlineManagement',
'MicrosoftTeams',
'MicrosoftGraphAuthentication'
)) {
Import-IsolatedModule -Profile $profile -PassThru
}
$profiles |
Format-Table ProfileName, ModuleName, ContextNameEach profile uses its own context name. PowerShell command names are still global in the runspace, so normal command-name conflicts can still occur if two modules export the same command. The isolation layer is about assembly loading, not command renaming.
Profiles are maintained in PowerForge so support is explicit and testable. A profile declares:
- module name and minimum supported version,
- source script path and generated script name,
- optional manifest path and generated manifest name when the upstream export contract should be preserved,
- whether copied manifests should clear
NestedModules, - whether the generated wrapper appends the source script body,
- number of original bootstrap lines to replace when source script content is used,
- source line fragments to skip when appending the original script body,
- whether copied Authenticode signature blocks should be removed from patched scripts,
- additional profile-maintained script lines to append after isolated binary loading,
- dependency assemblies to load into the context without importing as PowerShell modules,
- binary assemblies to import as PowerShell modules through the isolated context,
- copied script-module binary imports that should be rewritten to use the shared context,
- namespace prefixes to bridge into PowerShell type resolution,
- stable load-context name.
This keeps the cmdlet surface small while allowing future profiles to add module-specific patching rules without duplicating loader code.
- PowerShell 7+ only. Windows PowerShell cannot use
AssemblyLoadContext. - Profiles are curated; arbitrary third-party modules are not automatically isolated.
- The generated wrapper imports commands into the current runspace; command names are not namespaced or renamed.
- Generated module copies are process-local working artifacts and can remain locked until the PowerShell process exits.
-PreferIsolatedModulePathchanges process-scopedPSModulePathonly after a successful import and does not prevent explicit imports of another module path.- Assembly isolation does not isolate service authentication state or remote service state.