Persisting PSModulePath across all sessions - powershell

I have a PSModule that I would like to put in my own PSTools directory instead of the standard powershell reserved directory for PSModule. I used the command
$env:PSModulePath += ';c:\MyPSModule'
This works but it is only good for my current session and PS seems to have forgotten my settings after a new session is created and I have to set it up again.
How do I persist my $env settings across all powershell sessions and if possible for all users? If possible, I do not want to hack the registry. Just ps command only if this is feasible.

$CurrentValue = [Environment]::GetEnvironmentVariable("PSModulePath", "Machine")
[Environment]::SetEnvironmentVariable("PSModulePath", $CurrentValue + ";C:\MyPSModule", "Machine")
To add a persistent variable by using a script, use the
SetEnvironmentVariable method on the Environment class. For example,
the above script adds the "C:\MyPSModule" path
to the value of the PSModulePath environment variable for the
computer. To add the path to the user PSModulePath environment
variable, set the target to "User".
https://msdn.microsoft.com/en-us/library/dd878326(v=vs.85).aspx

Related

Changing the PSModulePath in Powershell 5

I've used the following to permanently change the PSModulePath:
[Environment]::SetEnvironmentVariable('PSModulePath', "ABC", "Machine")
This works fine when I call the below (it returns "ABC"):
[Environment]::GetEnvironmentVariable('PSModulePath', "Machine")
But in any Powershell Session when I run:
$env:PSModulePath
I get:
C:\Users\myname\Documents\WindowsPowerShell\Modules;ABC
Where is this path coming from, is it PS5 magic? I've checked the "User" target and this is blank. It's as if something is pre-pending the PSModulePath with this default path?
The environment drive Env: contains the environment variables specific to the current user's session (source). It is equivalent to the Process scope. So
[Environment]::GetEnvironmentVariable('PSModulePath', 'Process')
should be equivalent to
$env:PSModulePath
The Process scope contains the environment variables of a particular process. It gets constructed like this (source):
This list of variables is inherited from the parent process and is constructed from the variables in the Machine and User scopes.
As you checked both, the Machine and the User scope, and did not find the path, it has to come from the parent process, which is PowerShell itself. And this is indeed the case as can be read here:
The CurrentUser module path is prefixed only if User scope $env:PSModulePath doesn't exist. Otherwise, the User scope $env:PSModulePath is used as defined.
As you confirmed in your question,
[Environment]::GetEnvironmentVariable('PSModulePath', 'User')
is empty and thus $env:PSModulePath has been prefixed by the CurrentUser module path, which is $HOME\Documents\PowerShell\Modules or $HOME\Documents\WindowsPowerShell\Modules depending on your Windows version.
You can read more about environment variables in my answer here.

How to permanently remove a UNC path from $env:PSModulePath for PowerShell?

Where I work, users have their folders redirected to a UNC path to save data. This seems to have affected my PowerShell because every time I start up powershell, it attempts to load modules from the UNC path and it takes a long time. This also affects how I use cmdlets because it tries to search the UNC path for cmdlet context.
When I look at the output of $env:PSModulePath I can see the UNC directory. But it doesn't show up in System's Environment Variables dialog editor.
How can I get rid of this so that Powershell doesn't keep looking for module support from the UNC directory? I understand that I can edit an existing sessions $env:PSModulePath, but I want it gone forever.
The Microsoft documentation page about_PSModulePath explains it. By default, PowerShell builds the $env:PSModulePath value by concatenating:
The user's "Documents" folder, likely the UNC path you are mentioning.
The contents of the PSModulePath system environment variable.
You can find out the path of the "Documents" folder as follows:
[Environment]::GetFolderPath('MyDocuments')
If you define a user-level environment variable PSModulePath in addition to the system environment variable PSModulePath, it will replace your "Documents" folder in $env:PSModulePath, which won't contain the UNC path anymore.
To permanently change the PSModulePath environment variable, you can use the .NET method SetEnvironmentVariable on the System.Environment class.
Example:
[Environment]::SetEnvironmentVariable("PSModulePath", "Some_Path", "Machine")
Keep in mind, this will overwrite what you had before on this variable. So make sure before you run the method as the PSModulePath will certainly have paths that you might need.
Also, if your company is using GPOs to set the PSModulePath variable, then the only way to remove the UNC path is to talk to your administrator that handles that.
I don't know why it is not shown in your system variables GUI. You should be able to remove it with something along these lines:
$yourvalueyouwanttodelete = 'somepath'
$oldval = [environment]::GetEnvironmentVariable("PSModulePath")
$arr_oldval = $oldval -split ";"
$arr_newval = $arr_oldval | ? {$_ -ne $yourvalueyouwanttodelete}
$newval = $arr_newval -join ";"
[environment]::SetEnvironmentVariable("PSModulePath", $newval)
The way I resolved this was to set the Module Path in the All users profile
For me $PROFILE.AllUsersAllHosts returned:
C:\Program Files\PowerShell\7\profile.ps1
I created that file and added the following:
$ModulePath = "c:\program files\powershell\7\Modules"
[Environment]::SetEnvironmentVariable("PSModulePath", $ModulePath)
I ran into the same issue after removing PowerShell 7 from my device. What appears to be happening is that the PSModulePath variable to My Documents is removed from the environment variables under Advanced System Settings. Powershell then uses the UNC path, which is configured by group policy.
The solution, that worked for me was to add a new Environment Variable called PSModulePath with the value of C:\Users\YourUserNameHere\Documents. Now powershell has a place to look for those PS Modules.

