How to troubleshoot the error [The term 'pwsh.exe' is not recognized as the name of a cmdlet, function, script file, or operable program]? - powershell

While creating a new pipeline on Azure DevOps to set up a CI for a .NET project, I set up the following PowerShell script to automate the .NET Core setup.
Here is the script:
$ErrorActionPreference="Stop"
$ProgressPreference="SilentlyContinue"
# $LocalDotnet is the path to the locally-installed SDK to ensure the
# correct version of the tools are executed.
$LocalDotnet=""
# $InstallDir and $CliVersion variables can come from options to the
# script.
$InstallDir = "./cli-tools"
$CliVersion = "1.0.1"
# Test the path provided by $InstallDir to confirm it exists. If it
# does, it's removed. This is not strictly required, but it's a
# good way to reset the environment.
if (Test-Path $InstallDir)
{
rm -Recurse $InstallDir
}
New-Item -Type "directory" -Path $InstallDir
Write-Host "Downloading the CLI installer..."
# Use the Invoke-WebRequest PowerShell cmdlet to obtain the
# installation script and save it into the installation directory.
Invoke-WebRequest `
-Uri "https://dot.net/v1/dotnet-install.ps1" `
-OutFile "$InstallDir/dotnet-install.ps1"
Write-Host "Installing the CLI requested version ($CliVersion) ..."
# Install the SDK of the version specified in $CliVersion into the
# specified location ($InstallDir).
& $InstallDir/dotnet-install.ps1 -Version $CliVersion `
-InstallDir $InstallDir
Write-Host "Downloading and installation of the SDK is complete."
# $LocalDotnet holds the path to dotnet.exe for future use by the
# script.
$LocalDotnet = "$InstallDir/dotnet"
When I try to run the build, I have got the following error:
and
I've already searched on Google for people who have the same problem and how to fix it. But I haven't found much information yet. The Azure DevOps forum doesn't help either.

As mentioned in the comment from above, all you have to do is install the appropriate version of PowerShell on the machine that Agent is running on. For example, PowerShell 7. Then you have to make sure that the environment variable path is set. This variable should point to the directory with PowerShell Core.
Windows
Just install PowerShell Core with the Windows Installer (.msi file from PowerShell Git repository). In this case, the path environment variable is automatically set or expanded so that there will be the path to the directory with pwsh.exe under this variable.
Linux
Install PowerShell Core that is supported by your distribution. Make sure that there is a path variable in your ~/.bashrc file and that path contains the path to the directory with PowerShell Core.
Note: If Azure Agent is already running, you have to restart it so that it sees the changes in the path variable. Hence, on Windows, just restart the agent if run interactively and restart the service if run as a service. On Linux, you can follow this guide in order to update the environment variables that were passed to the Agent.
I know you have already configured your script as a PowerShell Core script, but for completeness I add this: If you use a PowerShell task in your Azure pipeline, the Core version of PowerShell is not set for it by default. In order to run the task as the PowerShell Core script, add this to the YAML code of the task: pwsh: true. Otherwise, if you are still using the old graphical interface, check the "Use PowerShell Core" checkbox under the "Advanced" heading for the task.

Related

Scheduling powershell script to run on server - finding powershell.exe

I'm trying to schedule a powershell script to run on a server. I used $env:PSModulePath and one of the powershell locations is c:\windows\system32\windowsPowerShell\v1.0\Modules. When I look in that location, there is no Powershell.exe there. I wanted to use that for the Application name in the system scheduler. How can I verify that Powershell.exe is there? I also wanted to find which version each one is for all of the powershell versions seen with $env command above to verify which would be the 5.1 version that I tested on my computer with.
env:PSModulePath
c:\windows\system32\windowsPowerShell\v1.0\Modules is the Modules directory
$psHome returns the .exe location

Restart environment and script during batch script

I've built a few FFmpeg powershell scripts for me and a few others to use and I'm attempting to make the setup and update process as easy as possible. The end goal is to be able to run 1 batch file that installs Chocolatey, FFmpeg, git, clones the github repo (for updates), and edits the Windows registry to add the actual FFmpeg powershell scripts / console programs to the Windows Explorer contextual menu. This way I just pass them the folder containing everything once and any time I change or add something to the project I can just tell them to run the batch file again, and presto everything is up to date.
However I'm struggling to find a way to install Chocolatey, then git with Chocolatey, and then run a git command with the execution of a single .bat file. From what I can tell after installing Chocolatey I need to restart the shell entirely before I can install git, and then I have to restart the shell again before I can use a git command. As of right now most of the actual processing is happening via Powershell scripts that are launched from the .bat file, and as each step is taken I update a txt file, attempt to restart the batch script, and read the txt file to pick up where I left off:
#echo off
echo Administrative permissions required. Detecting permissions...
echo.
net session >nul 2>&1
if %errorLevel% == 0 (
echo Success: Administrative permissions confirmed.
echo.
) else (
echo Failure: Current permissions inadequate.
PAUSE
exit
)
set relativePath=%~dp0
set relativePath=%relativePath:~0,-1%
PowerShell -NoProfile -ExecutionPolicy Bypass -File "%relativePath%\Setup\CheckRequiredPackages.ps1" -relativePath "%relativePath%"
set /p step=<"%relativePath%\Setup\Step.txt"
if %step% == 1 (
(echo 2) > "%relativePath%\Setup\Step.txt"
PowerShell -NoProfile -ExecutionPolicy Bypass -File "%relativePath%\Setup\GetChocolatey.ps1"
start "" "%relativePath%\RunMe.bat"
exit
)
if %step% == 2 (
(echo 3) > "%relativePath%\Setup\Step.txt"
PowerShell -NoProfile -ExecutionPolicy Bypass -File "%relativePath%\Setup\GetRequiredPackages.ps1"
start "" "%relativePath%\RunMe.bat"
exit
)
if %step% == 3 (
(echo 0) > "%relativePath%\Setup\Step.txt"
PowerShell -NoProfile -ExecutionPolicy Bypass -File "%relativePath%\Setup\Update.ps1" -relativePath "%relativePath%"
)
PAUSE
Exit
The problem is using the start command in the batch script doesn't seem to work, I'm guessing since that new process is spawned from the same process that handles the Chocolatey install it doesn't count as actually restarting the shell. Is there any way to actually restart the shell and somehow have the batch file start back up without user intervention?
I'm not sure why I didn't initially think of reloading the path environment variable but that's a whole lot more reasonable than restarting the script 4 times with an intermediary file.
Firstly I moved 99% of the heavy lifting from the .bat file to a Powershell script, as the only reason I'm using Batch is so the user can easily run the file by clicking it in Explorer. I couldn't get RefreshEnv to work, which is a feature of Chocolatey, but running this between each new package worked great:
$env:Path = [System.Environment]::GetEnvironmentVariable("Path","Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path","User")
So I have something like this now, and the Batch scrip just launches this Powershell Script:
Write-Host "Installing / updating required packages..."
Set-ExecutionPolicy Bypass -Scope Process -Force; [System.Net.ServicePointManager]::SecurityProtocol =
[System.Net.ServicePointManager]::SecurityProtocol -bor 3072; Invoke-Expression ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1'))
$env:Path = [System.Environment]::GetEnvironmentVariable("Path","Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path","User")
choco install ffmpeg -y
choco install git -y
$env:Path = [System.Environment]::GetEnvironmentVariable("Path","Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path","User")
Write-Host "Deleting old files..."
Remove-Item -LiteralPath $relativePath -Force -Recurse
Start-Sleep 2
Write-Host "`nUpdating Files..."
git clone https://github.com/TheNimble1/FFmpegContextCommands.git $relativePath
Which installs Chocolatey, refreshes the path, installs FFmpeg & Git, refreshes the path, deletes the old files, and then clones the git to replace with new files.
Indeed, a start-launched process inherits the calling process' environment rather than reading possibly updated environment-variable definitions from the registry.
Chocolatey comes with batch file RefreshEnv.cmd (C:\ProgramData\chocolatey\bin\RefreshEnv.cmd, but C:\ProgramData\chocolatey\bin should be in the %PATH%) specifically to avoid having to start a new, independent session for environment updates to take effect.
Therefore, something like the following may work:
:: Assumes that Chocolatey was just installed to the default location.
call "%ProgramData%\chocolatey\bin\RefreshEnv.cmd"
:: If Chocolatey was *previously* installed and its installation directory
:: has already been added to %Path%, the following will do:
:: call RefreshEnv.cmd
call "%relativePath%\RunMe.bat"
Since Chocolatey is only being installed during your script's execution and its binaries folder is therefore not yet in %Path%, you'll have to call RefreshEnv.cmd by its full path, as shown above - which assumes the default install directory.
Your own answer now shows how to refresh the $env:Path (%Path%) environment variable using .NET methods directly _from PowerShell, which is a pragmatic solution.
Note, however, that RefreshEnv.cmd is more comprehensive in that it reloads all environment-variable definitions and therefore potentially newly added and modified ones.
Note that calling RefreshEnv.cmd from PowerShell does not work, because it then runs out of process (which means that it cannot update the calling process' environment).
However, Chocolatey offers an Update-SessionEnvironment PowerShell command (aliased to refreshenv), which you can make available immediately after a Chocolatey install as follows:
# Import the module that defines Update-SessionEnvironment aka refreshenv
Import-Module "$env:ProgramData\Chocolatey\helpers\chocolateyProfile.psm1"
# Refresh all environment variables.
Update-SessionEnvironment # or: refreshenv
See this answer for a more robust approach that doesn't rely on assuming that the default location was installed to.

wget not found by PowerShell script?

I have an old notebook with Windows 7 64-bit that executes a PowerShell script perfectly every Sunday. Unfortunately it starts to crash as soon as the load increases and I decided to get a new PC. On this PC I previously installed Windows&nbspe;10 Pro 64-bit and even here the script was executed every Sunday. Due to the update policy of Microsoft I removed Windows 10 from the new PC and installed Windows 7 64-bit. But now the same script crashes as it does not find wget:
$wg = Start-Process wget.exe -wait -NoNewWindow -PassThru -ArgumentList $argList
Gnu Wget is installed correctly (I think). It is placed at:
C:\Program Files (x86)\GnuWin32\bin\wget.exe
It is even entered in the registry under HKEY_LOCAL_MACHINE → SOFTWARE → Wow6432Node → GnuWin32|Wget|1.11.4-1|setup|InstallPath: C:\Program Files (x86)\GnuWin32.
But despite this if I open the CMD console and enter wget (or wget.exe) I get:
The order "wget" is either misspelled or could not be found.
What do I have to do that PowerShell finds wget constantly even after a restart of the PC? Even e.g. Notepad++ cannot be found by the CMD console despite it is installed properly(?). What's wrong here?
If you want to be able to run a command without specifying its path you need to add the directory it resides in to the PATH environment variable. The install path in the SOFTWARE branch of the registry has nothing to do with it.
To add a directory to the PATH for the current and all future sessions you need to do something like this:
$dir = "${env:ProgramFiles(x86)}\GnuWin32\bin"
# set PATH environment variable for current session
$env:Path += ";${dir}"
# set PATH environment variable for future sessions
$path = [Environment]::GetEnvironmentVariable('PATH', 'Machine')
$path += ";{$dir}"
[Environment]::SetEnvironmentVariable('PATH', $path, 'Machine')
Note, however, that the second step (setting the variable for future sessions) only works correctly if there are no Windows environment variables (%something%) used in $path, because the method saves the value as a REG_SZ in the registry. Windows only expands environment variables in the PATH variable if it's stored as a REG_EXPAND_SZ value.
If you do have regular Windows environment variables somewhere in $path you must manually write the value to the registry with the correct type.
$key = 'HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager\Environment'
Set-ItemProperty -Path $key -Name 'Path' -Value $path -Type ExpandString
Addendum:
All of the above applies only if you want to do this programmatically, of course. For a manual approach you can always edit the environment variables via the GUI and restart PowerShell.

How to refresh the environment of a PowerShell session after a Chocolatey install without needing to open a new session

I am writing automated script for cloning GitHub source code to local machine.
I failed after installing Git in my script, it asked for close/open powershell.
So I am not able to clone code automatic after installing Git.
Here is my code:
iex ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1'))
choco install -y git
refreshenv
Start-Sleep -Seconds 15
git clone --mirror https://${username}:${password}#$hostname/${username}/$Projectname.git D:\GitTemp -q 2>&1 | %{ "$_" }
Error:
git : The term 'git' is not recognized as the name of a cmdlet,
function, script file, or operable program.
Check the spelling of the name, or if a path was included,
verify that the path is correct and try again.
Please let me what should I put for reboot PowerShell without exiting?
You have a bootstrapping problem:
refreshenv (an alias for Update-SessionEnvironment) is generally the right command to use to update the current session with environment-variable changes after a choco install ... command.
However, immediately after installing Chocolatey itself, refreshenv / Update-SessionEnvironment themselves are only available in future PowerShell sessions, because loading these commands happens via code added to profile $PROFILE, based on environment variable $env:ChocolateyInstall.
That said, you should be able to emulate what Chocolatey does when $PROFILE is sourced in future sessions in order to be able to use refreshenv / Update-SessionEnvironment right away, immediately after installing Chocolatey:
iex ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1'))
choco install -y git
# Make `refreshenv` available right away, by defining the $env:ChocolateyInstall
# variable and importing the Chocolatey profile module.
# Note: Using `. $PROFILE` instead *may* work, but isn't guaranteed to.
$env:ChocolateyInstall = Convert-Path "$((Get-Command choco).Path)\..\.."
Import-Module "$env:ChocolateyInstall\helpers\chocolateyProfile.psm1"
# refreshenv is now an alias for Update-SessionEnvironment
# (rather than invoking refreshenv.cmd, the *batch file* for use with cmd.exe)
# This should make git.exe accessible via the refreshed $env:PATH, so that it
# can be called by name only.
refreshenv
# Verify that git can be called.
git --version
Note: The original solution used . $PROFILE instead of Import-Module ... to load the Chocolatey profile, relying on Chocolatey to have updated $PROFILE already at that point. However, ferventcoder points out that this updating of $PROFILE doesn't always happen, so it cannot be relied upon.
NEW:
The old approach I originally answered with has a few quirks with environment variables that use a ; delimiter. I tried compensated by handling PATH seperately, but sometimes there are other such variables.
So this is the new approach; you might want to put it in a script or something. It has to nest a couple of powershell processes real quick, which isn't ideal, but it's the only reliable way I've found to escape the active environment and capture the output.
# Call a powershell process to act as a wrapper to capture the output:
& ([Diagnostics.Process]::GetCurrentProcess().ProcessName) -NoP -c (
# String wrapper to help make the code more readable through comma-separation:
[String]::Join(' ', (
# Start a process that escapes the active environment:
'Start-Process', [Diagnostics.Process]::GetCurrentProcess().ProcessName,
'-UseNewEnvironment -NoNewWindow -Wait -Args ''-c'',',
# List the environment variables, separated by a tab character:
'''Get-ChildItem env: | &{process{ $_.Key + [char]9 + $_.Value }}'''
))) | &{process{
# Set each line of output to a process-scoped environment variable:
[Environment]::SetEnvironmentVariable(
$_.Split("`t")[0], # Key
$_.Split("`t")[1], # Value
'Process' # Scope
)
}}
OLD:
Did my best to try and make it a one-liner, but since the PATH variable needs special handling, I couldn't do it without making a stupidly long line. On the plus side, you don't need to rely on any third-party modules:
foreach ($s in 'Machine','User') {
[Environment]::GetEnvironmentVariables($s).GetEnumerator().
Where({$_.Key -ne 'PATH'}) | ForEach-Object {
[Environment]::SetEnvironmentVariable($_.Key,$_.Value,'Process') }}
$env:PATH = ( ('Machine','User').ForEach({
[Environment]::GetEnvironmentVariable('PATH',$_)}).
Split(';').Where({$_}) | Select-Object -Unique ) -join ';'
The code is scoped to the process, so you don't have to worry about anything getting messed up (not that it would, I tested it).
Note: Neither approach removes uniquely-named environment variables that were created in your active environment, so if you defined $env:FOO = 'bar' and 'FOO' is not normally one of your environment variables, $env:FOO will still return bar even after any of the above code is ran.
You can try and use Update-SessionEnvironment:
Updates the environment variables of the current powershell session with
any environment variable changes that may have occured during a Chocolatey package install.
That will test if that change is still effective after the chocolatey call.
If not, one easy workaround would be at least to use an absolute path for calling git.
To call Git from Powershell:
new-item -path alias:git -value 'C:\Program Files\Git\bin\git.exe'
Then you can try:
git clone --mirror https://${username}:${password}#$hostname/${username}/$Projectname.git D:\GitTemp -q 2>&1 | %{ "$_" }
I go with the lo-tech solution:
$env:Path += ";C:\Program Files\Git\bin"

PowerShell PackageManagement, how to uninstall a package provider?

To troubleshoot a problem, I thought I'd try reinstalling the Chocolatey package provider. There appears to be no cmdlet to remove or uninstall a package provider. I'm not referring to removing a package source or package. I'm using PowerShell 5 on Windows 10.
Is there a way to uninstall a package provider?
Package providers are bundled with the WMF installation.
You can easily add package providers (and remove) if you know the search locations (even your own custom package providers).
Find where your package-provider is installed:
$p = (Get-packageProvider -name Chocolatey);
$p.ProviderPath
If you remove / move the assembly to somewhere outside the providers default search path; it will be unavailable (NB: Restart your host too see the effects).
Similarly can you add package providers by copying a exe / dll that implements the specifications for a packageprovider to the search location.
More documentation can be found here (implementing your own and the default search locations):
https://github.com/OneGet/oneget/wiki/Provider-assembly-search-locations
https://github.com/OneGet/oneget/wiki/ImplementingPackageProvider
A simple example of how to remove NuGet provider
(Get-PackageProvider|where-object{$_.name -eq "nuget"}).ProviderPath|Remove-Item -force
Restart-Computer
To complement Harald F's helpful answer, given that the PackageManagement module as of version 1.4.7 still has no Uninstall-PackageProvider command (see all commands that come with the module with Get-Command -Module PackageManagement):
Note: To be able to undo this change later, make a note of the path reported by (Get-PackageProvider NuGet).ProviderPath and make a backup copy of that file.
Step-by-step instructions for removing the NuGet package provider, for example:
On Windows:
Copy the path of the NuGet package-provider assembly (DLL) to the clipboard:
(Get-PackageProvider NuGet).ProviderPath | Set-Clipboard
Start an elevated PowerShell session (run as admin - requires administrator credentials). To do that from an existing (non-elevated) session, run:
Start-Process -Verb RunAs (Get-Process -Id $PID).Path
Before continuing, close all other PowerShell sessions, which may include needing to exit Visual Studio Code.
Deleting the DLL will only succeed if no session has it currently loaded; if that isn't ensured, you'll get an Access denied error, even with elevation.
In the elevated session (in which you must not have submitted any PackageManagement commands), submit the following command to delete the NuGet package-provider assembly (DLL):
Remove-Item -Force <paste-the-previously-copied-path-here>
On macOS and Linux:
Start a PowerShell session with sudo. To do that from an existing (non-elevated) session, run:
sudo pwsh
Submit the following command to delete the NuGet package-provider assembly (DLL):
(Get-PackageProvider NuGet).ProviderPath | Remove-Item -Force
The remaining steps apply to all platforms:
Exit the elevated / sudo session.
Start a new (non-elevated) session for the change to take effect: Get-PackageProvider should then no longer list the NuGet provider.
If I understand what you want :
Uninstall-Package [-Id] [-RemoveDependencies] [-ProjectName ] [-Force] [-Version ] [-WhatIf]
Use the -Force option to forces a package to be uninstalled.