Shell scripting in controlled web environment - powershell

I'm working on some web-based tools for our IT department, and need to launch a PowerShell script to do remote maintenance queries prior to loaning out a laptop.
I have an existing web-based registry of the laptop inventory, when one is loaned out we get the borrower to log in before they leave to ensure their profile is on the machine. At that point, I would like to query the laptop using a PowerShell script collecting information from it which would feed to the database and show on the display page.
The page itself is restricted to IT personnel, and our environments are very homogeneous -- everyone is running Windows (7 or 10) and IE (10 or 11).
I can launch a PowerShell script server-side easily enough from the VB code.
[ CommandToRun is a textbox, PStext an output span ]
Dim app = New Process()
Dim psi As ProcessStartInfo = app.StartInfo
psi.FileName = "powershell.exe"
psi.Arguments = CommandToRun.Text
psi.RedirectStandardOutput = True
psi.RedirectStandardError = True
psi.UseShellExecute = False
PStext.InnerHtml &= "<br/>Launching app: " & psi.FileName & " " & psi.Arguments
app.Start()
app.WaitForExit(5000)
PStext.InnerHtml &= "<br/>OUTPUT<br/><pre>" & app.StandardOutput.ReadToEnd() & "</pre>"
PStext.InnerHtml &= "<br/>ERROR<br/><pre>" & app.StandardError.ReadToEnd() & "</pre>"
But it runs as the local system which does not have the authority to run remote PS commands. Lacking Administrator status, the server cannot execute anything on remote computers, therefore cannot query the laptop being loaned out.
I found a Javascript client-side solution that looks easy enough:
function tryLaunch(commandtoRun, commandParms) {
// Instantiate the Shell object and invoke its execute method.
var oShell = new ActiveXObject("Shell.Application");
// Invoke the execute method.
oShell.ShellExecute(commandtoRun, commandParms, "", "open", "1");
}
But it gets a Permission denied error launching the shell.
Other sources have had the same JavaScript with "WScript.Shell" as the ActiveXObject parameter -- that fails on the construction, whereas the "Shell.Application" version fails on the ShellExecute command.
I know what I'm doing is intentionally disallowed, as its usually a pretty dangerous idea to allow web servers to launch code on clients, and portability issues kill even the most well-meaning ideas -- but like I said, this is a very controlled and homogeneous environment.
So ... my questions:
Am I missing an obviously better way to do this?
Should I focus on trying to make PS scripts run on the client? ( If so, any clues?? )
Should I focus on running the script server-side with different
access rights? ( Can I shell as the web site user? I have their
"user" object from AD )
Am I ignoring some capability of IIS to install a PS script as an
application using an Administrator account? (Seems like this should exist, which means I'm probably looking right at it and not seeing it. )

Related

Task scheduler "Run whether user is logged on or not" issue to startup application

I have a .bat file that starts up a powershell script.
Within this powershell script, i startup PowerBI with a given database.
The powershell script waits till powerBI has been done starting up, and will then be exporting data to some datadump files.
Doing this manually works fine, and also when its on the task scheduler to run when user is logged on.
The moment i change this to "Run whether user is logged on or not" it doesnt work anymore.
The reason behind this, is that it seems that powershell is unable to start PowerBI and therefore there is no open data to query in the rest of the script.
So the positive side is it runs the bat and powershell just fine, only the powershell itself seems incapable to start powerBI.
Are there any solutions to this? should i for example use a different method to call the appliation to start?
currently the powershell snippit to start the app looks like this:
$PBIDesktop = "C:\Program Files\Microsoft Power BI Desktop\bin\PBIDesktop.exe"
$template = "C:\LiveData\Data.pbix"
$waitoPBD = 60
$app = START-PROCESS $PBIDesktop $template -PassThru
log_message "Waiting $($waitoPBD) seconds for PBI to launch"
Start-Sleep -s $waitoPBD
I faced similar issue. So, sharing my experience..
First of all, please verify couple of things.
Specify user account which will be used to invoke the job. Also, ensure that, the account have sufficient permission.
Don't forget to un-check the checkbox (as shown in screenshot) under Conditions Tab
Just found this one - sorry it took so long :D
But, i had this totally nervwrecking issue to.
Solution for me is to realize that the task scheduler is very deep part of the OS.
Thats why i have to grant access to the file, for the computername$ (system name) on the file or folder containing the file to run.
Rightclick on the file or folder -> Security. Select edit and add [Name of your computer]$ and give the read and execute permissions.
That's the only way I can make it run.
But i hope you found the solution in the meantime :)

