How do I make my own alias override a module's alias? - powershell

I have an alias for touch created in my $profile, however I also use PSCX seems to have it's own touch. When I run touch the PSCX one runs:
$ which touch
Pscx\Set-FileTime
How do I make PowerShell prefer my alias over the modules alias?

In your $PROFILE:
First, explicitly import the PSCX module - at that point, that module's touch alias is imported and would replace any existing definition: Import-Module PSCX
Note: Add Import-Module PSCX even if it currently isn't in your $PROFILE; typically, the module is auto-loaded, on demand, by virtue of being located in one of the directories listed in the $env:PSModulePath environment variable.
It is that on-demand importing - whose timing is unpredictable (whenever you first happen to call a command from the module) - that prevents reliable alias replacement; forcing import of the module in your $PROFILE before you define your version of the alias solves that problem.
Then redefine the alias as needed; e.g.: Set-Alias touch touch.exe
Caveat: If someone forcefully re-imports the module later with Import-Module -Force PSCX, your alias will again be replaced.
Note: It's not necessary for the specific alias in question, but in order to replace aliases that are defined with -Option AllScope (true for built-in aliases such as dir and select), you're faced with two suboptimal choices:
Either: Redefine it with -Option AllScope, which, however, means that all scopes will see the new definition; e.g., Set-Alias -Option AllScope touch touch.exe.
Or: Remove the original alias first - e.g., Remove-Item alias:touch (PSv6+: Remove-Alias touch) - and then define it as needed, which, however, means that other scopes will no longer see the original definition.

Making this an answer rather than comment so I can type more text.
See https://serverfault.com/questions/452430/how-to-override-the-default-dir-alias-in-powershell
Opinion follows:
However, depending on your usage, I would strongly recommend against using aliases in any script or module as they can be changed and may not perform the actions you want.
If you really want to use an alias, I might just avoid all this and create a non-conflicting alias that still makes sense. Maybe tf or tfile for TouchFile.

Related

How to export variables from a PowerShell module

I've defined a variable in a psm1 file but when I try to access it in another script, after importing the module, I'm not seeing the value set in the psm1 file.
globals.psm1
$blah = "hello world"
my-script.ps1
Import-Module "$PSScriptRoot\globals.psm1" -Force -Verbose
Write-Output "blah: ${blah}"
output
PS C:\blah> .\my-script.ps1
VERBOSE: Loading module from path 'C:\blah\globals.psm1'.
blah: ''
I thought all variables get exported by default. I must be interrupting this wrong:
Specifies the variables that the module exports to the caller's session state. Wildcard characters are permitted. By default, all variables ('*') are exported
source: MSFT Docs -> How to write a PowerShell module manifest
(CTRL + F on 'VariablesToExport' to find the quoted text)
And yes, if I export the variable, I can access it but the documentation says: 'By default, all varialbes ('*') are exported so what am I doing wrong or misunderstanding? 🤔
globals.psm1
$blah = "hello world"
Export-ModuleMember -Variable blah
Your module is not using a module manifest (a companion .psd1 file whose RootModule entry points to your .psm1 file in the case of script modules), whereas the documentation you quote pertains to module manifest-based modules.
If a module consists only of a .psm1 file, and that file contains no Export-ModuleMember calls, the following rule applies:
Only functions and aliases are automatically exported.
Conversely, this means: in order to also export variables, you must use an Export-ModuleMember call - and if you do, the slate is wiped clean, so to speak, and you must explicitly specify all definitions you want to export (in the simplest case, use Export-ModuleMember -Function * -Alias * -Variable *).
Also, be sure to place this call at the end of your .psm1 file, to ensure that all definitions to export have already been defined.
Caveat, if a manifest (.psd1) is used:
The manifest's *ToExport keys apply on top of what the .psm1 file - implicitly or explicitly - exports, i.e. you can use it to further narrow what is to be exported, by explicitly enumerating the elements to export, which not only makes the module more self-describing, but also helps performance when PowerShell auto-discovers the commands in available, but not-(yet)-imported modules.
Therefore, if a manifest-based module wants to export variables, it too must have an explicit Export-ModuleMember call in its .psm1 file, with the manifest potentially narrowing down what variables are ultimately to be exported.
Generally, exporting variables from modules is best avoided, because:
it increases the risk of name collisions with variables of the same name defined elsewhere.
discovering which variables are exported by what module isn't as well-known as use of Get-Command is in order to determine what module a given function, cmdlet, or alias comes from. This is because (a) modules that export variables are rare and users generally don't expect it, and (b) the Get-Variable cmdlet - which can tell you what module a variable is defined in - isn't often used in practice.
To see which definitions a given module exports, pass -Verbose to the Import-Module call that imports it. Additionally, pass -Force in order to force re-loading of an already imported module.

