I'm looking to update the value of registry using a Edit Control in WiX installation process.
I have no problem in using Edit Control which I'm using as follows -
<Property Id="WIXUI_USERLIST" Value="Demo;" Secure="yes"/>
<Control Id="UsersList" Type="Edit" X="105" Y="192" Width="180" Height="18" Property="WIXUI_USERLIST" Indirect="no" />
This works fine to take user input and push the value in registry using WIXUI_USERLIST.
Problem start when I introduce following code to read the registry to display existing value in Edit control in a property and attach it to WIXUI_USERLIST as
<Property Id="USERLIST" Secure="yes">
<RegistrySearch Id="UserList"
Root="HKLM"
Key="[APPLICATIONHIVE]"
Name="UserList"
Type="raw"
Win64="yes" />
</Property>
<SetProperty Id="WIXUI_USERLIST" Value="[USERLIST]" After="AppSearch">USERLIST</SetProperty>
Looking at the log suggest the value correctly passed to the INSTALL phase but gets overwritten by USERLIST.
Action 14:37:22: INSTALL.
Action start 14:37:22: INSTALL.
MSI (s) (78:44) [14:37:22:770]: Running ExecuteSequence
MSI (s) (78:44) [14:37:22:770]: Doing action: FindRelatedProducts
MSI (s) (78:44) [14:37:22:770]: Note: 1: 2205 2: 3: ActionText
Action 14:37:22: FindRelatedProducts. Searching for related applications
Action start 14:37:22: FindRelatedProducts.
MSI (s) (78:44) [14:37:22:772]: Skipping FindRelatedProducts action: already done on client side
Action ended 14:37:22: FindRelatedProducts. Return value 0.
MSI (s) (78:44) [14:37:22:772]: Doing action: AppSearch
MSI (s) (78:44) [14:37:22:772]: Note: 1: 2205 2: 3: ActionText
Action 14:37:22: AppSearch. Searching for installed applications
Action start 14:37:22: AppSearch.
MSI (s) (78:44) [14:37:22:774]: Skipping AppSearch action: already done on client side
Action ended 14:37:22: AppSearch. Return value 0.
MSI (s) (78:44) [14:37:22:774]: Doing action: WIXUI_USERLIST
MSI (s) (78:44) [14:37:22:774]: Note: 1: 2205 2: 3: ActionText
Action 14:37:22: WIXUI_USERLIST.
Action start 14:37:22: WIXUI_USERLIST
MSI (s) (78:44) [14:37:22:775]: PROPERTY CHANGE: Modifying WIXUI_USERLIST property. Its current value is 'Gurinder;TestUser'. Its new value: 'Gurinder'.
Action ended 14:37:22: SetWIXUI_PORTSERVERADD. Return value 1..
In log "Gurinder" is stored in registry and edit during install to "Gurinder;TestUser"
There are a few ways to solve this problem but I believe the following is the probably the most efficient. It just requires a few tweaks to what you've done already.
First, I'd put the default values in USERLIST so that WIXUI_USERLIST can always be set. Second, need the WIXUI_USERLIST to be set only once in the UI or Execute sequence. The SetProperty element doesn't expose this ability but we can get to it using the CustomAction element. Getting the action to run only once in the sequences is the magic.
The resulting code for setting the properties would go a little like this:
<Property Id="USERLIST" Value="Demo;" Secure="yes">
<RegistrySearch Id="UserList"
Root="HKLM"
Key="[APPLICATIONHIVE]"
Name="UserList"
Type="raw"
Win64="yes" />
</Property>
<!-- replaces SetProperty but adds the ability to run only in the first sequence -->
<CustomAction Id='SetWIXUI_USERLIST' Property='WIXUI_USERLIST' Value='[USERLIST]'
Execute='firstSequence'>
<InstallUISequence>
<Custom Action='SetWIXUI_USERLIST' After='AppSearch' />
</InstallUISequence>
<InstallExecuteSequence>
<Custom Action='SetWIXUI_USERLIST' After='AppSearch' />
</InstallExecuteSequence>
Update: Additionally, I went and double checked my memory against the MSI SDK. The AppSearch action is only executed once. That means I believe you could probably remove all the WIXUI_USERLIST and custom actions and stuff and just use USERLIST everywhere. So an even simpler solution is to remove all of this and replace all instances of your WIXUI_USERLIST with USERLIST. :)
Related
Using WiX, I'm replacing a application with a new version.
In the old version, there was a directory which is no longer needed in the new version.
So on install, this should do it:
<Component Id="killDir_" Guid="..." KeyPath="yes">
<RemoveFolder Id="killDir" On="both" />
</Component>
So far so good, this works fine.
But if the folder is already removed, the setup creates it again!
This happens also when using
<util:RemoveFolderEx .../>
I have tried a condition within the <Component> tag:
<Condition>OLD_STUFF_EXISTS</Condition>
along with a DirectorySearch in the Product:
<Property Id="OLD_STUFF_EXISTS">
<DirectorySearch Id='DirSearch' Path="OldDir" Depth='0'>
</DirectorySearch>
</Property>
But that doesn't seem to work.
In the MSI log file, the RemoveFolder seems to be executed:
MSI (s) (A0:4C) [13:44:45:742]: Doing action: RemoveFolders
MSI (s) (A0:4C) [13:44:45:742]: Note: 1: 2205 2: 3: ActionText
Action 13:44:45: RemoveFolders. Removing folders
Action start 13:44:45: RemoveFolders.
Action ended 13:44:45: RemoveFolders. Return value 1.
Some lines down, the istalled creates it again:
MSI (s) (A0:4C) [13:44:45:807]: Executing op: FolderCreate(Folder=C:\...\,Foreign=0,,)
Is there any way to tell the installer NOT to create it again?
Here's what I have (based on what I've gleaned from several other Stack Overflow posts and elsewhere:
<Property Id="CACHEFOLDER">
<RegistrySearch Key="SOFTWARE\SIL\Transcelerator" Root="HKCU" Type="raw"
Id="CacheFolderRegSearch" Name="CachePath" />
</Property>
<Directory Id="TARGETDIR" Name="SourceDir
<!-- Transcelerator's cache folder in LocalAppData: -->
<!-- C:\Users\<current user>\AppData\Local\SIL\Transcelerator -->
<!-- This needs to be saved to a registry key so it can be cleaned up on uninstall and also purged when there is a new install in order to ensure that reparsing occurs. -->
<?define AppCacheFolder = "SIL\Transcelerator" ?>
<Component Id="CacheCleanup" Guid="{6A45D61D-EA73-4A8C-8941-B49A881ABB49}">
<RegistryValue Root="HKCU" Key="Software\SIL\Transcelerator" Name="CachePath"
Type="string" Value="[LocalAppData]$(var.AppCacheFolder)"
KeyPath="yes" />
<util:RemoveFolderEx On="both" Property="CACHEFOLDER"/>
</Component>
</Directory>
<Feature Id="MainApplication" Title="App Name" Level="1" Absent="disallow" Display="expand" AllowAdvertise="no" InstallDefault="local">
<ComponentRef Id="CacheCleanup" />
</Feature>
Note: Eventually, I'll want to make the MainApplication feature hidden, but for now it's comforting to see it.
Here are what I think are the relevant excerpts from the WIX log file:
AppSearch: Property: CACHEFOLDER, Signature: CacheFolderRegSearch
MSI (c) (38:F0) [18:25:39:116]: PROPERTY CHANGE: Adding CACHEFOLDER property. Its value is 'SIL\Transcelerator'.
Action ended 18:25:39: AppSearch. Return value 1.
...
MSI (c) (38:F0) [18:25:45:594]: Switching to server: PARATEXT7="C:\Program Files (x86)\Paratext 7\" PARATEXT7TEST="C:\Program Files (x86)\ParatextDir7Test\" PARATEXT8="C:\Program Files (x86)\Paratext 8\" PARATEXT8TEST="C:\Program Files (x86)\ParatextDir8Test\" PARATEXT75100ORGREATER="C:\Program Files (x86)\Paratext 7\Paratext.exe" CACHEFOLDER="SIL\Transcelerator" TARGETDIR="C:\" INSTALLDIR7="C:\Program Files (x86)\Paratext 7\plugins\Transcelerator\" INSTALLDIR7TEST="C:\Program Files (x86)\ParatextDir7Test\plugins\Transcelerator\" INSTALLDIR8="C:\Program Files (x86)\Paratext 8\plugins\Transcelerator\" INSTALLDIR8TEST="C:\Program Files (x86)\ParatextDir8Test\plugins\Transcelerator\" PLUGINDIR7="C:\Program Files (x86)\Paratext 7\plugins\" PLUGINDIR7TEST="C:\Program Files (x86)\ParatextDir7Test\plugins\" PLUGINDIR8="C:\Program Files (x86)\Paratext 8\plugins\" PLUGINDIR8TEST="C:\Program Files (x86)\ParatextDir8Test\plugins\" CURRENTDIRECTORY="C:\Projects\Transcelerator" CLIENTUILEVEL="0" CLIENTPROCESSID="17976" SOURCEDIR="C:\Projects\Transcelerator\output\installer\" ACTION="INSTALL" EXE
...
MSI (s) (E4:44) [18:25:46:006]: PROPERTY CHANGE: Adding CACHEFOLDER property. Its value is 'SIL\Transcelerator'.
...
Action 18:25:46: WixRemoveFoldersEx.
Action start 18:25:46: WixRemoveFoldersEx.
MSI (s) (E4:00) [18:25:46:041]: Invoking remote custom action. DLL: C:\Windows\Installer\MSI6019.tmp, Entrypoint: WixRemoveFoldersEx
MSI (s) (E4:78) [18:25:46:042]: Generating random cookie.
MSI (s) (E4:78) [18:25:46:044]: Created Custom Action Server with PID 18712 (0x4918).
MSI (s) (E4:54) [18:25:46:067]: Running as a service.
MSI (s) (E4:54) [18:25:46:069]: Hello, I'm your 32bit Impersonated custom action server.
WixRemoveFoldersEx: Recursing path: SIL\Transcelerator\ for row: wrfA9D8B049E87ACFF02034C5FFCFB64E42.
WixRemoveFoldersEx: Search path not found: SIL\Transcelerator*
Action ended 18:25:46: WixRemoveFoldersEx. Return value 1.
...
MSI (s) (E4:44) [18:25:46:267]: Executing op: ComponentRegister(ComponentId={6A45D61D-EA73-4A8C-8941-B49A881ABB49},KeyPath=01:\Software\SIL\Transcelerator\CachePath,State=3,,Disk=1,SharedDllRefCount=0,BinaryType=0)
1: {97A212AC-E01E-486A-A220-AF9BBBC79E87} 2: {6A45D61D-EA73-4A8C-8941-B49A881ABB49} 3: 01:\Software\SIL\Transcelerator\CachePath
...
MSI (s) (E4:44) [18:25:46:597]: Executing op: RegOpenKey(Root=-2147483647,Key=Software\SIL\Transcelerator,,BinaryType=0,,)
MSI (s) (E4:44) [18:25:46:597]: Executing op: RegAddValue(Name=CachePath,Value=SIL\Transcelerator,)
WriteRegistryValues: Key: \Software\SIL\Transcelerator, Name: CachePath, Value: SIL\Transcelerator
...
Property(S): CACHEFOLDER = SIL\Transcelerator
Nothing relevant seems to be getting added to the registry. (At one point, it seems it was adding something withe correct GUID to tell it to do an uninstall action, but now I can't figure out what I changed to make that go away.) And none of files or subfolders in C:\Users\bogle\AppData\Local\SIL\Transcelerator are getting removed either on install or uninstall. I also tried changing from On="both" to On="Uninstall" to see if I could get that to work, but no dice.
I ended up using a custom action because it turns out that what I really needed to do was clear the cached files for any/all users, not just the current user. This was especially true because the installer always runs under elevated privileges, so the current user quite typically would not be the one I actually care about. I will point out that the original problem remains unsolved, so if anyone can figure out the problem and post an alternate answer that might help someone else, that could be useful.
The name of the directory property is LocalAppDataFolder, not LocalAppData. That's not defined so it's an empty string and the path RemoveFolderEx is given isn't valid (hence the Search path not found: SIL\Transcelerator error).
We need our MSI to store the password in the registry, so users can use our installer to change connection strings, etc.
We found MsiExt for its cryptography DLL and are trying to configure the custom actions.
Our users will need to be able to install both through the UI dialogs and silently -- so we need to able to execute in both sequences.
Here is a sample of our Product.wxs
<Property Id="DB_PASSWORD" Secure="yes"/>
<Property Id="P.DB_PASSWORD">
<RegistrySearch Id="S.DB_PASSWORD" Root="HKLM" Key="SOFTWARE\$(var.Manufacturer)\$(var.ProductName)" Name="DB_PASSWORD" Type="raw" Win64="$(var.Win64)"/>
</Property>
<Component Id="c.RegistryEntries" Guid="XXXXX-XXXXX-XXXXX-XXXXX" Directory="INSTALLDIR">
<RegistryKey Root="HKLM" Key="SOFTWARE\$(var.Manufacturer)\$(var.ProductName)" Action="createAndRemoveOnUninstall">
<RegistryValue Id="R.DB_PASSWORD" Name="DB_PASSWORD" Value="[ENCRYPTED_DBPASSWORD]" Type="string" />
</RegistryKey>
</Component>
<!--For encrypting the database password on the way to the registry-->
<Binary Id="Cryptography" SourceFile="..\..\lib\msiext-1.4\CustomActions\Cryptography.dll"/>
<!--This property will receive the encrypted DB_PASSWORD that the user enters and will be encrypted-->
<Property Id="CRYPTPROTECT_DATA" Hidden="yes" />
<Property Id="CRYPTPROTECT_FLAGS" Value="CRYPTPROTECT_LOCAL_MACHINE|CRYPTPROTECT_UI_FORBIDDEN" />
<CustomAction Id="EncryptPassword" BinaryKey="Cryptography" DllEntry="CryptProtectDataHex" Execute="immediate" />
<CustomAction Id="SetDBUSERsPASSWORDForEncryption" Property="CRYPTPROTECT_DATA" Value="[DB_PASSWORD]" />
<Property Id="ENCRYPTED_DBPASSWORD" Hidden="yes" />
<SetProperty Id="ENCRYPTED_DBPASSWORD" Value="[CRYPTPROTECT_RESULT]" Sequence="execute" After="SetDBUSERsPASSWORDForEncryption" />
<!--This is for decrypting the registry value-->
<Property Id="CRYPTUNPROTECT_DATA" Hidden="yes" />
<Property Id="CRYPTUNPROTECT_FLAGS" Value="CRYPTPROTECT_LOCAL_MACHINE|CRYPTPROTECT_UI_FORBIDDEN" />
<SetProperty Id="CRYPTUNPROTECT_DATA" Value="[P.DB_PASSWORD]" Before="DecryptPassword" >NOT(DB_PASSWORD)</SetProperty>
<CustomAction Id="DecryptPassword" BinaryKey="Cryptography" DllEntry="CryptUnprotectDataHex" Execute="firstSequence" />
<CustomAction Id="SetDBUSERsDecryptedPASSWORD" Property="DB_PASSWORD" Value="[CRYPTUNPROTECT_RESULT]"/>
<CustomAction Id="LaunchApplication" BinaryKey="WixCA" DllEntry="WixShellExec" Impersonate="yes" />
<InstallUISequence>
<Custom Action="DecryptPassword" After="CostFinalize"><![CDATA[NOT(DB_PASSWORD)]]></Custom>
<Custom Action="SetDBUSERsDecryptedPASSWORD" After="DecryptPassword" ><![CDATA[NOT(DB_PASSWORD)]]></Custom>
</InstallUISequence>
<InstallExecuteSequence>
<Custom Action="SchedXmlConfig" After="InstallFiles"><![CDATA[(NOT REMOVE~="All")]]></Custom>
<Custom Action="SetDBUSERsPASSWORDForEncryption" Before="InstallInitialize" />
<Custom Action="EncryptPassword" After="SetDBUSERsPASSWORDForEncryption" />
<Custom Action="DecryptPassword" Before="InstallFiles"><![CDATA[CRYPTUNPROTECT_RESULT]]></Custom>
<Custom Action="SetDBUSERsDecryptedPASSWORD" After="DecryptPassword" ><![CDATA[CRYPTUNPROTECT_RESULT]]></Custom>
</InstallExecuteSequence>
The install log shows that the DecryptPassword is firing during the UISequence. The decryption custom action fires, then the DecryptPassword works.
Action ended 17:52:43: CostFinalize. Return value 1.
MSI (c) (CC:A8) [17:52:43:936]: Doing action: SetCRYPTUNPROTECT_DATA
Action 17:52:43: SetCRYPTUNPROTECT_DATA.
Action start 17:52:43: SetCRYPTUNPROTECT_DATA.
MSI (c) (CC:A8) [17:52:43:937]: PROPERTY CHANGE: Adding CRYPTUNPROTECT_DATA property. Its value is '**********'.
Action ended 17:52:43: SetCRYPTUNPROTECT_DATA. Return value 1.
MSI (c) (CC:A8) [17:52:43:937]: Doing action: DecryptPassword
Action 17:52:43: DecryptPassword.
Action start 17:52:43: DecryptPassword.
MSI (c) (CC:7C) [17:52:49:129]: Invoking remote custom action. DLL: C:\Users\kujotx\AppData\Local\Temp\MSI9904.tmp, Entrypoint: CryptUnprotectDataHex
MSI (c) (CC:54) [17:52:49:130]: Cloaking enabled.
MSI (c) (CC:54) [17:52:49:130]: Attempting to enable all disabled privileges before calling Install on Server
MSI (c) (CC:54) [17:52:49:130]: Connected to service for CA interface.
CryptUnprotectDataHex: MSI Extensions 1.4.1114.0
MSI (c) (CC!94) [17:52:49:236]: PROPERTY CHANGE: Adding CRYPTUNPROTECT_RESULT property. Its value is 'password'.
Action ended 17:52:49: DecryptPassword. Return value 1.
MSI (c) (CC:A8) [17:52:49:238]: Doing action: SetDBUSERsDecryptedPASSWORD
Action 17:52:49: SetDBUSERsDecryptedPASSWORD.
Action start 17:52:49: SetDBUSERsDecryptedPASSWORD.
MSI (c) (CC:A8) [17:52:49:239]: PROPERTY CHANGE: Adding DB_PASSWORD property. Its value is '**********'.
Action ended 17:52:49: SetDBUSERsDecryptedPASSWORD. Return value 1.
My problem is that SetCRYPTUNPROTECT_DATA is not executing during ExecuteSequence, so DecryptPassword fails:
Action ended 17:53:47: AppSearch. Return value 1.
MSI (s) (28:1C) [17:53:47:206]: Doing action: DecryptPassword
Action 17:53:47: DecryptPassword.
Action start 17:53:47: DecryptPassword.
MSI (s) (28:98) [17:53:47:217]: Invoking remote custom action. DLL: C:\Windows\Installer\MSI9020.tmp, Entrypoint: CryptUnprotectDataHex
CryptUnprotectDataHex: MSI Extensions 1.4.1114.0
CryptUnprotectDataHex: [CryptUnprotectDataHex] std::exception: 0x80070057 - Error in CryptUnprotectData: The parameter is incorrect.
MSI (s) (28!DC) [17:53:47:237]: PROPERTY CHANGE: Adding CA_ERROR property. Its value is '0x80070057 - Error in CryptUnprotectData: The parameter is incorrect.'.
CustomAction DecryptPassword returned actual error code 1603 (note this may not be 100% accurate if translation happened inside sandbox)
Action ended 17:53:47: DecryptPassword. Return value 3.
Action ended 17:53:47: INSTALL. Return value 3.
Can you point out how to schedule our custom actions to get this to decrypt properly?
Your problem is likely due to one of the following:
The <SetProperty> element that sets the CRYPTUNPROTECT_DATA property is being conditionally run based on the value of DB_PASSWORD not being set, but we can't see from the logs that this property is indeed not set. The property action won't run if this evaluates to false.
The default setting for the Sequence attribute on the <SetProperty> element is not working correctly. Try setting the Sequence attribute to first, which will schedule it to be run in the first of either InstallUISequence or InstallExecuteSequence (will be first in silent mode). If that doesn't work, try setting it to both explicitly.
Try changing the Before attribute on the custom action for DecryptPassword in the InstallExecuteSequence to After="InstallInitialize", which is much earlier on in the install sequence than InstallFiles. This will get you behavior that is similar to running with the UI Sequence, which computes and modifies your properties and runs your actions prior to anything actually being installed.
See this page for suggested sequencing (and see relative ordering of events)
From tutorial:
<Property Id="BROWSER">
<RegistrySearch Id='DefaultBrowser' Type='raw' Root='HKCR' Key='http\shell\open\command' />
</Property>
<CustomAction Id='LaunchBrowser' Property='BROWSER' ExeCommand='www.something.com' Return='asyncNoWait' />
<InstallExecuteSequence>
...
<Custom Action='LaunchBrowser' After='InstallFinalize'>NOT Installed</Custom>
</InstallExecuteSequence>
As I can see from installation traces, property BROWSER is calculated correctly:
Property(S): BROWSER = "C:\Program Files (x86)\Internet
Explorer\iexplore.exe" -nohome
But browser is not open after installation.
MSI (s) (88:90) [18:38:30:331]: Doing action: LaunchBrowser MSI (s)
(88:90) [18:38:30:331]: Note: 1: 2205 2: 3: ActionText Action start
18:38:30: LaunchBrowser. Action ended 18:38:30: INSTALL. Return value
1. Action ended 18:38:30: LaunchBrowser. Return value 1631.
...
MSI (c) (64:6C) [18:38:30:409]: Product: WebPrintingService -- Installation completed successfully.
What could be wrong?
Windows 7.
UAC - default.
UPDATE:
As a workaround it is possible to use following code(but I am not sure that it is a good workaround):
<Property Id="BROWSER">
<RegistrySearch Id='DefaultBrowser' Type='raw' Root='HKCR' Key='http\shell\open\command' />
</Property>
<CustomAction Id="LaunchBrowser" Directory="INSTALLDIR" Impersonate="no" Execute="deferred" ExeCommand='[BROWSER] "test.html"' Return="check"/>
<InstallExecuteSequence>
<Custom Action='LaunchBrowser' Before='InstallFinalize'>NOT Installed</Custom>
</InstallExecuteSequence>
I have not made an installer that can run the default browser after installtion but I have made an installer with WiX that launches the installed application. As far as I know, the ExeCommand property must contain the file path to an executable file. It can be either the path on your computer directly or a string property you defined containing the file path. Maybe you can tell the program wich file it should open, wich would be in your case a website.
I hope this helps.
I have a WiX installer which does some work, and at the end, launch RSYNC installer (it's an EXE file, no problem to have both working at same time).
But when uninstalling, I want to remove RSYNC too.
<CustomAction Id="InstallRSyncDaemon" FileKey="cwRsyncServer_4.0.5_Installer.exe" ExeCommand="/q" Execute="deferred" Return="ignore" Impersonate="no" />
<CustomAction Id="UninstallRSyncDaemon" FileKey="rsync_uninstall.bat" ExeCommand="[INSTALLDIR]" Execute="deferred" Return="check" Impersonate="no" />
<CustomAction Id="EditConf" FileKey="rsync.bat" ExeCommand="[INSTALLDIR]" Execute="deferred" Return="ignore" Impersonate="no"/>
<InstallExecuteSequence>
<Custom Action='InstallRSyncDaemon' Before='InstallFinalize'>(NOT remove="ALL")</Custom>
<Custom Action='EditConf' After='InstallRSyncDaemon'>(NOT remove="ALL")</Custom>
<Custom Action='UninstallRSyncDaemon' Before='RemoveFiles'>remove="ALL"</Custom>
</InstallExecuteSequence>
My problem is that during uninstalling, the Custom Action 'UninstallRSyncDaemon' is skipped:
MSI (s) (58:78) [17:37:40:475]: Skipping action: UninstallRSyncDaemon (condition is false)
I have tried with "installed" and other conditions, with the same result.
At the beginnning of the log file, I have:
MSI (c) (9C:80) [17:37:36:772]: PROPERTY CHANGE: Adding ACTION property. Its value is 'INSTALL'.
MSI (c) (9C:80) [17:37:36:772]: Doing action: INSTALL
Action 17:37:36: INSTALL.
Action start 17:37:36: INSTALL.
MSI (c) (9C:80) [17:37:36:772]: UI Sequence table 'InstallUISequence' is present and populated.
MSI (c) (9C:80) [17:37:36:772]: Running UISequence
MSI (c) (9C:80) [17:37:36:772]: PROPERTY CHANGE: Adding EXECUTEACTION property. Its value is 'INSTALL'.
If I try to change to the condition to launch it during installation, it is working, so for me it is just the statement of the condition.
But I do not understand why, this is the condition always used in a tutorial or in an answer on Stack Overflow.
I think the remove should be in upper case. Try this.
REMOVE="ALL"
But it will run in modify and Upgrade also. Check more about this condition here.