I would like to run a script that requires to use commands from the GroupPolicy module. However, this module is not guaranteed to be installed on every machine and some machines might be stand alone. Can I have have the module copied over with my scripts and run commands from it, without installing the module?
Can I have have the module copied over with my scripts and run commands from it, without installing the module?
Yes. Installing a module in PowerShell simply means placing it one of the directories listed in the $env:PSModulePath environment variable, which allows PowerShell to discover it and import (load) it on demand, as part of the module auto-loading feature
However, you're free to copy modules elsewhere, as long as the code that relies on them knows their path and loads them with an explicit Import-Module call.
For instance, if you bundle your script with a GroupPolicy subdirectory containing that module, the script could import it as follows:
# $PSScriptRoot contains the calling script's directory.
Import-Module $PSScriptRoot\GroupPolicy
Related
We learn that Powershell introduced Module Auto-Loading in 3.0 :
... PowerShell imports modules automatically the first time that you
run any command in an installed module. You can now use the
commands in a module without any set-up or profile configuration, ...
And this is done via PSModulePath.
What the docs fail to explain is how Powershell can detect which commands are in a module without first loading the module.
That is, when I use Import-Module, I "know" that Powershell will execute (??!) the powershell code in my .psm1 file, exporting all functions, ... or whatever I spec with Export-Modulemember.
However, the Auto-Load feature has to know before hand that a certain command is available via a certain module without actually loading the module.
Since we had some misbehaving third party modules in PsModulePath and since we have a very few modules that we wrote ourselves that we like to anchor in PSModulePath, I would very much like to understand how the files in PSModulePath are processed.
This is a partial answer.
This is done/implemented via Get-Command and this seems to be enabled to "parse" module files without actually executing the PoSh Code there. See below.
From the powershell docs:
Implicitly Importing a Module
... works on any module in a directory that is included in the value
of the PSModulePath environment variable ...
To support automatic importing of modules, the Get-Command cmdlet
gets all cmdlets and functions in all installed modules, even if the
module is not imported into the session. ...
And then:
Get-Command
The Get-Command cmdlet gets all commands that are installed on the
computer ...
Get-Command that uses the exact name of the command, without
wildcard characters, automatically imports the module that contains
the command so that you can use the command immediately. ...
Get-Command gets its data directly from the command code, unlike
...
The docs do not explain how this is implemented, but one could possibly look up how it's done in the source code. (I wasn't so far able to find it, despite browsing the sources for a while.)
Incidentally I find mentioned that Powershell 3 was the first version to expose the AST, so it stands to reason that the posh code does exactly that: Parse the Scripts and inspect their AST in some way to determine if the command is provided.
I have a custom script module and one of the functions depends on the AWSPowerShell module. I attempted to put Import-Module AWSPowerShell inside that function, but when this runs on the server it fails and says no valid module is found. If I try Import-Module AWSPowerShell from the command line on the same server running as the same user it works fine. There seems to be something wrong with calling Import-Module from inside another module.
I saw that NestedModules can be used to specify dependencies so I added NestedModules #('AWSPowerShell') to the module manifest and removed the Import-Module AWSPowerShell from the function that needs it.
Now the error about AWSPowerShell happens at the point where I import my own custom module from the calling script.
The exact error is: Import-Module : The module to process 'AWSPowerShell', listed in field 'NestedModules' of module manifest 'C:\Program Files\WindowsPowerShell\Modules\...MyModule.psd1' was not processed because no valid module was found in any module directory.
I did notice that the AWSPowerShell module is installed in a different folder path than my custom module. My module is in C:\Program Files\WindowsPowerShell\Modules and AWSPowerShell is in C:\Program Files (x86)\AWS Tools\PowerShell.
How can I set this up so that AWSPowerShell can be loaded for use inside my module?
Update
I made a copy of the AWSPowerShell module folder under C:\Program Files\WindowsPowerShell\Modules and now the module imports successfully using the NestedModules method. Everything I saw about NestedModules appeared to be for combining modules that were being developed for a bigger solution. I'm not sure about using this technique for importing a 3rd party module like this. I may be misusing this feature.
The NestedModule module-manifest (.psd1) entry is for modules that are bundled with your module and available in your module's code only.
RequiredModules is for importing independently pre-installed modules, which are imported into the global scope when your module is imported, which means that their commands are also available to the caller.
Note: This answer shows how to specify RequiredModules modules to be installed on demand by declaring them as external dependencies, but the limitation appears to be that both the module and its dependency must come from the same repository and must be installed with Install-Module.
i have custom functions list, and another config file that is going to be used by the functions, my goal is to run those functions on remote servers, considering that some functions call other function from within them that's why i can't use the method below to call functions that are loaded in the local session.
invoke-command -scriptblock ${function:foo}
is it possible to make a module out of it and then make that module get imported automatically on system boot rather than user logon.
any suggestions on how to accomplish the main goal ?
Well, if you want to install Powershell Module on the system just copy it to appropriate directory.
Installing Modules for all Users in Program Files
If you want a module to be available to all user accounts on the computer, install the module in the Program Files location.
$EnvProgramFiles\WindowsPowerShell\Modules\<Module Folder>\<Module Files>
Reference: https://msdn.microsoft.com/en-us/library/dd878350(v=vs.85).aspx
Install Modules in PSModulePath whenever possible, install all modules in a path that is listed in the PSModulePath environment variable or add the module path to the PSModulePath environment variable value.
The PSModulePath environment variable ($Env:PSModulePath) contains the locations of Windows PowerShell modules. Cmdlets rely on the value of this environment variable to find modules.
By default, the PSModulePath environment variable value contains the following system and user module directories, but you can add to and edit the value.
$PSHome\Modules (%Windir%\System32\WindowsPowerShell\v1.0\Modules)
# This location is reserved for modules that ship with Windows. Do not install modules to this location.
$Home\Documents\WindowsPowerShell\Modules (%UserProfile%\Documents\WindowsPowerShell\Modules)
$Env:ProgramFiles\WindowsPowerShell\Modules (%ProgramFiles%\WindowsPowerShell\Modules)
We have a TFS build process using a custom template. The build works fine with the crm solution files successfully being created. We are then attempting to run a powershell script on the post build. This script imports a module. But the commands in the module are not being exposed. Whenever we attempt to invoke a command, we get an error. Along the lines of
The term 'Get-XrmSolution' is not recognized as the name of a cmdlet,
function, script file, or operable program.
It doesn't matter which command we use in that module, we get the same kind of error.
To troubleshoot, we have logged onto the build server under the identity of the build account and can successfully run the script we are attempting to run.
Putting some more output into the script to troubleshoot...
Get-Module lists Xrm.Framework.CI.PowerShell. - Good.
Get-Command -Module "Xrm.Framework.CI.PowerShell" returns nothing. From the console, a number of commands are listed.
Is there something we need to do with the running of powershell post build scripts to enable the contents of an imported module to be seen?
Watch out for the bitness of PowerShell invoked by MSBuild and likewise, the bitness of MSBuild launched by Visual Studio. Certain modules only run in either 32-bit or 64-bit PowerShell and not both. You want to make sure the correct version of PowerShell is getting launched.
IIRC you have to explicitly import the module with no assumption of being loaded on user profile, nor on the script path.
Suppose Module1.psm1 is in the same folder as your script, use something like
Import-Module (Join-Path (Split-Path -Parent $MyInvocation.MyCommand.Definition) 'Module1.psm1')
I had the same problem.
The module was loaded in the 'C:\Program Files\WindowsPowerShell\Modules' folder (64 bits).
It all seemed fine when I logged on as the user, but it failed during TFS build.
Solution: I had to uninstall the module in PowerShell 64 bit and re-install in PowerShell 32 bit.
The module was then installed in 'C:\Program Files (x86)\WindowsPowerShell\Modules' folder.
I am creating a module that depends on several other modules that I need loaded into the global environment. I tried creating a script and using ScriptsToProcess to import the modules, but it looks like the check for RequiredModules happens before ScriptstoProcess runs.
Is there a nice, clean way in a Module Manifest to both Require a module and load it automatically if its not already loaded? If the module could not be loaded, then the RequiredModule would throw an error.
In PowerShell 3 and up, RequiredModules are automatically loaded
It's also the only way to make sure people using PowerShellGet (i.e. the PowerShell Gallery) install your dependencies, if you're going to distribute the module.
It would still fail if the required modules are missing, but otherwise works exactly the way you'd wish it to.
In PowerShell 2, there's no way to automatically load RequiredModules
In either case, users can manually load the requirements by typing Import-Module RequiredModule, YourModule -- they won't get a second instance if it's already imported ...
You can also specify the module in NestedModules instead. Even in PowerShell 2, those get loaded "inside" your module, but don't seem to negatively affect resources when they're already loaded. However, as #JasonMArcher reminded me, in PowerShell 2, NestedModules get unloaded along with your module if your module gets unloaded (via Remove-Module), and this happens even if they were pre-loaded separately by the user, which can end up producing really weird bug reports since your users won't expect that.
The other option, which works in all versions of PowerShell, is to call Import-Module at the top of your module (in a psm1 script, after checking to make sure the module's not already loaded) with the -ErrorAction Stop set so that the import of your module will fail if the import of the dependent module fails.
if (!(Get-Module Dependency)) { ## Or check for the cmdlets you need
## Load it nested, and we'll automatically remove it during clean up
Import-Module Dependency -ErrorAction Stop
}
Actually, if you wanted to check for versions ...
if (!(Get-Module Dependency | Where { $_.Version -ge "2.5" )) {
## Load version 2.5 (or newer), or die
Import-Module Dependency -Version 2.5 -ErrorAction Stop
}
Just remember that this doesn't serve as documentation, so if you distribute the module, your users will not know about the dependencies.
PowerShell V3 works somewhat differently. Required modules are now loaded when you load a manifest with that key specified. Also, removing the module does not unload the required module, no matter how that module was loaded. Interestingly, -Force does not seem to show the autoloading of the required module(s).
Edit: If your module is not in one of the default locations, you will need to add the path to that module (or the parent folder under which the module resides) to the $env:PSModulePath variable.
Side note: since PSv3 there is support for the #Requires command in scripts. You can use this to (1) attempt to load a module dependency in your script and (2) terminate if the module is not/cannot be loaded.
#Requires -Modules ModuleName1,ModuleName2
This also needs the modules to be somewhere under $env:PSModulePath, which must be set before running the script.
about_Requires