How to change PowerShell default module installation folder?

Is there a way to change PowerShell module installation folder (the folder that modules are placed after Install-Module)?
This is why I want to do this:
I'm on Windows 10, PowerShell 5.1.17763.503
My default installation folder is Documents\WindowsPowerShell\Modules
My Documents folder have been moved to a location containing , symbol (corporate policies)
PS has a bug loading .ps1 that contain classes and have , in the file path (similar to this issue.)
What I've tried:
I thought the installation folder is the first folder in the $env:PSModulePath and I can change it. When I've opened "Edit System Environment Variables" I saw the installation folder is not in the $env:PSModulePath. It's automatically added on the variable when you start PowerShell.
There is no way to change the behaviour of Install-Module so it installs modules in a custom path.
However, You can use Install-Module [...] -Scope AllUsers to install the modules for all users. This would install the modules in $env:ProgramFiles\PowerShell\Modules, but this operation requires elevated permissions (a.k.a. Local Administrator rights).
If you download and install modules to a custom path yourself (or use an alternative implementation to Install-Module), you can modify $env:PSModulePath as you wish.
You can use a profile to patch the $env:PSModulePath every time you start a PowerShell session by adding this to one of your profiles:
# Prepend custom module path.
$env:PSModulePath = ((#("C:\mymodulepath") + ($env:PSModulePath -split ";")) -join ";")
From Modifying the PSModulePath Installation Path
To add paths to this variable, use one of the following methods:
To add a temporary value that is available only for the current session, run the following command at the command line:
$env:PSModulePath = $env:PSModulePath + ";c:\ModulePath"
To add a persistent value that is available whenever a session is opened, add the following command to a Windows PowerShell profile:
$env:PSModulePath = $env:PSModulePath + ";c:\ModulePath"
For more information about profiles, see about_Profiles in the
Microsoft TechNet library.
To add a persistent variable to the registry, create a new user environment variable called PSModulePath using the Environment
Variables Editor in the System Properties dialog box.
To add a persistent variable by using a script, use the SetEnvironmentVariable method on the Environment class. For example,
the following script adds the "C:\Program Files\Fabrikam\Module" path
to the value of the PSModulePath environment variable for the
computer. To add the path to the user PSModulePath environment
variable, set the target to "User".
$CurrentValue = [Environment]::GetEnvironmentVariable("PSModulePath", "Machine")
[Environment]::SetEnvironmentVariable("PSModulePath", $CurrentValue + ";C:\Program Files\Fabrikam\Modules", "Machine")

How to use a variable as part of the path of a calling program

As a new PowerShell user, I am planning to call a local program whose path is not in the PATH environment variable. When calling the program the relative or full path of the program has to be given, e.g. .\Users\xxx\Downloads\program_name\bin\prog. However, as this path is way long and sometimes might be changed, so as a Linux Shell script programmer I would like to use a variable in the path, for instance .\$prog_home\bin\prog.
Hereby my question is how to initialize this variable so that it can be used as I assumed beforehands? I tried to initialized the variable in such way -
$prog_home="User\xxx\Downloads\program_name"
Unfortunately, this really can not work at all like in Linux Shell
Please consider to use the Join-Path cmdlet to combine a path. There is also a builtin USERPROFILE environment variable which you can use. This is how you could do it:
$prog_home = Join-Path $env:USERPROFILE 'program_name'
$prog = Join-Path $prog_home '\bin\prog.exe'
& $prog #execute it

Paths from environmental variables not available in Powershell

So I have installed the Team Foundation Server PowerShell Tools, and verified they exist, but the executables (TFPT.exe) do not appear to be available in powershell.
Looking at $env:path (and looking at the path variable through System Properties) I see that the end of the path variable looks like this:
$env:Path = {other variables};%TFSPowerToolDir%;%BPADir%;.;
System Properties Enviromental Variables = {other variables};%TFSPowerToolDir%;%BPADir%
When I look at $env:TFSPowerToolDir I get C:\Program Files (x86)\Microsoft Team Foundation Server 2013 Power Tools\, so that seems correct.
But if I try to run tfpt I get the error "The term 'tfpt.exe' is not recognixed as the name of a cmdlet...
If I first do cd $env:TFSPowerToolDir and the run tfpt it works fine. So the environmental variable is correct. But it doesn't seem to get placed in the path.
Any ideas on how to fish this?
Can't replicate the problem here, actually. The problem seems to be that other environment variables are not expanded in $Env:PATH here, but in a quick test PowerShell did so for me reliably.
You could try to work around the problem by manually expanding environment variables in your profile script. E.g. with something like the following:
$Env:PATH = [regex]::Replace($Env:PATH, '%([^%]+)%', {
param($m)
$n = $m.Groups[1].Value
Get-Content -Raw Env:\$n
})