Modules that modify your environment.

Modules usually just keep few functions together and so you are perfectly fine with removing those functions when module is removed. But that is not always the case. Let say you want to modify prompt, or overwrite it for the time when module is loaded:

# Simple module to modify my prompt            
function prompt {            
    "$pwd ]> "            
}

It is fine as long module is loaded. Now try to unload it. You are left without prompt function. My first guess was that I should re-create this function in special scriptblock that is invoked when module is removed:

# Simple module to modify my prompt            
# But first let's create backup copy...            
$oldPrompt = Get-Content function:\prompt            
            
function prompt {            
    "$pwd ]> "            
}            
            
$MyInvocation.MyCommand.ScriptBlock.Module.OnRemove = {            
    Set-Content function:\prompt -Value $OldPrompt            
}            

But unfortunately it won’t work either. Why? Try to Remove-Module –Verbose and add some Write-Host to OnRemove scriptblock, you will notice that this method is invoked before imported functions are being removed. Funny, that when I initially thought about that I did some other thing, simply modified contents of prompt function:

# Simple module to modify my prompt            
# But first let's create backup copy...            
$oldPrompt = Get-Content function:\prompt -ErrorAction SilentlyContinue            
$addedPrompt = @'
# This is only a comment but could be something useful too.

'@            
            
Set-Content function:\prompt -Force $($AddedPrompt + $oldPrompt)            
            
$MyInvocation.MyCommand.ScriptBlock.Module.OnRemove = {            
    Set-Content function:\prompt -Value $OldPrompt            
}            

I was surprised to see, that if I don’t do anything with prompt function in OnRemove it will get removed together with module, and if I do something – it remains untouched (you can see it when –Verbose view is on). It was done like that in my CD module that uses prompt to save each PSContainer that you walk into and as far as I can tell – works fine. Now I wanted to overwrite psEdit function when module to work with PowerShell SE is loaded. But to have option to re-create it I had to again use Set-Content. If there is other option – I had no luck finding one. So now instead of normal function definition I have long herestring. Yak. 😦

Update after Jason’s comment.

So, eventually I got prompt reply from a person who I should probably asked first – one of authors of brilliant extension for PowerShell Tab completion (I have it in my PowerShell.exe profile to make working with it less horrible 😉 ). PowerTab is overwriting TabExpansion function, and once module in removed (I would do that only for working with ActiveDirectory provider probably) it is restored to it’s original version. This solution requires you to define function in global scope rather than default one and you have to restore it back in a way I just did:

# Simple module to modify my prompt            
# But first let's create backup copy...            
$oldPrompt = Get-Content function:\prompt -ErrorAction SilentlyContinue            
            
function global:prompt {            
    "$pwd ]:> "            
}            
            
$MyInvocation.MyCommand.ScriptBlock.Module.OnRemove = {            
    Set-Content function:\prompt -Value $OldPrompt            
}            

So if you want to overwrite function than that is probably the best way to do that. Result is similar to the one I got with my workaround: no action on function if it’s modified in OnRemove, removed if no action was taken. And my psEdit is looks like function again. 😀 Thanks, Jason!

Advertisements