Search code examples
powershellmoduleverbose

PowerShell ignoring Write-Verbose while running Import-Module


For presenting the problem, I have this simple script saved as PowerShell module (test.psm1)

Write-Verbose 'Verbose message'

In real life, it includes command to import additional functions, but that is irrelevant at the moment.

If I run Import-Module .\test.psm1 -Verbose -Force I get only

VERBOSE: Loading module from path 'C:\tmp\test.psm1'.

My Write-Verbose is ignored 😟

I tried adding cmdletbinging but it also did not work.

[cmdletbinding()]
param()

Write-Verbose 'Verbose message'

Any clue how to provide Verbose output while importing the PowerShell module?

P.S. I do not want to display Verbose information always, but only if -Verbose is specified. Here would be my expected output for these two different cases:

PS C:\> Import-Module .\test.psm1 -Verbose -Force # with verbose output
VERBOSE: Loading module from path 'C:\tmp\test.psm1'.
VERBOSE: Verbose message

PS C:\> Import-Module .\test.psm1 -Force # without verbose output

PS C:\>

Solution

  • That is an interesting situation. I have a theory, but if anyone can prove me wrong, I would be more than happy.

    The short answer: you probably cannot do what you want by playing with -Verbose only. There may be some workarounds, but the shortest path could be setting $VerbosePreference.


    First of all, we need to understand the lifetime of a module when it is imported:

    When a module is imported, a new session state is created for the module, and a System.Management.Automation.PSModuleInfo object is created in memory. A session-state is created for each module that is imported (this includes the root module and any nested modules). The members that are exported from the root module, including any members that were exported to the root module by any nested modules, are then imported into the caller's session state. [..] To send output to the host, users should run the Write-Host cmdlet.

    The last line is the first hint that pointed me to a solution: when a module is imported, a new session state is created, but only exported elements are attached to the global session state. This means that test.psm1 code is executed in a session different than the one where you run Import-Module, therefore the -Verbose option, related to that single command, is not propagated.

    Instead, and this is an assumption of mine, since I did not find it on the documentation, configurations from the global session state are visible to all the child sessions. Why is this important? Because there are two ways to turn on verbosity:

    • -Verbose option, not working in this case because it is local to the command
    • $VerbosePreference, that sets the verbosity for the entire session using a preference variable.

    I tried the second approached and it worked, despite not being so elegant.

    $VerbosePreference = "Continue" # print all the verbose messages, disabled by default
    Import-Module .\test.psm1 -Force
    $VerbosePreference = "SilentlyContinue" # restore default value
    

    Now some considerations:

    • Specifying -Verbose on the Import-Module command is redundant

    • You can still override the verbosity configuration inside your module script, by using

      Write-Verbose -Message "Verbose message" -Verbose:$false
      

      As @Vesper pointed out, $false will always suppress the Write-Verbose output. Instead, you may want to parameterized that option with a boolean variable assigned in a previous check, perhaps. Something like:

      if (...) 
      {
          $forceVerbose=$true
      } 
      else 
      { 
          $forceVerbose=$false
      }
      
      Write-Verbose -Message "Verbose message" -Verbose:$forceVerbose
      
    • There might be other less invasive workarounds (for instance centered on Write-Host), or even a real solution. As I said, it is just a theory.