Powershell script running terminals indefinitely - powershell

what I'm trying to do is setting up aliases to run Windows Terminal from file browser in the current directory, as admin or regular user.
I'm not very familiar with powershell scripts and I'm trying to understand why my functions are called when the script is loaded instead of when the alias is called, which in turn runs terminals indefinitely..
Here is the script I wrote in $Profile :
$wtHere = "wt -d ."
function Run-Command{
param($Command)
Invoke-Expression $Command
}
function Run-As-Administrator{
param($Command)
Run-Command -Command "runas /user:administrator $Command"
}
set-alias -Name wtha -Value $(Run-As-Administrator -Command $wtHere)
set-alias -Name wth -Value $(Run-Command -Command $wtHere)

Quoting this excellent answer which explains really well what you're doing wrong:
PowerShell aliases do not allow for arguments.
They can only refer to a command name, which can be the name of a cmdlet or a function, or the name / path of a script or executable
And from the official Docs:
The Set-Alias cmdlet creates or changes an alias for a cmdlet or a command, such as a function, script, file, or other executable. An alias is an alternate name that refers to a cmdlet or command.
This should help you understand what is going:
$wtHere = "Hello world"
function Run-Command {
param($Command)
"$Command - Received on {0}" -f $MyInvocation.MyCommand.Name
}
function Run-As-Admin {
param($Command)
Run-Command ("From {0} - Command: $Command" -f $MyInvocation.MyCommand.Name)
}
Set-Alias -Name wtha -Value $(Run-As-Admin -Command $wtHere)
Set-Alias -Name wth -Value $(Run-Command -Command $wtHere)
Get-Alias wth, wtha | Select-Object Name, Definition
You're storing the "result" of your functions and not the functions definitions into your aliases. You can't store an expression such as Run-As-Admin -Command $wtHere as the Value of your Alias, it will be stored as literal, even if you run the alias you would see that PowerShell will run the result of your expression and fail:
wth: The term 'Hello world - Received on Run-Command' is not recognized as a name of a cmdlet, function, script file, or executable program.
Check the spelling of the name, or if a path was included, verify that the path is correct and try again.
What you should do is as simple as:
Set-Alias -Name wtha -Value Run-As-Admin
Set-Alias -Name wth -Value Run-Command
Then you can interactively run:
PS /> wtha $wtHere

Related

How to decompress the tar file into specific folder using powershell

I have tried with the command
if (-not (Get-Command Expand-7Zip -ErrorAction Ignore)) {
>> Install-Package -Scope CurrentUser -Force 7Zip4PowerShell > $null
>> }
PS C:\Users\noufal p> Expand-7Zip C:\Amazon\abc123d-setup.tar C:\awsUsers\%USERNAME%\Downloads
But getting error => The path 'C:\Amazon\abc123d-setup.tar' either does not exist or is not a valid file system path.
I suspect the error message is wrong - i think its actually complaining about the output location? Couple of things jump out me:
c:\awsUsers\ - is this correct/does it exist?
%USERNAME% - probably should be $env:USERNAME (have you tried hard-coding a value and see if that works?)
Based on the above assumptions, you could try:
Expand-7Zip C:\Amazon\abc123d-setup.tar "C:\Users\$env:USERNAME\Downloads"
Or just cut out the middle-man and use 7z.exe directly:
start-process -FilePath "C:\Program Files\7-Zip\7z.exe" -ArgumentList #("x -ttar C:\path\to\a\archive.tar -oc:\path\to\output")

Why when I run invoke-expression 'cmd /c start pwsh -NoExit I get the below output?

