Can a Batch script know if it's called from PowerShell? - powershell

Is there any way a batch script can know if it's called from PowerShell (without extra parameters feeded)?
Need something like..
if (%THIS_BATCH_CALLED_FROM_POWERSHELL%)
... warn the user or drop back to powershell to execute the proper instructions...
Question related with this question - virtualenv-in-powershell.

You could use a tool like "tlist.exe /t" or this one to display the PIDs of the current process and all parent processes. You could check each one of those PIDs to see if any correspond to PowerShell.exe.

You could add a default warning in the script and pass it a flag that tells it not to show the warning. When you call it from power shell pass it that flag.

In my Powershell environment (a PS 2.0 CTP), I seem to have an environment variable PSMODULEPATH which is not set by the normal command line environment, but still exists when Powershell has a child CMD.exe shell.
I think you might be able to "reliably enough" check for the existence of PSMODULEPATH in your batch script.

Related

Powershell command to run for everyone

I am running into a problem with a PowerShell script. I want to add a Microsoft store app with a PowerShell command:
Add-Appxpackage -path C:\Temp\whiteboard.appx
The command is working fine, but only for 1 user not for everyone. It must be installed for everyone. How can that be done?
As #WafflesAndCustard already mentioned, the issue for executing the script is the ExecutionPolicy.
https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_execution_policies?view=powershell-7
The PowerShell-Script Execution takes another road as you probably might think of ... PS-User-Policy takes precedence over PS-Computer-Policy, but Local- (gpedit) and Domain-Policies (gpmc) overrides anything and takes the final precedence (Domain policies have highest priority)
By default -> no PowerShell Script execution is allowed.
When you want to use your script for all users, without predefining the PS-executionpolicy, you should use the native command like
powershell.exe -ExecutionPolicy ByPass -Script ....
BUT, and that's really IMPORTANT when it comes to security:
Please don't use the temp folder as this is usual something everybody can write to!
E.g. a user/program can place a malicious executable file into that folder with same name and your task (or whatever) will execute it with high privileges.

Powershell environment variable

In a Powershell script (.ps1)
Launched from the command line of a console (cmd.exe)
How can set and environment variable in the console,
so that when the Powershell script ends processing,
and exits to the console where was invoked...
the environment variable exists,
and can be read by a batch file,
or viewed with the SET command ?
do not want to set a 'Machine' or a 'User' variable...
only a console process variable...
the same variable you get if you use SET in the console
To run a PowerShell script from cmd.exe invariably requires a (powershell.exe / pwsh.exe) child process, and child processes fundamentally cannot set environment variables for their parent process[1].
Your best bet is to have your *.ps1 file output the name and value of the desired environment variable and then have the calling cmd.exe process create it, based on that output.
Security note: Blindly defining environment variables based on the name-value pairs output by another command (a *.ps1 script, in your case) should only be done if you trust that command not to output malicious definitions.
Here's a simple example (run directly from an interactive cmd.exe session):
for /f "delims== tokens=1,*" %v in ('powershell.exe -c "'FOO=bar'"') do #set "%v=%w"
The above defines environment variable %FOO% with value bar, based on the PowerShell command outputting the literal name-value pair FOO=bar.
Verify with echo %FOO%.
To extend this approach to defining multiple environment variables, make the command output each definition on its own line (which in PowerShell you can achieve by outputting an array of strings):
for /f "delims== tokens=1,*" %v in ('powershell.exe -c "'FOO=bar', 'BAZ=bam'"') do #set "%v=%w"
The above additionally defines %BAZ% with value bam.
To make this more convenient, I suggest creating a wrapper batch file (*.cmd) that performs the above:
Note that you'll have to use %%v and %%w instead of %v and %w there.
Instead of -c (for -Command) with the demo command, use -File with the path to your *.ps1 file to invoke it.
Also consider use of -NoProfile as well, to bypass loading of your PowerShell environment's $PROFILE file, which not only slows things down, but may pollute your command's output.
[1] As LotPings points out, child processes inherit copies of the parent process' environment variables. Modifications of these copies are never seen by the parent. A child process is fundamentally unable to modify its parent's environment, which is a restriction at the OS level - for good reasons: Modifying a running process' environment by an arbitrary (child) process would be a serious security concern.

Setting a Environment Variables via Powershell for another User

