How do I get a windows service to update itself - c#

I'm working on an application that will run as a windows service, and I'm trying to get it to update itself automatically.
My current approach is to to execute a powershell script, which will stop the service, run a msi installer, and then restart the service.
This is what the powershell script looks like at this time
Start-Sleep -s 10
Write-Host "update start"
Stop-Service ServiceName1
msiexec /i c:\ProgramData\ProgramName\Install\ServiceName.Setup.msi /passive /l*v C:\ProgramData\ProgramName\Install\log.txt | Out-Null
Start-Service ServiceName1
Write-Host "update finished"
This is how I'm running it from the app
Process.Start("Powershell", #"C:\ProgramData\ProgramName\Install\UpdateApp.ps1");
What happens, is the service stops and restarts, but it doesn't update. It's as though the msi never gets run. The log file doesn't even appear.
When I run the Service as a command line app from an elevated command prompt it works as expected and the app gets updated, so My current theory is that the service isn't running the powershell script with administrator privileges.
Other questions suggest that I set up the log on settings for the service to use an administrator account, so I set the service to run as the account that I was currently logged in under, who was able to open an elevated command prompt and/or manually run the installer, but doing that didn't change anything.
Is there any way to do what I'm trying to do?
I'm currently not committed to any particular automatic update strategy, but I do know that I want this service to update itself. So if I'm doing something completely wrong, I'm 100% open to attempting a different approach.
UPDATE:
I made the following change to log the error and output for msiexecc
Try{
c:\windows\system32\msiexec.exe /i c:\ProgramData\ProgramName\Install\ServiceName.msi /passive /l*v C:\ProgramData\ProgramName\Install\log.txt | Out-File -filepath C:\ProgramData\ProgramName\Install\output.txt
}
Catch {
$_ | Out-File C:\ProgramData\ProgramName\Install\errors.txt -Append
}
After running that script, I found the following error:
The term 'msiexec' is not recognized as the name of a cmdlet, function, script file, or operable program..

It looks like the call to msiexec isn't actually targeting c:\windows\system32\msiexec.exe
As per this question it seems that Powershell does not use the standard PATH environment variable, but has its own scheme, which perhaps doesn't work as expected in the context of a system service.
The simplest resolution, as you say, is to specify the full path, which is probably c:\windows\system32\msiexec.exe.
However, in production, it would probably be wise to avoid the use of a hardcoded path, since you might run into problems with localization, operating system changes, and so on. You could perhaps use SearchPath or a .NET equivalent from your service and either write out the Powershell script in real time or pass the path to msiexec as a command-line option, or there may be a sensible Powershell solution.

Related

System.Diagnostics.Process closing powershell immediately

I have an issue where I'm trying to deploy a driver installation via an MSI Installer, it contains the .CAT and .INF files and outputs them to a directory, typically from here an infrastructure engineer could right click the .INF file and press install, however we're trying to streamline this process and automate this step.
Via C# I have a class utilising the System.Diagnostics.Process namespace to spawn a powershell process to run a powershell script containing a simple command as follows:
var process = new System.Diagnostics.Process();
var newProcessInfo = new System.Diagnostics.ProcessStartInfo();
string powerShellScript = #"C:\PowershellScript\DriverInstall.ps1"
newProcessInfo.FileName = #"C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe"
newProcessInfo.Verb = "runas";
string newArgs = "-File " + powerShellScript;
newProcessInfo.Arguments = newArgs;
process.StartInfo = newProcessInfo;
process.Start();
Powershell script command is as follows:
Get-ChildItem "C:\DriverLocation" -Recurse -Filter "*inf" | ForEach-Object {PNPUtil.exe /add-driver $_.FullName /install }
when running this from powershell it works as expected, however if I try and spawn a powershell process from a C# class it doesn't work, comes up with some red error message text but the process windows spawns and closes immediately so I can't identify what the error is.
Essentially I either need the output from the powershell process to a text file or I need the powershell window to persist so I can identify why the script won't work when running from the System.Diagnostics.Process namespace.
I have tried editing the registries so that powershell will remain open to no avail.
Please don't suggest using the System.Management.Automation.Powershell namespace because utilisation of .NET core is unavailable in our project scenario.
Thanks for taking the time to read this, any help is much appreciated.
Resolution was to specify the execution policy within the powershell script, when running from the powershell window it had local admin rights and therefore didn't need an execution policy specified, however because it was being spawned from a separate process it only had remote access rights, therefore it wouldn't allow the script to execute.. never managed to output the particular error message but by process of elimination identified the resolution.
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope LocalMachine

How to run PowerShell script using System.Diagnostics.Process.Start in c#?

I'm writing this:
using System.Diagnostics;
Process.Start("C:\\CodeProjects\\C#\\WindowsPowerShell\\v1.0\\powershell_ise.exe", "-File .\\mp4_to_flac.ps1");
All this does is open up the script in Windows PowerShell ISE. But I also want it to RUN! So what other arguments do I have to pass in so that it executes?
Process.Start method Reference
You don't want to run powershell_ise.exe but powershell.exe. From a dos command prompt you can just prefix your command or script with #powershell, but for a process you're going to want to use something like
C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe
(that's mine on Win 8.1), yours should be somewhere near there if you're on a different version.
The chocolatey guys throw in these switches
-NoProfile -ExecutionPolicy unrestricted
when executing powershell from the command prompt, you might have to do that also depending on how your execution policy is set.
You could call it like so:
Process.Start(".\\mp4_to_flac.ps1");
and make sure all your windows settings are correct, so a double click on the file will execute it.

Runas admin (from batch script/cmd) not working properly

I'm trying to get a batch script to run a silent install of my program. Here's the line that's causing trouble:
runas /user:domain\admin /savecred start "" "%temp%\MyProgram - 4.6.0.0\Setup.exe" /silent >> %userprofile%\Desktop\BatchLog.txt
A few notes:
"" before the file location is there to avoid issues with spaces in the location of Setup.exe
/silent is a parameter passed into Setup.exe to run a silent installation
>> %userprofile%\Desktop\BatchLog.txt pipes the output to a log file
When run as part of a batch script, Setup.exe isn't running as domain\admin. For the SharePoint savvy, SPFarm.Local is throwing a null ref (it's written in C#), indicating that the running user doesn't have DB access. Can you spot anything wrong with my use of runas here?
Running that line from the command line just pulls up the runas help screen. I'd like to find out why that's happening as well.
If I just manually run (double-click) Setup.exe (logged in as domain\admin) I don't get that null ref, indicating that my program is running properly as domain\admin.
How can I fix this line to execute my program as domain\admin?
Think that the whole command needs to be in quotes as runas only takes 1 "program" parameter. Also, the start command doesn't seem compatible with runas. Try this:
runas /user:domain\admin /savecred "\"%temp%\MyProgram - 4.6.0.0\Setup.exe\" /silent >> \"%userprofile%\Desktop\BatchLog.txt\""

.ps1 cannot be loaded because the execution of scripts is disabled on this system

I run this code to execute PowerShell code from an ASP.NET application:
System.Management.Automation.Runspaces.Runspace runspace = System.Management.Automation.Runspaces.RunspaceFactory.CreateRunspace();
runspace.Open();
System.Management.Automation.Runspaces.Pipeline pipeline = runspace.CreatePipeline();
pipeline.Commands.AddScript(#"\\servername\path");
pipeline.Commands.Add("Out-String");
Collection<PSObject> results = pipeline.Invoke();
runspace.Close();
But I am getting an error:
.ps1 cannot be loaded because the execution of scripts is disabled on
this system. Please see "get-help about_signing" for more details.
The same code runs fine from a command prompt or a windows (Windows Forms) application.
Your script is blocked from executing due to the execution policy.
You need to run PowerShell as administrator and set it on the client PC to Unrestricted. You can do that by calling Invoke with:
Set-ExecutionPolicy Unrestricted
There are certain scenarios in which you can follow the steps suggested in the other answers, verify that Execution Policy is set correctly, and still have your scripts fail. If this happens to you, you are probably on a 64-bit machine with both 32-bit and 64-bit versions of PowerShell, and the failure is happening on the version that doesn't have Execution Policy set. The setting does not apply to both versions, so you have to explicitly set it twice.
Look in your Windows directory for System32 and SysWOW64.
Repeat these steps for each directory:
Navigate to WindowsPowerShell\v1.0 and launch powershell.exe
Check the current setting for ExecutionPolicy:
Get-ExecutionPolicy -List
Set the ExecutionPolicy for the level and scope you want, for example:
Set-ExecutionPolicy -Scope LocalMachine Unrestricted
Note that you may need to run PowerShell as administrator depending on the scope you are trying to set the policy for.
The problem is that the execution policy is set on a per user basis. You'll need to run the following command in your application every time you run it to enable it to work:
Set-ExecutionPolicy -Scope Process -ExecutionPolicy RemoteSigned
There probably is a way to set this for the ASP.NET user as well, but this way means that you're not opening up your whole system, just your application.
(Source)
You need to run Set-ExecutionPolicy:
Set-ExecutionPolicy Unrestricted <-- Will allow unsigned PowerShell scripts to run.
Set-ExecutionPolicy Restricted <-- Will not allow unsigned PowerShell scripts to run.
Set-ExecutionPolicy RemoteSigned <-- Will allow only remotely signed PowerShell scripts to run.
I had a similar issue and noted that the default cmd on Windows Server 2012 was running the x64 one.
For Windows 7, Windows 8, Windows Server 2008 R2 or Windows Server 2012, run the following commands as Administrator:
x86
Open C:\Windows\SysWOW64\cmd.exe
Run the command: powershell Set-ExecutionPolicy RemoteSigned
x64
Open C:\Windows\system32\cmd.exe
Run the command powershell Set-ExecutionPolicy RemoteSigned
You can check mode using
In CMD: echo %PROCESSOR_ARCHITECTURE%
In Powershell: [Environment]::Is64BitProcess
I hope this helps you.
Try This:
Set-ExecutionPolicy -scope CurrentUser Unrestricted
If you faced this error while running json-server and landed here on this page.
one alternate solution is to run it using npx directly without installing it.
npx json-server --watch db.json

silent uninstall msi package command WITHOUT administrative rights

I am trying to uninstall a program from my visual studio project but that seems to requiere me to run vs as an admin....so i tried doing this from the cmd to debug it .
I have managed to uninstall a msi setup project installation with this command from cmd :
msiexec /x {3A40307D-6DF2-4412-842F-B1D848043367} /quiet , but that only works when i start cmd as an admin, without admin rights it wont uninstall. What am i doing wrong and is there another approach to get the result I want?
I want to be able to silent uninstall an application without having to ask the user to login as an admin.
Edit:
This is the result from the log :
Error 1001. Error 1001. Unable to delete file C:\ProgramData\XXX.InstallState.
DEBUG: Error 2769: Custom Action _F6174138_B428_4AB6_9FEF_C4DD7A69BDC0.uninstall did not close 1 MSIHANDLEs.
The installer has encountered an unexpected error installing this package. This may indicate a problem with this package. The error code is 2769. The arguments are: _F6174138_B428_4AB6_9FEF_C4DD7A69BDC0.uninstall, 1,
CustomAction _F6174138_B428_4AB6_9FEF_C4DD7A69BDC0.uninstall returned actual error code 1603 (note this may not be 100% accurate if translation happened inside sandbox)
Action ended 17:54:40: InstallExecute. Return value 3.
Action ended 17:54:40: INSTALL. Return value 3.
MSI (s) (F0:3C) [17:54:40:355]: Product: XXX -- Removal failed.
That error from the log file indicates that a custom action is crashing. You'll want to investigate the root cause of that issue. My guess is that the custom action requires elevation (admin privileges) to work correctly but is not marked deferred (i.e. runs during the part when the MSI is elevated).
If you launch the uninstall of the MSI from Add/Remove Programs (Programs and Features) then you should not be prompted for elevated credentials. Thus the root issue probably is this custom action.
It appears you have several options here. All of them require creating an msi that doesn't require Admin privileges from the start. If the msi requires them from the start (eg, you have no control over the creation of the msi), there is no way around it. It all depends on what files are being edited as to whether or not admin rights are really required. Check out this answer: How can I create a windows installer MSI that does not require admin access

Categories