invoke-expression 'cmd /c start pwsh -NoExit -Command { cd -path $env:homedrive$env:homepath/Documents/MySillyFolder;
$host.UI.RawUI.WindowTitle = "A Silly Little Title"; color -background "red";
}'
#< CLIXML
System.Management.Automation.ErrorRecordSystem.ObjectCannot find path 'C:\Users\Administrator\Documents\MySillyFolder' because it does not exist.System.Management.Automation.ItemNotFoundExceptionSystem.Management.Automation.SessionStateExceptionSystem.Management.Automation.RuntimeExceptionSystem.SystemExceptionSystem.ExceptionSystem.ObjectSystem.Management.Automation.ItemNotFoundException: Cannot find path 'C:\Users\Administrator\Documents\MySillyFolder' because it does not exist.x000D__x000A at System.Management.Automation.LocationGlobber.ExpandMshGlobPath(String path, Boolean allowNonexistingPaths, PSDriveInfo drive, ContainerCmdletProvider provider, CmdletProviderContext context)x000D__x000A at System.Management.Automation.LocationGlobber.ResolveDriveQualifiedPath(String path, CmdletProviderContext context, Boolean allowNonexistingPaths, CmdletProvider& providerInstance)x000D__x000A at System.Management.Automation.LocationGlobber.GetGlobbedMonadPathsFromMonadPath(String path, Boolean allowNonexistingPaths, CmdletProviderContext context, CmdletProvider& providerInstance)x000D__x000A at System.Management.Automation.SessionStateInternal.SetLocation(String path, CmdletProviderContext context, Boolean literalPath)x000D__x000A at System.Management.Automation.PathIntrinsics.SetLocation(String path, CmdletProviderContext context, Boolean literalPath)x000D__x000A at Microsoft.PowerShell.Commands.SetLocationCommand.ProcessRecord()Cannot find path 'C:\Users\Administrator\Documents\MySillyFolder' because it does not exist.C:\Users\Administrator\Documents\MySillyFolderDrivefalseSystem.Collections.ObjectModel.Collection`1[System.String] ExpandMshGlobPath(System.String, Boolean, System.Management.Automation.PSDriveInfo, System.Management.Automation.Provider.ContainerCmdletProvider, System.Management.Automation.CmdletProviderContext) at System.Management.Automation.LocationGlobber.ExpandMshGlobPath(String path, Boolean allowNonexistingPaths, PSDriveInfo drive, ContainerCmdletProvider provider, CmdletProviderContext context)x000D__x000A at System.Management.Automation.LocationGlobber.ResolveDriveQualifiedPath(String path, CmdletProviderContext context, Boolean allowNonexistingPaths, CmdletProvider& providerInstance)x000D__x000A at System.Management.Automation.LocationGlobber.GetGlobbedMonadPathsFromMonadPath(String path, Boolean allowNonexistingPaths, CmdletProviderContext context, CmdletProvider& providerInstance)x000D__x000A at System.Management.Automation.SessionStateInternal.SetLocation(String path, CmdletProviderContext context, Boolean literalPath)x000D__x000A at System.Management.Automation.PathIntrinsics.SetLocation(String path, CmdletProviderContext context, Boolean literalPath)x000D__x000A at Microsoft.PowerShell.Commands.SetLocationCommand.ProcessRecord()Cannot find path 'C:\Users\Administrator\Documents\MySillyFolder' because i
Why are you not following the Help file specifics?
https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_pwsh?view=powershell-7.1
pwsh -Command {Get-WinEvent -LogName security}
In cmd.exe, there is no such thing as a script block (or ScriptBlock
type), so the value passed to Command will always be a string. You can
write a script block inside the string, but instead of being executed
it will behave exactly as though you typed it at a typical PowerShell
prompt, printing the contents of the script block back out to you.
A string passed to Command is still executed as PowerShell code, so
the script block curly braces are often not required in the first
place when running from cmd.exe. To execute an inline script block
defined inside a string, the call operator & can be used:
pwsh -Command "& {Get-WinEvent -LogName security}"
If the value of Command is a string, Command must be the last
parameter for pwsh, because all arguments following it are interpreted
as part of the command to execute.
When called from within an existing PowerShell session, the results
are returned to the parent shell as deserialized XML objects, not live
objects. For other shells, the results are returned as strings.
If the value of Command is -, the command text is read from standard
input. You must redirect standard input when using the Command
parameter with standard input. For example:
Firstly this entire line has issues
invoke-expression 'cmd /c start pwsh -NoExit -Command {cd -path $env:homedrive$env:homepath/Documents/MySillyFolder;$host.UI.RawUI.WindowTitle = "A Silly Little Title"; color -background "red";}'
1 - Invoke-Expression
Invoke-Expression considered harmful | PowerShell Team: https://devblogs.microsoft.com/powershell/invoke-expression-considered-harmful
InfoSec Handlers Diary Blog: https://isc.sans.edu/diary/Did+You+Spot+%22Invoke-Expression%22%3F/26762
So, in a PowerShell session, use the call operator & or Invoke-Command or Start-Process to run executables. See details here:
• PowerShell: Running Executables: https://social.technet.microsoft.com/wiki/contents/articles/7703.powershell-running-executables.aspx
2 - this is not valid, and Windows does not use a forward slash for file system paths.
cd -path $env:homedrive$env:homepath/Documents/MySillyFolder
# Results
<#
$env:homedrive$env:homepath
At line:1 char:15
+ $env:homedrive$env:homepath
+ ~~~~~~~~~~~~~
Unexpected token '$env:homepath' in expression or statement.
#>
these are two separate things.
$env:homedrive
# Results
<#
C:
#>
$env:homepath
# Results
<#
\Users\WDAGUtilityAccount
#>
You get the path you are after using the right $env variable
$env:USERPROFILE
# Results
<#
C:\Users\WDAGUtilityAccount
#>
So this...
cmd /c start pwsh -NoExit -Command {cd -path $env:homedrive$env:homepath/Documents/MySillyFolder
...becomes this...
cmd /c start pwsh -NoExit -Command {cd -path $env:USERPROFILE\Documents\MySillyFolder
3 - This is not valid at all
color -background "red"
4 - Why are you in Powershell, starting cmd.exe to start Powershell? That is redundant. In a PS instance to start cmd.exe to start another PS Instance.
cmd /c "start pwsh"
3- if this is in the shell, then the semi-colon concatenation is fine, but pointless in a script, also, since you are using variables, they must be expanded using double quotes, not single. Simple strings use single quotes.
So, in the cmd.exe shell, this
pwsh -NoExit -NoProfile -NoLogo -Command "& {cd -path "$env:USERPROFILE\Documents\MySillyFolder";$host.UI.RawUI.WindowTitle = 'A Silly Little Title';$host.UI.RawUI.ForegroundColor = 'Red'}"
More specifically, always check if the target exists before trying to do anything to/with it. So, this:
pwsh -NoExit -NoProfile -NoLogo -Command "& {If(Test-Path -Path "$env:USERPROFILE\Documents\MySillyFolder"){cd -path "$env:USERPROFILE\Documents\MySillyFolder";$host.UI.RawUI.WindowTitle = 'A Silly Little Title';$host.UI.RawUI.ForegroundColor = 'Red';'Hello World'}Else{Write-Warning -Message 'Path not found'}}"
As a script:
# Save this as Start-Script.ps1
If(Test-Path -Path "$env:USERPROFILE\Documents\MySillyFolder")
{
Set-Location -path "$env:USERPROFILE\Documents\MySillyFolder"
$host.UI.RawUI.WindowTitle = 'A Silly Little Title'
$host.UI.RawUI.ForegroundColor = 'Red'
'Hello World'
}
Else{Write-Warning -Message 'Path not found'}
pwsh -NoExit -NoProfile -NoLogo C:\Scripts\Start-Script.ps1
Udpdate
As for this comment:
What I was really trying for is to get a new script to open up in a
new window and be able to pass those variables with a job. But I am
finding this to be very difficult. It seems powershell does not like
to pass a local variable in a job.
This is really a straightforward thing via PowerShell. Yet as with using local variables in the PSRemote session, variables have scope per session. AS detailed here:
https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_scopes?view=powershell-7.1
As also noted on SO, in these Q&A's
# Powershell pass variable to start-job
# Use the -ArgumentList parameter on Start-Job e.g.:
Start-Job -Scriptblock {param($p) "`$p is $p"} -Arg 'Server1'
or
$Servername = 'Server1'
Start-Job { "Target: " + $using:ServerName } |
Receive-Job -Wait -AutoRemoveJob
Or this one:
Start-Job -ScriptBlock {& $using:command}