I am trying to set the environment variables of a user XYZ from the powershell of admin user ABC. I am using Start-Process to launch the powershell of user XYZ but i am not able to capture the output. All this process needs to be done in Java.
Can someone help me out.
Thanks
Ajax
When you change environment variables, the change affects only the current PowerShell session (like if you were using SET command in a Windows cmd). To make the changes permanent, you have to change them with a utility like SETX. You must also have permission to change the values.
Check this TechNet article on it: https://technet.microsoft.com/en-us/library/ff730964.aspx
Basically, you're going to want to set it using the .NET method at the machine scope:
[Environment]::SetEnvironmentVariable("TestVariable","Test Value","Machine")
You'll need to restart your Powershell session to be able to access the new environment variable after creating it.

clear powershell session

Is there a commandlet that clears the current powershell session variables that I have added?
I am using the Add-Type commandlet, and I am getting the error "Cannot add type. The type name already exists."
A possible "work around": Open a powershell window and then to run your script enter powershell .\yourScriptHere.ps1
This launches a new powershell instance which exits when your script exits. If you want to "play" in the new instance then change the invocation to powershell -NoExit .\yourScriptHere.ps1 and the new instance will not exit when the script completes. Enter exit when you need another restart and hit the "up arrow" key to get the previous command. All script output will appear in the same window. The overhead for starting a new powershell instance is low -- appears to be less than 1 second on my laptop.
Unfortunately you can't unload .NET assemblies that have been loaded into the default AppDomain which is what Add-Type does. You can rename types or namespaces to limp along but at some point you just have to exit and restart PowerShell.
This is not a PowerShell limitation so much as it is a .NET/CLR limitation. You can load .NET assemblies into separate AppDomains which can be unloaded later but you would have to code that yourself and it imposes restrictions on the types you plan to use in the separate AppDomain. That is, those types need to work through .NET Remoting so they either have to derive from MarshByRefObject or they have to be serializable (and this applies to all the objects referenced by their properties, and so on down the object graph).

In vbscript, how do I run a batch file or command, with the environment of the current cmd prompt window?

In vbscript, how do I run a batch file or command, in the current cmd prompt window,
without starting a new process.
For example. According to script56.chm (the vbscript help apparently)
Windows Script Host
Run Method (Windows Script Host)
"Runs a program in a new process"
So if I have code that uses that e.g. a VBS file, and a BAT file.
An environment variable g has the value abc g=abc
from that command window,
The VBS file calls the BAT file with windows scripting host Run.
The bat process sets g=z. and finishes.. and the vbs process finishes.
The environment variable is left untouched as g=abc.
I know
CreateObject("Wscript.Shell").Run "c:\test.bat", 0
starts a new window as is clear when using 1 instead of 0. (since 0 hides the window)
How do I
-run the bat file from the vbs, in the same cmd environment that the vbs was called in, so changes affect the cmd environment it was called in?
-In the two windows case which this one is at the moment, how do I access the environment of the parent cmd window, from the batch file?
how do I run a batch file or command, in the current cmd prompt window, without starting a new process?
I don't think you can; your vbscript runs under a script host engine (such as cscript.exe or wscript.exe), and batch files are interpreted by the command interpreter (typically cmd.exe). Both are separate executables and neither is, to my knowledge, available as an in-process library, so you cannot interpret .vbs and .cmd files within the same process. I also highly doubt that the script host engine that is running your VBScript also could run the batch file in its parent cmd.exe - I don't think you can 'inject' a new batch file into a running cmd.exe.
how do I access the environment of the parent cmd window, from the batch file?
Not just access, but change - MSDN's "Changing Environment Variables" is quite explicit on this: "Altering the environment variables of a child process during process creation is the only way one process can directly change the environment variables of another process. A process can never directly change the environment variables of another process that is not a child of that process." You are trying to change the environment of the parent, not child, process. (I do wonder what 'directly' means in the context of this quote, though).
I would guess that the reason for this is security; imagine the havoc that could be wreaked if arbitrary processes could (maliciously or accidentally) change the PATH (or COMSPEC) environment variable of a running process, such as your vbscript host engine process - it could fail to launch your bat file entirely, breaking your program.
It would seem that you're out of luck - however, there are lots of other mechanisms for passing information between processes. Here are a couple of suggestions that are fairly simple to implement when talking between a batch file & vbscript, although it's by no means exhaustive:
Exit codes
Writing to & Parsing the consoleoutput (stdout) or a temp file
If you absolutely need to set environment variables in the parent cmd.exe (and also absolutely need the intermediate step of a vbscript), then you may have to write a wrapper batch file which runs the vbscript, consumes information produced by it and then sets environment variables; because the wrapper cmd is executing in the top-level cmd process, it will be able to change the env vars there.
Footnote: Note that you can change the permanent system/user environment variables (as opposed to process environment variables) from within a VBScript, but I wouldn't recommend this if you are trying to create a transient state; besides this won't affect already-running processes (like the parent cmd.exe) anyway.