Why do I have to open a new powershell window to get the changes to my psm1 files? [duplicate]

I created a custom powershell module in the
C:\Program Files\WindowsPowerShell\Modules\PennoniAppManagement directory. Whenever I make changes to a function in the module, then import the module into a script, the updated code won't take effect. Any solutions?
Make sure you remove the already-loaded version of the module from the session before re-importing it:
Remove-Module PennoniAppManagement -Force
Import-Module PennoniAppManagement
Normally, Import-Module-Force - by itself - is enough to force reloading of an updated module into the current session.
Import-Module -Force implicitly performs Remove-Module before reloading the module (if the module isn't currently loaded, -Force just loads the module normally).
Also note that force-reloading a module is not an option if you're loading it via a using module statement (at least as of PowerShell 7.1.2). Notably the using module method of importing is required if a module exports custom class definitions that the caller should see - see this answer for details.
Mathias' two-step approach - Remove-Module -Force, followed by Import-Module - is apparently needed in some cases, and seems to be required in yours.
It would be good to understand when the two-step approach is needed. Mathias thinks it is related to cached versions of custom class definitions (used module-internally) lingering instead of getting reloaded and redefined when Import-Module -Force is called. That is, while the module overall may get reloaded, it may be operating on stale classes. At least in the simple scenario below I was not able to reproduce this problem, neither in Windows PowerShell 5.1, nor in PowerShell (Core) 7.2.1, but there may be scenarios where the problem does surface.
The Remove-Module documentation describes the -Force parameter solely as relating to the - rarely used - .AccessMode property available on a loaded module's module-information object (you can inspect it with (Get-Module ...).AccessMode). The default value is ReadWrite, which allows unloading (removal) of the module anytime. If the property value is ReadOnly, Remove-Module -Force is needed to unload; if it is Constant, the module cannot be removed from the session at all, once loaded - at least not with Remove-Module.
Notably, the implicit unloading that happens with Import-Module -Force is not subject to these restrictions and implicitly unloads a module even if its .AccessMode is Constant (as of PowerShell 7.1.2; I am unclear on whether that is by design).
Test code involving reloading a module with a modified class definition, to see if Import-Module -Force is enough:
# Create a template for the content of a sample script module.
# Note: The doubled { and } are needed for use of the string with
# with the -f operator later.
$moduleContent = #'
class MyClass {{
[string] $Foo{0}
}}
function Get-Foo {{
# Print the property names of custom class [MyClass]
[MyClass]::new().psobject.Properties.Name
}}
'#
# Create the module with property name .Foo1 in the [MyClass] class.
$moduleContent -f 1 > .\Foo.psm1
# Import the module and call Get-Foo to echo the property name.
Import-Module .\Foo.psm1; Get-Foo
# Now update the module on disk by changing the property name
# to .Foo2
$moduleContent -f 2 > .\Foo.psm1
# Force-import (reload) the module and
# see if the property name changed.
Import-Module -Force .\Foo.psm1; Get-Foo
# Clean up.
Remove-Item .\Foo.psm1
In both Windows PowerShell (whose latest and last version is v5.1) and PowerShell (Core) 7.2.1 (current as of this writing), the above yields, as expected:
Foo1 # Original import.
Foo2 # After modifying the class and force-reloading

Write to Profile File After Installing PowerShell Module with PowerShellGet

I have a custom PowerShell module with two cmdlets. I have it successfully, but manually, deployed on my machine. However, I deployed it by placing the binary file and module manifest in a location, and then registering the module. I also had to manually write an Import-Module command into my 'all users' profile.
Now I am sure I can deploy this module with Publish-Module, but how do I get the Install-Module to write the Import-Module statement to the profile file?
As of PowerShell 3.0, a module is automatically imported when a command from the module is invoked. This was a brilliant on Microsoft's part; however, it did require that modules are located in a location where PowerShell looks for modules by default. Makes sense. You can see those locations by running the following command:
$env:PSModulePath -split ';'
Is there a reason you'd rather not use one of the paths stored in the above environmental variable? That said, I'd keep your code out of the "C:\Windows\System32..." path. The other options are better: "C:\Program Files\PowerShell\Modules" (AllUsers) and "C:\Users\tommymaynard\Documents\PowerShell\Modules" (CurrentUser). Depending on your PowerShell version/OS, those path could be different. You won't need to write an Import-Module command into a $PROFILE script if you get the module into a preferred location. Maybe you already know this, but maybe not.
You're not going to get Install-Module to write to any of the $PROFILE scripts.
$PROFILE | Select-Object -Property *
Well, not by default anyway. You could write your own Install-Module function, that runs PowerShellGet's Install-Module function, and includes writing to various $PROFILE scripts. The problem is that you'll need to include logic so you don't blow away the contents of someone's $PROFILE script if it's not empty, and only append to it.
Seriously though, this is turning into a lot of work, when you could drop the module into a location where PowerShell can find it on its own.
Edit: It just occurred to me, you can add a value/path to the $env:PSModulePath environmental variable. It's a single string with semi-colon delimiters:
$env:PSModulePath.GetType().Name
Therefore, it'd look like this:
$env:PSModulePath += ';C:\Another\Path'
That's great and all, but again how might you stage this, right? It takes you back to the write-to-all-the-$PROFILE-scripts problem,... although you may be able to update the variable via Group Policy Preferences. Again, probably better to just relocate your module.

How do I force powershell to reload a custom module?

I have created a module 'ActiveDirectory.psm1' which contains a class in powershellv5. I am importing that module in another file called 'test.ps1' and then calling a method from the class.
test.ps1 contains the following:
using module '\\ser01\Shared\Scripts\Windows Powershell\modules\ActiveDirectory\ActiveDirectory.psm1'
Set-StrictMode -version Latest;
$AD = [ActiveDirectory]::New('CS');
$AD.SyncGroupMembership($True);
It all works as expected BUT when I make a change to ActiveDirectory.psm1 & save the changes they aren't reflected immediately. i.e. if ActiveDirectory.psm1 contains:
write-verbose 'do something';
If I change that to
write-verbose 'now the script does something else';
the output remains 'do something'
I'm guessing it has stored the module in memory and doesn't reload it therefore missing the changes I have made. What command do I need to run to load the most recent saved version of the module?
As suggested by wOxxOm, you can try pass the -Force flag:
Import-Module ... -Force
Or if that does not work try to explicitly remove it and then reimport with:
Remove-Module
From what I've gathered. Import-Module does not import classes. You have to use the "using module " and it has to be in the first line of your script. On top of that problem, the classes appear to be "cached" in some esoteric way that precludes any uninstall-module or remove-module options. I've found I basically need to restart my powershell terminal to clear it.
If classes are not involved use import-module OR install-module. In both cases you can do a get-modules -all or get-installedmodule and then remove-module or uninstall-module. You want to make sure you look for all versions and pipe that to remove/uninstall to ensure you wipe everything out.
For anyone else coming across this issue, see https://github.com/PowerShell/PowerShell/issues/2505
It seems that there is a known long-standing bug regarding importing of modules that are anything above rudimentary level in complexity (for example, I have a module with a single class and class method that fails to update).

Is there ever a reason to explicitly Import-Module?

I was just reading the PowerShell Modules guide page and I noticed a line on the Import-Module section:
The following actions trigger automatic importing of a module, also
known as "module auto-loading."
Using a cmdlet in a command. For
example, typing Get-ExecutionPolicy imports the
Microsoft.PowerShell.Security module that contains the
Get-ExecutionPolicy cmdlet.
So given that, why should we ever care about using Import-Module? Isn't it always taken care for us automatically? In what case would I need to explicitly write out Import-Module?
You have to use Import-Module in the following cases :
The module file is not in a path included in $PSModule Path
You have different modules with the same name but in different paths
The module is already loaded and you want to reload it after making modifications to it. (with -Force)
To import only specific cmdlets, functions or variables from that module (with the -Cmdlet, -Function, and -Variable parameters respectively)
To prevent loading cmdlets or functions from the module that would overwrite the commands with the same name and are already loaded in the current session ( with -NoClobber )
To add a prefix to the nouns of the cmdlets in this module ( with -Prefix)
To import a module from a remote computer (with the -PSSession parameter )
The list is not totally exhaustive but these are the main use cases for the Import-Module cmdlet.
I know there is already an accepted answer, but I wanted to add my two cents.
To explicitly document the dependency of a script upon a module
If $PSModuleAutoloadingPreference is set to "none", modules need to be explicitly loaded. You don't know if users have turned this off or not.