Unable to set alias in PowerShell using a string

I tried to set an alias in PowerShell by running Set-Alias -Name artisan -Value 'php aritsan', though the command ran successfully but when I call the alias the following error occurs :
artisan : The term 'php aritsan' 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.
At line:1 char:1
+ artisan
+ ~~~~~~~
+ CategoryInfo : ObjectNotFound: (php aritsan:String) [], CommandNotFoundException
+ FullyQualifiedErrorId : CommandNotFoundException
What's the correct way to go for?
P.S: artisan is a file in the current directory. The file is packaged with laravel framework
This will help you create an alias for "php artisan" in Windows PowerShell Globally.
Just follow these steps and its done.
First of all search powershell using win+S and run it as administrator and then paste this code and hit Enter.
if (!(Test-Path -Path $PROFILE )) {New-Item -Type File -Path $PROFILE -Force }
Then run this command.
notepad $profile
A notepad file will open. Paste this code in notepad file and hit ctrl+S
Function CD32($arg1,$arg2,$arg3,$arg4,$arg5) {php artisan $arg1 $arg2 $arg3 $arg4 $arg5}
Set-Alias -Name pa -Value CD32
And then finally paste this command on your powershell and hit enter.
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned
And it's done. Now you can use "pa" as an alias for "php artisan" globally in your windows powershell.
Here I am using "pa" on my VS Code PowerShell.
All the details are described in:
Get-Help alias -ShowWindow shows all available help for alias
Get-Help Set-Alias show specific help for Set-Alias
An alias is an alternate name or nickname for a cmdlet or for a command
element, such as a function, script, file, or executable file. You
can use the alias instead of the command name in any Windows PowerShell
commands.
You can assign an alias to a cmdlet, script, function, or executable file.
However, you cannot assign an alias to a command and its parameters.
For example, you can assign an alias to the Get-Eventlog cmdlet, but you
cannot assign an alias to the "Get-Eventlog -LogName System" command.
However, you can create a function that includes the command. To create a
function, type the word "function" followed by a name for the function.
Type the command, and enclose it in braces ({}).
For example, the following command creates the syslog function. This
function represents the "Get-Eventlog -LogName System" command:
function syslog {Get-Eventlog -LogName System}
You can now type "syslog" instead of the command. And, you can create
aliases for the syslog function.
For more information about functions, type:
Get-Help about_Functions

