141

To check if a module exists I have tried the following:

try {
    Import-Module SomeModule
    Write-Host "Module exists"
} 
catch {
    Write-Host "Module does not exist"
}

The output is:

Import-Module : The specified module 'SomeModule' was not loaded because no valid module file was found in any module directory.
At D:\keytalk\Software\Client\TestProjects\Export\test.ps1:2 char:5
+     Import-Module SomeModule
+     ~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : ResourceUnavailable: (SomeModule:String) [Import-Module], FileNotFoundException
    + FullyQualifiedErrorId : Modules_ModuleNotFound,Microsoft.PowerShell.Commands.ImportModuleCommand

Module exists

I do get an error, but no exception is thrown, so we see Module exists in the end, although SomeModule does not exist.

Is there a good way (preferably without generating an error) to detect if a PowerShell module is installed on the system?

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Klemens Schindler
  • 3,379
  • 3
  • 11
  • 11

18 Answers18

179

You can use the ListAvailable option of Get-Module:

if (Get-Module -ListAvailable -Name SomeModule) {
    Write-Host "Module exists"
} 
else {
    Write-Host "Module does not exist"
}
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Klemens Schindler
  • 3,379
  • 3
  • 11
  • 11
  • 1
    I was going to suggets: Import-Module NonexistingModule -ErrorAction SilentlyContinue IF($error){Write-Host 'Module does not exist'} ELSE{Write-Host 'Module does exist'} But your way is better, more elegant :) – Erik Blomgren Feb 26 '15 at 10:58
  • This works great. Thanks. I'll use `Write-Warning "Module does not exist..." ;Break` But you've done all the hard work. – Craig.C Dec 20 '16 at 20:25
  • 1
    If you are importing libraries using `Import-Module` and a custom dll file, don't use the `-ListAvailable` option to determine if the module is installed because it won't be listed. According to [the PowerShell 6 documentation](https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/get-module?view=powershell-6#parameters), "ListAvailable does not return information about modules that are not found in the PSModulePath environment variable, even if those modules are loaded in the current session". – Dave F Sep 02 '19 at 13:50
  • This does not determine if a module has been installed (i.e. `Import-Module`) it only determines if the module is immediately available to be installed without specifying a specific location not already in `$env:PSModulePath` – Slogmeister Extraordinaire May 21 '20 at 18:17
51

A module could be in the following states:

  • imported
  • available on disk (or local network)
  • available in an online gallery

If you just want to have the darn thing available in a PowerShell session for use, here is a function that will do that or exit out if it cannot get it done:

function Load-Module ($m) {

    # If module is imported say that and do nothing
    if (Get-Module | Where-Object {$_.Name -eq $m}) {
        write-host "Module $m is already imported."
    }
    else {

        # If module is not imported, but available on disk then import
        if (Get-Module -ListAvailable | Where-Object {$_.Name -eq $m}) {
            Import-Module $m -Verbose
        }
        else {

            # If module is not imported, not available on disk, but is in online gallery then install and import
            if (Find-Module -Name $m | Where-Object {$_.Name -eq $m}) {
                Install-Module -Name $m -Force -Verbose -Scope CurrentUser
                Import-Module $m -Verbose
            }
            else {

                # If the module is not imported, not available and not in the online gallery then abort
                write-host "Module $m not imported, not available and not in an online gallery, exiting."
                EXIT 1
            }
        }
    }
}

Load-Module "ModuleName" # Use "PoshRSJob" to test it out
Federico Navarrete
  • 3,069
  • 5
  • 41
  • 76
Rod
  • 1,443
  • 15
  • 17
  • 2
    this is great "all-in-one" solution (I've only changed Load-Module to return $true/$false instead of EXIT) – Andrzej Martyna Aug 29 '18 at 06:06
  • This is beautiful. – sean Dec 16 '20 at 19:53
  • This seems to slow down my profile a lot when I do like this. It takes more than 2 sec now to load the profile. – John Jan 15 '21 at 16:44
  • I tested this successfully on PowerShell 7.3.1 on macOS. Nice work, Rod! – Van Vangor Dec 21 '22 at 22:03
  • Great answer! There's one more state - a module can be "saved" with `Save-Module` but not available in `Get-Module -ListAvailable`. Microsoft has some modules saved in `C:\Modules` on [Microsoft-hosted Azure DevOps agents](https://github.com/actions/runner-images/blob/main/images/win/Windows2022-Readme.md#powershell-modules), for example. – oderibas Feb 23 '23 at 12:30
36

The ListAvailable option doesn't work for me. Instead this does:

if (-not (Get-Module -Name "<moduleNameHere>")) {
    # module is not loaded
}

Or, to be more succinct:

if (!(Get-Module "<moduleNameHere>")) {
    # module is not loaded
}
GaTechThomas
  • 5,421
  • 5
  • 43
  • 69
karezza
  • 586
  • 5
  • 13
  • @oɔɯǝɹ I thought -ListAvailable was simply not available but I was still trying Import-Module. With Get-Module it's fine – Craig.C Dec 20 '16 at 20:16
  • 4
    You check if module LOADED(which is useful by itself -http://www.systemcentercentral.com/powershell-tip-how-to-load-if-a-module-is-loaded-before-attempting-import-module-3/), but not the other answer checks if module exists. – Michael Freidgeim Jan 10 '17 at 22:31
  • 1
    This executes much faster than using ListAvailable. – GaTechThomas Apr 21 '17 at 19:33
  • Fairly sure `!` doesn't work in powershell depending on the version? – Kellen Stuart Jun 20 '17 at 17:46
  • 2
    @KolobCanyon `!` is an alias for `-not`, but I would not recommend using aliases in ps1 scripts in general. @GaTechThomas it also has a different behavior, as specified by @MichaelFreidgeim (it does not return a truthy value for installed, but not imported, modules). – AndreasHassing Jan 22 '18 at 11:00
  • I am unable to use this script via Core; the accepted answer that utilizes "-ListAvailable" works like a charm, however. – CodeBreaker Nov 16 '20 at 21:58
26

The current version of Powershell has a Get-InstalledModule function that suits this purpose well (or at least it did in my case).

Get-InstalledModule

Description

The Get-InstalledModule cmdlet gets PowerShell modules that are installed on a computer.

The only issue with it is that it throws an exception if the module that is being requested doesn't exist, so we need to set ErrorAction appropriately to suppress that case.

if ((Get-InstalledModule `
    -Name "AzureRm.Profile" `
    -MinimumVersion 5.0 ` # Optionally specify minimum version to have
    -ErrorAction SilentlyContinue) -eq $null) {

    # Install it...
}
NightOwl888
  • 55,572
  • 24
  • 139
  • 212
  • 2
    `Get-InstalledModule` only accounts for modules installed via `Install-Module`, `Get-Module -List` will account for all available modules regardless of how they were installed – gordy Apr 29 '22 at 17:51
18

You can use the Get-InstalledModule

If (-not(Get-InstalledModule SomeModule -ErrorAction silentlycontinue)) {
  Write-Host "Module does not exist"
}
Else {
  Write-Host "Module exists"
}
mellifluous
  • 2,345
  • 2
  • 29
  • 45
  • 2
    Many good answers here, but with this new simple method, this should probably be the new accepted answer. – not2qubit Sep 29 '20 at 10:55
  • 2
    This only works if `PowerShellGet` is installed. On some airgapped servers this is not the case. – Diti Apr 20 '22 at 07:10
16

Just revisiting this as it's something I just faced and there is some incorrect stuff in the answers (though it's mentioned in the comments).

First thing though. The original questions ask how to tell if a PowerShell module is installed. We need to talk about the word installed! You don't install PowerShell modules (not in the traditional way you install software anyway).

PowerShell modules are either available (i.e. they are on the PowerShell module path), or they are imported (they are imported into your session and you can call the functions contained). This is how to check your module path, in case you want to know where to store a module:

$env:psmodulepath

I'd argue that it's becoming common to use C:\Program Files\WindowsPowerShell\Modules; more often due to it being available to all users, but if you want to lock down your modules to your own session, include them in your profile. C:\Users%username%\Documents\WindowsPowerShell\Modules;

Alright, back to the two states.

Is the module available (using available to mean installed in the original question)?

Get-Module -Listavailable -Name <modulename>

This tells you if a module is available for import.

Is the module imported? (I'm using this as the answer for the word 'exists' in the original question).

Get-module -Name <modulename>

This will either return an empty load of nothing if the module is not imported or a one-line description of the module if it is. As ever on Stack  Overflow, try the commands above on your own modules.

Federico Navarrete
  • 3,069
  • 5
  • 41
  • 76
bytejunkie
  • 1,003
  • 14
  • 30
  • 1
    You can install module in PowerShell. PowerShellGet has command `Get-InstalledModule` which is not returning the same output as `Get-Module -ListAvailable` – Igor May 23 '19 at 19:30
16

When I use non-default modules in my scripts I call the function below. Besides the module name, you can provide a minimum version.

# See https://www.powershellgallery.com/ for module and version info
Function Install-ModuleIfNotInstalled(
    [string] [Parameter(Mandatory = $true)] $moduleName,
    [string] $minimalVersion
) {
    $module = Get-Module -Name $moduleName -ListAvailable |`
        Where-Object { $null -eq $minimalVersion -or $minimalVersion -lt $_.Version } |`
        Select-Object -Last 1
    if ($null -ne $module) {
         Write-Verbose ('Module {0} (v{1}) is available.' -f $moduleName, $module.Version)
    }
    else {
        Import-Module -Name 'PowershellGet'
        $installedModule = Get-InstalledModule -Name $moduleName -ErrorAction SilentlyContinue
        if ($null -ne $installedModule) {
            Write-Verbose ('Module [{0}] (v {1}) is installed.' -f $moduleName, $installedModule.Version)
        }
        if ($null -eq $installedModule -or ($null -ne $minimalVersion -and $installedModule.Version -lt $minimalVersion)) {
            Write-Verbose ('Module {0} min.vers {1}: not installed; check if nuget v2.8.5.201 or later is installed.' -f $moduleName, $minimalVersion)
            #First check if package provider NuGet is installed. Incase an older version is installed the required version is installed explicitly
            if ((Get-PackageProvider -Name NuGet -Force).Version -lt '2.8.5.201') {
                Write-Warning ('Module {0} min.vers {1}: Install nuget!' -f $moduleName, $minimalVersion)
                Install-PackageProvider -Name NuGet -MinimumVersion 2.8.5.201 -Scope CurrentUser -Force
            }        
            $optionalArgs = New-Object -TypeName Hashtable
            if ($null -ne $minimalVersion) {
                $optionalArgs['RequiredVersion'] = $minimalVersion
            }  
            Write-Warning ('Install module {0} (version [{1}]) within scope of the current user.' -f $moduleName, $minimalVersion)
            Install-Module -Name $moduleName @optionalArgs -Scope CurrentUser -Force -Verbose
        } 
    }
}

usage example:

Install-ModuleIfNotInstalled 'CosmosDB' '2.1.3.528'

Please let me known if it's useful (or not)

Federico Navarrete
  • 3,069
  • 5
  • 41
  • 76
TJ Galama
  • 457
  • 3
  • 12
8

You can use the #Requires statement (supports modules from PowerShell 3.0).

The #Requires statement prevents a script from running unless the PowerShell version, modules, snap-ins, and module and snap-in version prerequisites are met.

So At the top of the script, simply add #Requires -Module <ModuleName>

If the required modules are not in the current session, PowerShell imports them.

If the modules cannot be imported, PowerShell throws a terminating error.

sheldonzy
  • 5,505
  • 9
  • 48
  • 86
  • 2
    Note for others that might think this is such a cleaner solution than the longer functions above: this will *import* the module if it is not in the current session, but it will not go out and install it from internet sources. So you may need/require the longer solutions if you're going to be deploying this to machines that may or may not already have the module installed. – Adam Nofsinger Apr 15 '21 at 16:42
6
try {
    Import-Module SomeModule
    Write-Host "Module exists"
} 
catch {
    Write-Host "Module does not exist"
}

It should be pointed out that your cmdlet Import-Module has no terminating error, therefore the exception isn't being caught so no matter what your catch statement will never return the new statement you have written.

(https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_try_catch_finally?view=powershell-6

From The Above:

"A terminating error stops a statement from running. If PowerShell does not handle a terminating error in some way, PowerShell also stops running the function or script using the current pipeline. In other languages, such as C#, terminating errors are referred to as exceptions. For more information about errors, see about_Errors."

It should be written as:

Try {
    Import-Module SomeModule -Force -Erroraction stop
    Write-Host "yep"
}
Catch {
    Write-Host "nope"
}

Which returns:

nope

And if you really wanted to be thorough you should add in the other suggested cmdlets Get-Module -ListAvailable -Name and Get-Module -Name to be extra cautious, before running other functions/cmdlets. And if it's installed from ps gallery or elsewhere you could also run a Find-Module cmdlet to see if there is a new version available.

Federico Navarrete
  • 3,069
  • 5
  • 41
  • 76
mwtilton
  • 69
  • 1
  • 2
5

IMHO, there is difference between checking if a module is:

1) installed, or 2) imported:

To check if installed:

Option 1: Using Get-Module with -ListAvailable parameter:

If(Get-Module -ListAvailable -Name "<ModuleName>"){'Module is installed'}
Else{'Module is NOT installed'}

Option 2: Using $error object:

$error.clear()
Import-Module "<ModuleName>" -ErrorAction SilentlyContinue
If($error){Write-Host 'Module is NOT installed'}
Else{Write-Host 'Module is installed'}

To check if imported:

Using Get-Module with -Name parameter (which you can omit as it is default anyway):

if ((Get-Module -Name "<ModuleName>")) {
   Write-Host "Module is already imported (i.e. its cmdlets are available to be used.)"
}
else {
   Write-Warning "Module is NOT imported (must be installed before importing)."
}
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Eddie Kumar
  • 1,216
  • 18
  • 20
4

Because this came up in so many answers, I am posting this as a separate answer instead of several comments. Please consider this as a public service announcement.

The answers based on using Get-InstalledModule are extremely dangerous if you really want to know if a module is available on a PowerShell installation. Get-InstalledModule will report the presence of a module only if it has been installed from PowerShellGet.

Evidence from PowerShell:

PS C:\Users\chris> Get-InstalledModule | Select-Object -Property Name, Version

Name         Version
----         -------
Choco        1.0.0
NTFSSecurity 4.2.6

PS C:\Users\chris> Get-Module | Select-Object -Property Name, Version

Name                            Version
----                            -------
chocolateyProfile               0.0
CimCmdlets                      7.0.0.0
Microsoft.PowerShell.Management 7.0.0.0
Microsoft.PowerShell.Security   7.0.0.0
Microsoft.PowerShell.Utility    7.0.0.0
Microsoft.WSMan.Management      7.0.0.0
PackageManagement               1.4.7
PowerShellGet                   2.2.5
PSReadLine                      2.1.0

There are no parameters for Get-InstalledModule that you can use to tell it to "show the other modules that you just aren't displaying right now." It can't display any others. As you can see above, it only shows two modules installed when I have nine imported into my session.

Since this is an answer, I will add guidance here:

  • @Kiemen Schindler's answer (https://stackoverflow.com/a/28740512/1236579) is a good one. As others have noted, the -ListAvailable parameter does not actually list all available modules. But practically, most of us probably don't care too much because if ListAvailable doesn't return a module, then we can't use it unless we already know how to load it using a non-standard/manual method, in which case we are unlikely to be searching for it to begin with.
  • If you need something beyond that, I think that both @TJ Galama and @Rod have supplied nice scripts for you to start with. I think that @Rod's function is particularly easy to read, and instructive if you are just learning about how modules are managed. However, it could still fail if your internet connection is down when it tries to seamlessly install a module before loading it.
  • So, if you simply want to check whether a module is loaded and just want to run one command to check, the best solution (as others have pointed out) is to use Get-Module -ListAvailable. Here is a simple example the OP could use:
$module = "SomeModule"

if (Get-Module -ListAvailable | Where-Object {$_.Name -eq $module}) {
  Write-Output "Module is installed."
  Import-Module $module
}
else {
  Write-Output "Module is not installed."
}

But rather than repeating this answer that others have supplied in the answers noted above, my main point is this: PLEASE do not walk away from this page thinking that Get-InstalledModule is a reliable way to determine if a module is installed (locally importable by name) on your PowerShell installation). It is not. It will supply you with the names of modules installed by PowerShellGet, but it will not supply you with the names of any other locally installed modules.

chris
  • 588
  • 5
  • 16
2

Coming from Linux background. I would prefer using something similar to grep, therefore I use Select-String. So even if someone is not sure of the complete module name. They can provide the initials and determine whether the module exists or not.

Get-Module -ListAvailable -All | Select-String Module_Name(can be a part of the module name)

010 M
  • 61
  • 3
  • 1
    I understand what you're saying, @010 M, but your reasoning got me thinking about how in Linux, it's not always good to stream a lot of text through `grep`. For example, instead of running `apt list | grep -E '^python3.*-pip.* \[installed(?:,\W+)?]'`, you might run `apt list --installed python3*-pip*`, instead. Similarly, in PS you can run `Get-Module -ListAvailable -All -Name '*partial-name*'` and not use `Select-String` at all to achieve the same result. Is one way better than the other? Probably, but whatever the reason is, it's not because one way is more like using `grep` than the other. – chris Jul 04 '22 at 00:21
1
  • First test if the module is loaded
  • Then import

```

if (Get-Module -ListAvailable -Name <<MODULE_NAME>>) {
    Write-Verbose -Message "<<MODULE_NAME>> Module does not exist." -Verbose
}
if (!(Get-Module -Name <<MODULE_NAME>>)) {
    Get-Module -ListAvailable <<MODULE_NAME>> | Import-Module | Out-Null
}

```

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
1

Here is the code to check if AZ module is installed or not:

$checkModule = "AZ"

$Installedmodules = Get-InstalledModule

if ($Installedmodules.name -contains $checkModule)
{

    "$checkModule is installed "

}

else {

    "$checkModule is not installed"

}
Alkum
  • 21
  • 2
1

The absolute simplest one-liner without if-else block using Az module as an example:

Get-InstalledModule Az

This is what you want if you're working in the shell console and just want to check if a PowerShell module is installed or not.

Samuel
  • 2,895
  • 4
  • 30
  • 45
0
Test-Path "C:\Program Files\WindowsPowerShell\Modules\ModuleName"

If you know the module name and common install paths, you could use:

$a = New-Object -TypeName 'System.Collections.ArrayList'

$paths = "$env:userprofile\*\ModuleName","C:\Program 
Files\WindowsPowerShell\Modules\ModuleName"

foreach ($path in $paths)
{
   $a.add($(Test-path $path))
}

If ($a -contains $true)
{
    Write-Host "ModuleName is installed" -ForegroundColor Green
}
else
{
    Write-Host "ModuleName is not installed" -foregroundcolor Red
}

Don't get me wrong, Get-module -listAvailable | where {$_.name -eq "ModuleName"} works very well, it just takes too long for me if you have a lot of modules installed.

PSNoob
  • 1
0

Another Option instead of try/catch:

$Error.Clear()
Import-Module ActiveDirectory
if ($Error){
    "AD Module not installed. Installing."
    Add-WindowsCapability -Name Rsat.ActiveDirectory.DS-LDS.Tools~~~~0.0.1.0 -Online
}
dognose
  • 20,360
  • 9
  • 61
  • 107
0

For example, if you want to check if the sharepoint online module is installed, and if not install it....

if (-not(Get-Module -Name Microsoft.Online.SharePoint.PowerShell -ListAvailable | Select Name,Version))
{
 Install-Module -Name Microsoft.Online.SharePoint.PowerShell -Confirm:$false -Force
}