NSIS Call PowerShell in Uninstall Section - powershell

I'm working on creating a NSIS installer for our new VPN we will be getting in around a month. I have it calling a PowerShell script to create the connection without issue. However, removing the VPN connection is not working with the same method. Below is all my code for the uninstall
Section Uninstall
ExpandEnvStrings $0 "%COMSPEC%"
ExecShell "" '"$0"' "/C powershell -ExecutionPolicy Bypass -WindowStyle Hidden -File .\DeleteVPNConnection.ps1 -connectionName ${VPN_NAME} " SW_HIDE
Delete "$INSTDIR\uninst.exe"
Delete "$INSTDIR\CreateVPNConnection.ps1"
Delete "$INSTDIR\DeleteVPNConnection.ps1"
RMDir /r "$INSTDIR"
DeleteRegKey ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}"
SetAutoClose true
SectionEnd
Does anyone have any ideas why the uninstall script isn't working (and I have tried removing the connectionName parameter as well, same issue).

There are two issues with your code:
You are specifying a relative .ps file path.
ExecShell does not wait so the .ps file might get deleted too early.
If you are using NSIS 3.02 you can use ExecShellWait:
ExpandEnvStrings $0 "%COMSPEC%"
ExecShellWait "" '"$0"' '/C powershell -ExecutionPolicy Bypass -WindowStyle Hidden -File "$InstDir\DeleteVPNConnection.ps1" -connectionName ${VPN_NAME}' SW_HIDE
If you are using a older NSIS version then you must use ExecWait (does not hide the console window) or the nsExec plug-in.

Related

Run PowerShell, admin mode, in directory from shell context menu