Running Powershell from CMD Causing Command Line to Not Parse Correctly

I am trying to run the following PowerShell code via a CMD shell:
$logfile = "x:\test.log"
try {
get-service
Add-Content - Path $logfile -Value "It Worked"
} catch {
Add-Content -Path $logfile -Value $_.Exception.Message
}
From a CMD script I am calling the script as follows:
Powershell.exe -executionpolicy bypass -command "I paste the code above
here"
I have also tried it as follows:
Powershell.exe -executionpolicy bypass -command "& 'Command From Above'"
You can see from the error, it doesn't seem to be trying to run the entire command, because it seems to be trying to run my log command:
!https://imgur.com/y62FzB2
If I run something simple, it works without issue. As follows:
Powershell.exe -executionpolicy bypass -command "get-service"
As for what you tried:
When calling from cmd.exe, PowerShell code you pass to Windows PowerShell's CLI, powershell.exe (pwsh.exe for PowerShell Core) must be single-line:
Your screenshot suggests that you indeed used a single line, but you're missing ; chars. between your statements; these statement separators are needed whenever you want to place multiple statements on a single line.
Your next problem is related to passing the code as individual arguments to -command, to be pieced together by PowerShell before execution (as opposed to trying to pass a single argument with outer double-quoting).
In passing arguments individually, any enclosing "..." are stripped on parsing by PowerShell, so that "x:\test.log" turns into x:\test.log - no quotes - which caused the error you saw.
A solution:
By:
properly separating your statements with ;
escaping the " chars. in your command as \" (sic)
you can get your command to work:
powershell -c $logfile = \"x:\test.log\"; try { get-service; Add-Content -Path $logfile -Value \"It Worked\" } catch { Add-Content -Path $logfile -Value $_.Exception.Message }
Generally, however, note that if your code happens to contain characters (also) reserved in cmd.exe - notably & | < > % ^ - you would have to individually ^-escape them.
It's challenging to get quoting right on the command line, and ultimately the only robust solution - short of avoiding the problem by creating a (temporary) script file - is to use PowerShell's -EncodedCommand CLI parameter, which takes a Base64-encoded representation of the PowerShell code using the UTF-16LE character encoding.
Unfortunately, such a representation is not easy to come by in batch files.
I have always had trouble calling multi-line PowerShell scripts through cmd.
I have seen people convert their script into base64 and run that string from PowerShell but I can't remember how.
The easiest way would be to save your script as a .PS1 and run PowerShell -NoProfile -ExecutionPolicy Bypass -file "C:\ps.ps1"
Otherwise you could ECHO each line out to a .ps1 file and then run it. Would work as a .bat file.
#echo off
set WD=%~dp0
ECHO $logfile = "x:\test.log" >> "%WD%Script.ps1"
ECHO try { >> "%WD%Script.ps1"
ECHO get-service; >> "%WD%Script.ps1"
ECHO Add-Content - Path $logfile -Value "It Worked" >> "%WD%Script.ps1"
ECHO } catch { >> "%WD%Script.ps1"
ECHO Add-Content -Path $logfile -Value $_.Exception.Message >> "%WD%Script.ps1"
ECHO } >> "%WD%Script.ps1"
powershell.exe -ExecutionPolicy Bypass -File "%WD%Script.ps1"
del "%WD%Script.ps1"

