Really, Remove the Module


Notice: The following post was originally published on another website. As the post is no longer accessible, it is being republished here on tommymaynard.com. The post was originally published on March 13, 2019.


I’m on a project. As a part of that project, I’m to deliver a PowerShell module that does — I don’t know — ten different things, we’ll say. The final, final thing that module does is remove itself from the computer. For real, it. deletes. itself.

The desktop admin, or whomever, will issue the Remove-Module command and via that function, the module will be removed from the system, as well as the session. Maybe you just had the thought I did: Yes, this module will live in the user’s module path. Therefore, the user will have the ability to delete the module from the system.

Let’s call our function Remove-MyModule and let’s assume it’s a member of the module called MyModule. I’ll also include a second function in MyModule called Get-MyModuleType. This way we have a command that offers some proof that my module is working, before the module is gone. Okay, let’s start by importing our module and using Get-Command to prove what I said we’d have up to this point. Remember, our Get-MyModuleType function tells us what type of module we have, and our Remove-MyModule function removes the whole module — including both commands — from existence.

PS> Import-Module -Name MyModule
PS> Get-Command -Module MyModule | Format-Table -AutoSize
CommandType Name             Version Source
----------- ----             ------- ------
Function    Get-MyModuleType 1.0.0   MyModule
Function    Remove-MyModule  1.0.0   MyModule

Before we move on, here’s the function code that makes up the Get-MyModuleType function — simple stuff. We collect the command name, and from it, the name of the containing module. And finally, we return the ModuleType property.

Function Get-MyModuleType {
    [CmdletBinding()]
    Param (
    )
 
    Begin {
        $CmdName = "$($MyInvocation.MyCommand.Name)"
        $CurrentModule = (Get-Command -Name $CmdName).Source
    } # End Begin.
 
    Process {
        (Get-Module -Name $CurrentModule).ModuleType
    } # End Process.
 
    End {
    } # End End.
} # End Function: Get-MyModuleType.

Now, let’s prove that our module’s Get function works. We invoke Get-MyModuleType and it outputs an indication that my module is a script module. Sure, we knew that, but with this first function, we have some indication that things are working, before we try the function in the module, that removes the module.

PS> Get-MyModuleType
Script

Let’s take a look at the second function in our module. As you look over the included code shortly, keep in mind that we have a few things going on. In our Begin block, we mostly do the same thing we did in the above function: return the name of the function and use it to return the name of the module. In addition to this, the Remove-MyModule function returns the module’s path, as well.

Once we exit the Begin block and enter the Process block, three tasks take place in succession. First, the function deletes the module folder from my computer (so have a backup if you’re playing along). It’s okay that the module is gone though. The function can continue to execute for now, as the module and its functions are still loaded in our current PowerShell session, even though its source is no longer on the disk. Next, it removes the module from the PowerShell session (think: from memory, where it’s still being stored). Lastly, our function removes the two functions from within the module (Get-MyModuleType and Remove-MyModule) from the Function PSDrive — we’ll discuss more shortly, after you’ve taken a look at the code that makes up the Remove-MyModule function.

Function Remove-MyModule {
    [CmdletBinding()]
    Param (
    )
 
    Begin {
        $CmdName = "$($MyInvocation.MyCommand.Name)"
        $CurrentModule = (Get-Command -Name $CmdName).Source
        $CurrentModulePath = (Get-Module -Name (Get-Command -Name $CmdName).Source).Path
    } # End Begin.
 
    Process {
        #region Remove (delete) module.
        try {
            $Path = "$(($CurrentModulePath -split $CurrentModule)[0])$CurrentModule"
            Remove-Item -Path $Path -Recurse -ErrorAction Stop
        } catch {
            Write-Warning -Message "Unable to remove (delete) the $CurrentModule PowerShell module."
        } # End try-catch.
        #endregion.
 
        #region Remove (unload) module.
        try {
            Remove-Module -Name $CurrentModule -ErrorAction Stop
        } catch {
            Write-Verbose -Message "Unable to remove (unload) the $CurrentModule PowerShell module."
        } # End try-catch.
        #endregion.
 
        #region Remove module functions from function PSDrive.
        Get-ChildItem -Path 'Function:\*-MyModule*' | Remove-Item
        #endregion.
    } # End Process.
 
    End {
    } # End End.
} # End Function: Remove-MyModule.

Typically, when we remove a PowerShell module from the PowerShell session using Remove-Module, it takes all the parts and pieces along. But, because we’ve already removed the module from the system, removing the module from the PowerShell session leaves the functions in the Function PSDrive. That’s one thought. The other, although I don’t believe this one so strongly (because both functions are left behind), is that they are left behind because we’re right in the middle of the Remove-MyModule function invocation.

Regardless of knowing exactly how this works, it’s fair to believe that traditional clean-up doesn’t work so well in this situation. The function we were executing was just removed from the computer in every way, and the function was completed without a problem. Sometimes, like this time, that’s good enough for me. The project is done. Okay fine, just that function part anyway. Let me hear your thoughts and ideas!

Leave a Reply

Your email address will not be published. Required fields are marked *