I have created an Explorer Shell cascading context menu for opening PowerShell within a directory using the code shown below. (Note: I have a sample path hard-coded in the admin command for testing.)
[HKEY_LOCAL_MACHINE\SOFTWARE\Classes\Directory\shell\powershell.exe]
"MUIVerb"=""
"SubCommands"="powershell;powershell_admin"
"Icon"="PowerShell.exe"
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\CommandStore\shell\powershell]
#="Open PowerShell here"
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\CommandStore\shell\powershell\command]
#="powershell.exe -NoLogo -NoExit -Command Set-Location '%V'"
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\CommandStore\shell\powershell_admin]
#="Open PowerShell (admin) here"
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\CommandStore\shell\powershell_admin\command]
#="powershell.exe -Command 'Start-Process PowerShell -ArgumentList \"-NoLogo -NoExit -Command Set-Location C:\Python27\" -Verb RunAs'"
The non-admin command works perfectly. The admin command ignores everything in the ArgumentList.
If I open a PowerShell and execute the code within the single-quotes(') directly it works fine. E.G.:
Start-Process PowerShell -ArgumentList \"-NoLogo -NoExit -Command Set-Location C:\Python27\" -Verb RunAs
But when executing from the context menu it opens in Admin mode but displays the logo and doesn't execute the Set-Location.
Thanks in advance
This is a cool utility.
I installed the Registry entries to experiment, and now that it's working I'll probably keep it.
In my experiments the window closed immediately, even before it brought up the UAC dialog, until I enclosed the outer command in escaped double quotes and the inner parameters in single quotes. Then it worked fine for every folder except those under Program Files. For that we need to enclose %v in single quotes, and double-escape the double-quotes surrounding it:
#="powershell.exe -command \"start-process powershell.exe -ArgumentList \\\"-NoLogo -NoExit -Command Set-Location '%v'\\\" -verb RunAs\""
I used %v - the context folder name, which seemed to be your original intention based on the menu label 'Open Powershell (admin) here'.
Debugging notes:
For experimentation it's a little easier to make the changes in Regedit.exe and let the export functionality add the escaping characters as necessary
In Windows 10 you can make changes directly within Regedit.exe and the context menu/action is updated immediately, which I confirmed by adding a timestamp to the menu label
In other versions of Windows it may be necessary to stop and restart explorer.exe

Windows shortcut to a PowerShell script with relative path

When creating a Windows shortcut to launch a PowerShell script the following works fine when double clicked as a regular user and with right click Run as administrator:
%SystemRoot%\System32\WindowsPowerShell\v1.0\powershell.exe -ExecutionPolicy "Bypass" -Command "&{& 'C:\Script.ps1'}"
However, when the path is relative and not known upfront the following works fine when double clicked as a regular user but not with right click Run as administrator:
%SystemRoot%\System32\WindowsPowerShell\v1.0\powershell.exe -ExecutionPolicy "Bypass" -Command "&{& '.\Script.ps1'}"
My question, how can I have it work in both cases when the path is relative? I tried using PSScriptRoot but that didn't work either.
Thank you for your help.
When launching as admin from Explorer, you must provide an absolute path to the script.
Explorer.exe ignores the starting directory from the shortcut when launching a process as admin. Instead, Admin-level processes always launch with the current directory in [Environment]::GetFolderPath('System') (usually C:\Windows\System32)
The easy way to run in a different directory is to change directory at the beginning of your script. The following line will cd to the directory the script is in.
Set-Location $PsScriptRoot
If the script needs to start in a different path, then you may have to write a function to discover where that path is on the local machine (such as enumerating USB drives)
You can use your current solution for non-admin promoted shortcuts then auto promote the script internally:
# ========================================= Admin Rights =======================================================
# Usage: asAdmin $PSCommandPath
function asAdmin
{
[string]$cmdPath = $args[0]
if (!([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")) { Start-Process powershell.exe "-NoProfile -ExecutionPolicy Bypass -File `"$cmdPath`"" -Verb RunAs; exit }
}

Run Powershell Silently via NSIS

I have a powershell script that I want to run silently.
I am using NSIS script, it's still promoting the powershell command prompt when .exe file is ran..
Is there a way so it will silently.
!include FileFunc.nsh
!include x64.nsh
OutFile "script.exe"
SilentInstall silent
RequestExecutionLevel admin
Function .onInit
SetSilent silent
FunctionEnd
Section
SetOutPath $EXEDIR
File "script.ps1"
IfSilent 0 +2
ExecWait "powershell -ExecutionPolicy Bypass .\script.ps1 -FFFeatureOff"
SectionEnd
Function .onInstSuccess
Delete "script.ps1"
FunctionEnd
There is an example here that uses silent install, but I couldn't get it working when I tried it. http://nsis.sourceforge.net/Examples/silent.nsi
Try this:
ExecWait "powershell -ExecutionPolicy Bypass -WindowStyle Hidden -File .\script.ps1 -FFFeatureOff"
More info: PowerShell.exe Command-Line Help
You can try ExecShell for this, it allows to hide console via SW_HIDE flag:
ExpandEnvStrings $0 "%COMSPEC%"
ExecShell "" '"$0"' "/C powershell -ExecutionPolicy Bypass .\script.ps1 -FFFeatureOff" SW_HIDE
Also, refer to this question:
Exec vs ExecWait vs ExecShell vs nsExec::Exec vs nsExec::ExecToLog vs nsExec::ExecToStack vs ExecDos vs ExeCmd
Powershell.exe is a console application and console applications get a console window by default and the NSIS silent parameter has no impact on console windows created for child processes. A parameter like -WindowStyle Hidden that can be passed to the child process will always cause a console window to be visible on the screen for a short period of time because Windows will create the console window before the child process starts running.
If you need to hide a console window then you should use a plugin. nsExec is part of the default install or you could use a 3rd-party plugin like ExecDos that offers more advanced features like stdin handling.
If you don't need to wait for the child process then you can try ExecShell as suggested by Serge Z...

How to execute a PowerShell script from Notepad++

I am using Notepad++ to edit a PowerShell file and want to be able to execute that file from inside Notepad++.
How can I set that up?
It took me a little fiddling, but I finally got this working. (I am using version 1.0 but this should work in other versions as well.)
Notepad++ can be set up to run commands, and assign shortcuts to those commands, as follows:
From the menu, click Run → Run
Add the command
C:\NotepadRun.bat "$(FULL_CURRENT_PATH)"
Save the command, giving it a name and a key shortcut.
Below are the contents of the batch file. I named mine NotepadRun.bat, but you can name it whatever.
#echo off
GOTO %~sx1
:.ps1
cd "%~d1%~p1"
powershell.exe .\%~n1%~sx1
GOTO end
:.rb
ruby "%~f1"
GOTO end
:.php
php "%~f1"
GOTO end
:end
pause
As a note upgrading to Windows7 and Powershell 2 I found some Issues with this and have updated to passing in an ExecutionPolicy to ensure I can run the script I am editing.
:.ps1
cd "%~d1%~p1"
powershell -ExecutionPolicy Unrestricted -File "%~n1%~sx1"
GOTO end
See Using Notepad++ to Compile and Run Java Programs and replace "javac" with "C:Windows\system32\WindowsPowerShell\v1.0\powershell.exe" (or your path to PowerShell). (Caveat: I'm not a Notepad++ user and haven't tried this.)
That said, I'd just use PowerShell ISE (installs with PowerShell) or one of the other dedicated PowerShell IDEs instead.
I would recommend using PowerShell ISE which comes as part of PowerShell and designed specifically for Powershell.
You can run a saved script from "Run" -> "Run" menu in Notepad++ with the following command:
powershell.exe -noexit -command . \"$(FULL_CURRENT_PATH)\"
Based on the answers before:
powershell.exe -ExecutionPolicy Unrestricted -NoLogo -File "$(FULL_CURRENT_PATH)"
You can also add the -NoExit parameter to keep PowerShell from closing automatically:
powershell.exe -ExecutionPolicy Unrestricted -NoExit -NoLogo -File "$(FULL_CURRENT_PATH)"
Note: File has to be saved.

Why are my PowerShell scripts not running?

I wrote a simple batch file as a PowerShell script, and I am getting errors when they run.
It's in a scripts directory in my path. This is the error I get:
Cannot be loaded because the execution of scripts is disabled on this system.
Please see "get-help about-signing".
I looked in the help, but it's less than helpful.
It could be PowerShell's default security level, which (IIRC) will only run signed scripts.
Try typing this:
set-executionpolicy remotesigned
That will tell PowerShell to allow local (that is, on a local drive) unsigned scripts to run.
Then try executing your script again.
You need to run Set-ExecutionPolicy:
Set-ExecutionPolicy Restricted <-- Will not allow any powershell scripts to run. Only individual commands may be run.
Set-ExecutionPolicy AllSigned <-- Will allow signed powershell scripts to run.
Set-ExecutionPolicy RemoteSigned <-- Allows unsigned local script and signed remote powershell scripts to run.
Set-ExecutionPolicy Unrestricted <-- Will allow unsigned powershell scripts to run. Warns before running downloaded scripts.
Set-ExecutionPolicy Bypass <-- Nothing is blocked and there are no warnings or prompts.
Use:
Set-ExecutionPolicy -ExecutionPolicy Bypass -Scope Process
Always use the above command to enable to executing PowerShell in the current session.
I was able to bypass this error by invoking PowerShell like this:
powershell -executionpolicy bypass -File .\MYSCRIPT.ps1
That is, I added the -executionpolicy bypass to the way I invoked the script.
This worked on Windows 7 Service Pack 1. I am new to PowerShell, so there could be caveats to doing that that I am not aware of.
[Edit 2017-06-26] I have continued to use this technique on other systems including Windows 10 and Windows 2012 R2 without issue.
Here is what I am using now. This keeps me from accidentally running the script by clicking on it. When I run it in the scheduler I add one argument: "scheduler" and that bypasses the prompt.
This also pauses the window at the end so I can see the output of PowerShell.
if NOT "%1" == "scheduler" (
#echo looks like you started the script by clicking on it.
#echo press space to continue or control C to exit.
pause
)
C:
cd \Scripts
powershell -executionpolicy bypass -File .\rundps.ps1
set psexitcode=%errorlevel%
if NOT "%1" == "scheduler" (
#echo Powershell finished. Press space to exit.
pause
)
exit /b %psexitcode%
Set-ExecutionPolicy -ExecutionPolicy Bypass -Scope Process
The above command worked for me even when the following error happens:
Access to the registry key 'HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\PowerShell\1\ShellIds\Microsoft.PowerShell' is denied.
Also it's worth knowing that you may need to include .\ in front of the script name. For example:
.\scriptname.ps1
The command set-executionpolicy unrestricted will allow any script you create to run as the logged in user. Just be sure to set the executionpolicy setting back to signed using the set-executionpolicy signed command prior to logging out.
We can bypass execution policy in a nice way (inside command prompt):
type file.ps1 | powershell -command -
Or inside powershell:
gc file.ps1|powershell -c -
On Windows 10:
Click change security property of myfile.ps1 and change "allow access" by right click / properties on myfile.ps1
It would be ideal to bypass execution policies e.g. through
powershell -executionpolicy bypass -File .\MYSCRIPT.ps1
Unfortunately this can still be prevented by group policies. As a workaround, you can encode your script as Base64 by running this in PowerShell:
[Convert]::ToBase64String([Text.Encoding]::Unicode.GetBytes((Get-Content .\MYSCRIPT.ps1)))
Then execute the result like this:
powershell.exe -EncodedCommand "put-your-base64-string-here"
Caveat: This won't work with scripts that require parameters.