title: Powershell module abort loading on unsupported hosts
body: <p>Glossary:</p>
  
  <ul>
  <li>Host: PowershellHost session</li>
  <li>Interactive: <code>[Environment]::UserInteractive -eq $True</code></li>
  </ul>
  
  <p>Scenario:</p>
  
  <p>Create a powershell module that only will abort propertly and without error on failed condition. In this case, some commands/modules only work properly in full interactive hosts, like ISE and Console, but not in fake interactive hosts like NuGet Package Manager Console</p>
  
  <p>Failed Solution: </p>
  
  <pre><code># Add value to Powershell manifest(psd1) 
  # Issue: Only supports a string for the `PowerShellHostName` property. How to specify both `ConsoleHost` and `Windows PowerShell ISE Host`? Unknown if this property supports regex, and even if it does, will the behavior change since it's not documented?
  @{
      ....
      # Name of the Windows PowerShell host required by this module
      # PowerShellHostName = ''
      ....
  }
  </code></pre>
  
  <p>Failed Solution:</p>
  
  <pre><code># Check for interactive shell
  # Issue: UserInteractive is still set in embedded shells like NuGet package manager
  # console. Commands that expect user input directly often hang.
  if([Environment]::UserInteractive) {
      # Do stuff, dotsource, etc
  }
  </code></pre>
  
  <p>Failed Solution:</p>
  
  <pre><code># Issue: returning still leaves module loaded, and it appears in Get-Module list
  # Even if value is supplied for return, powershell's return statement is 'special'
  # and the value is ignored
  if($Host.Name -inotmatch '(ConsoleHost|Windows PowerShell ISE Host)') {
      Write-Warning "Host [$($Host.Name)] not supported, aborting"
      return 
  }
  </code></pre>
  
  <p>Failed Solution:</p>
  
  <pre><code># Issue: Module isn't loaded, so it can't be unloaded
  if( $Host.Name -inotmatch '(ConsoleHost|Windows PowerShell ISE Host)' ) {
      Remove-Module ThisModuleName
  }
  </code></pre>
  
  <p>Failed Solution:</p>
  
  <pre><code># Issue: Powershell module error output is just passthrough, import-module 
  # still reports success, even though $Error is has more stuff than before
  if( $Host.Name -inotmatch '(ConsoleHost|Windows PowerShell ISE Host)' ) {
      Write-Error "Unsupported Host:" $Host.Name
  }
  </code></pre>
  
  <p>Annoying solution:</p>
  
  <pre><code># Issue: Leaves two errors on the stack, one for the throw, one for the module not 
  # loading successfully
  if($Host.Name -inotmatch '(ConsoleHost|Windows PowerShell ISE Host)') {
      throw "Host [$($Host.Name)] not supported, aborting"
  }
  </code></pre>
  
  <p>Not a solution:</p>
  
  <pre><code>Force user to wrap the import every time.
  </code></pre>
  
  <p>Questionable Solution:</p>
  
  <p>Split module into nested submodules, one for 'Common', and one for each supported Host. Use subfolder for each, and duplicate psd1 for each. Looks like it will end up being a maintainability nightmare, especially with respect to nested dependencies.</p>
  
  <pre><code>UberModule
      /ModuleCommon
          /ModuleCommon.(psd1|psm1)
      /ConsoleHostSpecific
          /ConsoleHostSpecific.(psd1|psm1)
      /IseHostSpecific
          /IseHostSpecific.(psd1|psm1)
      /etc...
  </code></pre>
  
  <p>Is there a better way to do this, or is the uber-module split the only way to go?</p>