How to run a PowerShell command in UWP?

How to run a PowerShell command in UWP?
using (PowerShell PowerShellInstance = PowerShell.Create())
{
PowerShellInstance.AddCommand(".\fpcalc.exe " + file.Path + " > out.txt");
}
Because UWP apps run in a sandbox, you cannot directly execute another processes or run PowerShell scripts. However, you can utilize Windows.ApplicationModel.FullTrustProcessLauncher if you declare such capability in your app to launch a full trust process (like a console application) which can safely execute such script.
You need to add a restricted fullTrust capability to your app's Package.appxmanifest.
You can learn more about full trust processes in UWP from documentation, UWP samples or blogposts like the excellent blogpost series from Stefan Wick.

Running a gui app with Powershell without displaying the gui

I want to run an app (it does not natively support command line mode) on Windows that require 5 fields of generic data from a user. However, I want to run this app without opening/displaying the gui (a la command line like). Is this something that can be done with Powershell. If so, can someone point me in the right direction. Thanks in advance
PowerShell does not change how an application is executed versus how it is when executed at the command line or a run dialog. If the application can accept input via arguments when run then any of these methods for executing the application will work.
If you are asking if powershell can read from the console host, the appropriate cmdlet would be read-host. So you could read from the user and then run the command with the arguments you desire.
$user = read-host "Username:"
& examplecommand.exe $user

Run Executable File Without UAC Popup as Administrator