Unable to run the parametrized batch file on remote machine using PowerShell

Execute the remote server parametrized batch file from PowerShell.
Doesn't throw an error nor executed command on remote machine.
$path = "D:\run\test-5.2.bat";
Invoke-Command -ComputerName testserver -Scriptblock { "$path" }
Script inside the bat file is msiexec with parameters, which shall execute through Command Prompt only.
Based on this msdn link, you can run a ps1 script file on remote computers. So if it is possible to "port" the content of the bat file in a ps1 it should work. Here is the msdn example:
Example 11: Run a script on all the computers listed in a text file
PS C:\> Invoke-Command -ComputerName (Get-Content Servers.txt) -FilePath C:\Scripts\Sample.ps1 -ArgumentList Process, Service
This example uses the Invoke-Command cmdlet to run the Sample.ps1 script on all of the computers listed in the Servers.txt file. The command uses the FilePath parameter to specify the script file. This command lets you run the script on the remote computers, even if the script file is not accessible to the remote computers.
When you submit the command, the content of the Sample.ps1 file is copied into a script block and the script block is run on each of the remote computers. This procedure is equivalent to using the ScriptBlock parameter to submit the contents of the script.
Hope that helps
$path is a string. PowerShell simply echoes bare strings instead of executing them, unlike CMD or bash. Use the call operator (&):
& "$path"
or Start-Process:
Start-Process cmd.exe -ArgumentList '/c', $path -NoNewWindow -Wait
to have PowerShell execute a string as a command. Since you say you're running msiexec.exe from the batch script using the latter may be required.
On top of that you have a scope issue. The variable $path inside the scriptblock is not the same as the one in the global scope. You can mitigate that via the using: scope qualifier:
Invoke-Command -Computer testserver -Scriptblock { & "$using:path" }
or by passing $path as an argument to the scriptblock:
Invoke-Command -Computer testserver -Scriptblock { & "$($args[0])" } -ArgumentList $path