I am running a large study where we have staff in various countries collecting information on tablet computers running Windows 10 Enterprise. Each staff member is assigned to a tablet and they log into the tablet with their standard username and password. These users do not have local admin rights on the machines, but all tablets have a single Administrator Username and Password which I know and these are uniform across the tablets.
Each night, users invoke a program on their tablets that uploads data to our servers and then we pass information back to the tablet during this synchronization process. Otherwise, they are disconnected from the internet. At the end of the synchronization process a program is executed that allows me to run any script I like, but the script executes under the standard user account (i.e. without elevated privileges).
I need to update all the tablets with a bug fix for software that they use on the tablets and I'd like to do this during the synchronization process. The bug fix is contained in a simple executable file that can be easily pushed to the staff memebers' tablets along with any code I like during the sync. If users were running the synchronization program as administrators, this wouldn't a problem as I could simply run the executable via a script at the end of the synchronization. But they aren't, so I'm trying to find a way that I could run a script (I don't really care what it is. It could be a windows batch file, a vbs script, VB.NET, powershell, etc.) and have that script execute with administrative privileges and run the installation without the UAC prompt interfering.
I don't even mind supplying the admin password in plaintext to be honest, since these users are all our employees and they can't really do anything really concerning to us with it (and I could always deploy a subsequent file through the synchronization process to delete the program that has the password in it). I realize this sounds somewhat complicated, but in a nutshell, I'd like to carry out these steps:
Send the bug update executable to the tablet computer (I can do this now)
Develop custom code, that will pass admin credentials to the tablet and install the executable in 1 without having the UAC appear (I can send the script to the tablet during sync but do not know how to execute it as the Admin without getting the UAC prompt).
Any ideas how I can do this? I've explored this all day with minimal success using PowerShell scripts like the ones described here and here. This was the closest I got after storing the credentials in $cred, but it continued to give me the UAC prompt:
Start-Process PowerShell.exe -Cred $cred -ArgumentList '-command &{Start-Process -FilePath C:\MySyncPath\BugFix32.exe -Verb runas}]
UPDATE
After some additional work, I think I'd be able to get this to run if I could somehow disable to UAC control with a script that can run under the regular user's account and pass the admin credentials to it. Any idea how I might be able to accomplish this? If I could get this to work, even with a reboot, I'd be able to accomplish what I need.
The actual issue you're having is that you want to update your application, but the application is in the Program Files folder (or some other location that standard users are not allowed to modify).
In order to allow any user the ability to update your program, you must grant all users Full Control to your folder. Ideally your application's installer would have done this adjustment to the DACL during installation (when the installer was running as an administrator).
For now you will have to settle for a final one-time requirement that the users elevate to administrator. Then you can disable all security on your application - allowing any user (malicious or not) to modify your application at will.
GrantEveryoneFullControlToFileOrFolder("C:\Program Files\Contoso");
with a pseudocode implementation of:
void GrantAllUsersFullControlToFileOrFolder(String path)
{
PACL oldDACL;
PACL newDACL;
PSECURITY_DESCRIPTOR sd;
//Get the current DALC (Discretionary Access Control List) and Security Descriptor
GetNamedSecurityInfo(path, SE_FILE_OBJECT, DACL_SECURITY_INFORMATION,
nil, nil, ref oldDACL, nil, ref sd);
//Create an SID for the "Users" group
PSID usersSid = StringToSid("S-1-5-32-545");
// Initialize an EXPLICIT_ACCESS structure for the new Access Control Entry (ACE)
EXPLICIT_ACCESS ea;
ZeroMemory(#ea, SizeOf(EXPLICIT_ACCESS));
ea.grfAccessPermissions = GENERIC_ALL;
ea.grfAccessMode = GENERIC_READ | GENERIC_WRITE | GENERIC_EXECUTE;
ea.grfInheritance = SUB_CONTAINERS_AND_OBJECTS_INHERIT;
ea.Trustee.TrusteeForm = TRUSTEE_IS_SID;
ea.Trustee.TrusteeType = TRUSTEE_IS_GROUP;
ea.Trustee.ptstrName = PChar(usersSID);
// Create a new ACL that merges the new ACE into the existing ACL.
// SetEntriesInAcl takes care of adding the ACE in the correct order in the list
SetEntriesInAcl(1, #ea, oldDACL, ref newDACL); //use LocalFree to free returned newDACL
//Attach the new ACL as the object's new DACL
SetNamedSecurityInfo(path, SE_FILE_OBJECT, DACL_SECURITY_INFORMATION,
nil, nil, newDACL, nil);
LocalFree(HLOCAL(sd));
LocalFree(HLOCAL(newDACL));
FreeSid(usersSID);
}
It's not completely unheard of for applications to be modifiable by any user: Most MMOs install updates while you play. MMOs usually have a shim applied by Microsoft that gives all users control of the application folder.
run the script as a domain admin account... and set execution policy before the script is run, then run as administrator... some applications are picky about UAC still, but Set-ExecutionPolicy [bypass/remotesigned] will ensure that you're not prompted. however, sharing and permissions may still be an issue if the domain admin account doesn't have access to the share. psexec does this, but it's literally a matter of doing what i just mentioned and the psexec file essentially writes out the permissions by the end of the script. the intent was to make sure that passwords weren't written in clear text, it hashes the password value. either way, if you want this done securely, using a GPO and making sure your file permissions/share is at the highest level might iterate out the prompt. that's why you'll see some batch files use %1 %2 %3 %4 %5 %6 %7 %8 %9 .... that's because it's automatically requesting elevation and will loop in an iterative cycle until the UAC prompt isn't necessary.
i know i'm bumping an old thread, but this is what i've found, trying to mix and match legacy cmd batches with powershell ... lots to consider about the execution policy leading into the call vs during the call...
This question is in the category of "when people ask for security holes as features".
You cannot bypass (or, if you prefer this phrasing, "programmatically accept") the UAC prompt and automatically elevate without interactive confirmation. UAC is designed specifically to prevent this. (If this were possible, all malware would do it.)
This isn't a PowerShell thing but a general windows 10 thing. You'd need to disable UAC for this. No experience with it on Windows 10 yet though.
You can try setting the EnableLUA registry key to 0. The key can be found in:
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System
This will probably need a reboot to be active though.

PS1 uninstallation script in SCCM

I'm a nub scripter and am trying to write a really simple script to taskkill 2 programs and then uninstall 1 of them.
I wrote it in Powershell and stuck it in SCCM for deployment...however every time I deploy it, it's not running the last line to uninstall the program.
Here's the code:
# Closing Outlook instance
#
taskkill /IM outlook.exe /F
#
# Closing Linkpoint instance
#
taskkill /IM LinkPointAssist.exe /F
#
# Uninstalling Linkpoint via uninstall string if in Program Files
#
MsiExec.exe /X {DECDCD14-DEF6-49ED-9440-CC5E562FDC41} /qn
#
# Uninstalling Linkpoint via WmiObject if installed manually in AppData
Get-WmiObject -class win32_product -Filter "Name like '%Linkpoint%'" | ForEach-Object { $_.Uninstall()}
#
Exit
Can someone help? SCCM says the script completes with no error and I know it's able to execute it since the taskkills work...but it's not uninstalling the program.
Thanks in advance for any input.
So, SCCM is running this script, and nothing in the script is going to throw an error.
If you want to throw an error which SCCM can return to know how the deployment went, you need to add an extra step.
$result = Get-WmiObject -class win32_product -Filter "Name like '%Linkpoint%'" | ForEach-Object { $_.Uninstall()}
if ($result.ReturnValue -ne 0){
[System.Environment]::Exit(1603)
}else
{
[System.Environment]::Exit(0)
}
I see a lot of these kinds of questions come through on SO and SF: Someone struggling with unexpected behavior of an application, script, or ConfigMgr and very little information about the assumptions I can make about their environment. At that stage, it would typically be days of interaction to narrow the problem to a point where a solution is possible.
I'm hoping this answer can serve as a reference for future such questions. The first question to OP should be "Which of these 9 principles are you violating?" You could think of it as a sort of Joel Test for ConfigMgr application packaging.
Nine Steps to Better ConfigMgr Application Packages
I have found that installing and uninstalling applications reliably using ConfigMgr requires carefully sticking to a bunch of principles. I learned these principles the hard way. If you're struggling to figure out why an application is not working right under ConfigMgr, odds are that you will answer "no" to one of the following questions.
1. Are you testing the entire lifecycle?
In order to have any hope of reliably managing an application you need to test the entire lifecycle of an application. This is the sequence I test:
Detect: make sure the detection script result is negative
Install: install the application using your installation script
Detect: make sure the detection script result is positive when run
Uninstall: uninstall using your uninstallation script
I run this sequence repeatedly making tweaks to each step until the whole sequence is working.
2. Are you testing independently of ConfigMgr first?
Using ConfigMgr to test your application's lifecycle is slow and has its own ways of failing that can mask problems with your application package. The goal, then, is to be able to test an application's installation, detection, and uninstallation separate from but equivalent to the ConfigMgr client. In order to achieve that goal you end up with three separate scripts for each application:
Install-Application.bat - the entry point for your installation script
Detect-Application.ps1 - the script that detects whether the application is install
Uninstall-Application.bat - the entry point for your uninstallation script
Each of these three scripts can be invoked directly by either you or the ConfigMgr client. For applications installed as system you need to use psexec -s to invoke scripts in the same context as ConfigMgr (caveat).
3. Are you aware of context?
Installers can behave rather differently depending on the context they are invoked in. You need to consider whether an application is installed for a user or the system. If it is installed for the system, when you test independently of ConfigMgr, use psexec -s to invoke your script.
4. Are you aware of user interaction?
An installer can also behave rather differently depending on whether a user can interact with it. To test a script as system with user interaction, use psexec -i -s.
5. Did you match ConfigMgr to the tested context and user interaction?
Once you have the full lifecycle working, make sure you select the correct corresponding options for context (installed for user vs. system) and interaction (user can interact with application, or not). If you don't do this, the ConfigMgr client will be installing the application different from the way you tested, so you really can't expect success.
6. Are you aware of the possibility of application detection context mismatch?
The context that detection scripts run in depends on whether the application is deployed to users or systems. This means that in some cases the installation and detection contexts won't matched. Keep this in mind when you write your detection scripts.
7. Have you structured your scripts so that exit codes work?
ConfigMgr needs to see exit codes from your installation and uninstallation scripts in order to do the right thing. Installers signal failure or the need to reboot using exit codes. In order for exit codes to get to the ConfigMgr client you need to ensure that your install and uninstall scripts are structured correctly.
for batch scripts, use exit /b %errorlevel% to pass the exit code of your executable out to the ConfigMgr client
for PowerShell scripts, this is the only way I have seen work reliably
8. Are you using PowerShell scripts for detection?
ConfigMgr has a nice user interface for checking things like the presence of files, registry keys, etc as a proxy for whether an application is installed. The problem with that scheme is that there is no way to test application detection separately from and equivalent to the ConfigMgr client. If you want to test the application lifecycle independent of the ConfigMgr client (trust me, you want that), all your detection must occur using PowerShell scripts.
9. Have you structured your PowerShell detection scripts correctly?
The rules ConfigMgr uses to interpret the output of a PowerShell detection script are arcane. Thankfully, they are documented.