How to set folder permission, in a msi-package, by calling on cacls.exe with a custom action

I am a new user to Installshield and I am trying to modify an allready existing msi-package so that it grants adequate folder permissions for non-administrotr users.

I have been reading every post on the matter on InstallShield own supportforum, and everybody seem to agree that the best way of doing this is by making a custom action which calls on the DOS availible "cacls.exe" or "icacls.exe" command (located in system32 folder). Using cacls is supposed to work alot better than using the "LockPermission table" - which I have also tried to use, without any success.

Using cacls is supposed to be fairly simple. How ever no one seems to be willing to explain in a thorough way, how this is exactly is achieved. This is the command I want to use, which should grant everyone full rights on the folder and its subfolders.

icacls "C:\Program Files (x86)\program_name" /grant:r <user>:(OI)(CI)M /T /C

But since the end-users have the ability to change installation directory I would need to be able to use some kind of a environment variable that can be used in cooperation with the msi-installation. For example:

icacls "C:\[INSTALLDIR]\program_name" /grant:r <user>:(OI)(CI)M /T /C

Some people mention that they use "LaunchAppAndWait when they call for cacls, but beeing the newbie as I am, that doesnt tell me anything. Perhaps you can explain that?

I would be immensely grateful if some one could explain how this can be done, step by step, in InstallShield!
Who is Participating?
Vadim RappCommented:
> Unfortunately, I do not have the skills required for writing a script like that

p = Session.Property("INSTALLDIR")
if right(p,1) = "\" then p = left(p,len(p)-1)
createobject("").run "cacls.exe """ & p & """ /T /E /C /G"
Vadim RappCommented:
> on InstallShield own supportforum, and everybody seem to agree that the best way of doing this is by making a custom action

Installshield-related public for some reason loves making custom action on every opportunity. Probably that's why practically every major installation made by Installshield soon gets accompanied by "cleanup utility" in order to clean up the mess left behind.

Custom actions generally should be used _only_ when it's absolutely proven that the task can't be accomplished by standard ways. Custom action by definition can't be best way, it is never better than unavoidable evil.

Here are some of the reasons why this is so:
1. if installation fails, everything that Installer have made by standard ways is rolled back. Custom actions of course are not.
2. There are many installation scenarios besides pure and simple loca installation. The product can be advertised; the product can be installed-on-demand; the user profile can be roaming; the product can be installed per-machine or per-user; the product can be repaired; and more. Native Installer actions are all tested and proven to work in each. Custom actions are not, you simply place it in the script, and then suddenly the product won't uninstall because it hits some scenario you did not even know exists, and your action runs where it should not, and fails, failing the whole process.

What exactly did not work with the standard way? by the way, you mentioned the table lockpermissions; why going directly to the table if you can simply right-click your folder in the "destination computer's folders" panel, select Properties/Permissions... and specify the permissions in user-friendly dialog?

I'm also curious how you are going to specify the user to assign permissions to, and generally, why is this necessary. Usually, if you use standard ways to build the installation, the permissions on the folders and files are assigned in exactly the right way, following best practices; unless this application has some very specific and unusual requirements  (whichmost likely would be the consequence of not following best practices in developing the application itself), this area never requires and special attention.

Now that you've been warned...

The way you have posted it should be working - if you insert [PROPERTY], it gets replaced by the property value at the time of installation.

In order to create custom action, click "custom actions and sequences" in the left tree, then right-click Custom Actions in the middle pane and create new action - new EXE - "path referencing a directory".
andre_stAuthor Commented:
Thank for your detailed answer!

The reason why I need to set permissions in the msi-packet is because the program developer didnt do it right from the beginning. The program stores profile information in the program folder, and therefore the users have to have read/write/change permissions in the programfolder. The program is no longer supported by the developer, so therefore I want to fix this problem in the msi-packet instead.

CACLS/ICACLS: The reason why many people use cacls.exe instead is because using the "lockpermission table" is for some reason ofen unreliable, and simply doesnt work always (either by going straight to the table, or by selecting properties/permission in the destination computer´s folder). I have tried it many times, but nothing seems to happen. The installation runs smoothly, but the permissions are not applied. In the "domain field" I used, among another things, [%USERDOMAIN] as suggested in the InstallShield help, and the usergroup "everyone". I read many posts where people were having problem of getting it working - and they finally solved the problem by calling "cacls" or something similar during install to set permissions. Another reason people mention why cacls should be used instead, is because there is a risk that the "lockpermission" table applies only the permissions in the msi-packet to the folders, and erases others.
I.E other accounts, on the installed machine/domain loose permissions, such as "administrator", "system".

Yet another reason, and a very significant one, is that using "lockpermission" table does not enable you to assign inherited permission. So if you have, lets say 50 folders who also have subfolders - you would have to right click on everyone of them and assign the permissions. That is very time consuming, but this is not a problem when cacls is used.

I´ve been testing some more this morning and I think I am on the right track, regarding "cacls". I created a custom action, in the way you suggested. I read that the script has to be set to "Deferred Execution". I have also read in a forum that the script is preferrably to be executed "after create folders" in the sequence. This way the permissions are applied during the filecopy, instead of in the end of the sequence - therefore saving time. Do you have any comments on this? Would you do this in some other way?

In the "Filename & Command line"-field of the Custom action tab I can write the full pathname of the folders which are to be modified. For example:

cacls.exe "C:\program files\company\programname" /T /E /G everyone:f

And this works well, the permissions are set during install. But I am having a hard time trying to find a way of using the parameter [INSTALLDIR] to work. Since the end-user can choose to install to a different location, I have to, of course, make this work regardless of which directory is beeing used.

How should this be typed in?

Most preferreably I woul like to just use:

cacls.exe "[INSTALLDIR]" /T /E /G everyone:f

But that doesnt work. I have tried other variants like:

c:\program files\[INSTALLDIR]

Thanks again for your help!


Cloud Class® Course: Microsoft Azure 2017

Azure has a changed a lot since it was originally introduce by adding new services and features. Do you know everything you need to about Azure? This course will teach you about the Azure App Service, monitoring and application insights, DevOps, and Team Services.

Vadim RappCommented:
I have to say, I did not ever try to control the permissions myself, so it's all general suggestions.

cacls.exe "[INSTALLDIR]" /T /E /G everyone:f

should work, but not in deferred execution. Place it in immediate execution; I would place it after CreateFolders, so the files would inherit. You can debug it by specifying instead something like

command.exe /c echo [INSTALLDIR]

so it spawns a message and you see what has been passed.
Vadim RappCommented:
Another thought: I think you can resolve your problem more easily and reliably by placing product directory under %appdata% instead of %programfiles%, so it becomes user-writeable without any special effort.
andre_stAuthor Commented:
I have managed to pinpoint where the problems seems to be. When I looked at the msi-logfiles I noticed that the internal script is indeed reading the correct values of the [INSTALLDIR] Property. The problem is however that the script writes an "\" after the path. For example:

cacls.exe "c:\program files\company\program_name\ /T /E /G everyone:F

And to me, a newbie, this looks fine...but this "\" in the end of the path, makes the "cacls" malfunction. I cannot either use the command line above in CMD. But I can however use:

cacls.exe "c:\program files\company\program_name /T /E /G everyone:F

The question then becomes, how do I go about making InstallShield not adding the "\" at the end of the path? Or is there some other way of solving this?
Vadim RappCommented:
Write vbscript that will remove the backslash and call the cacls.exe. Vbscript can get the value of INSTALLDIR as passed parameter, or as Session.Property("INSTALLDIR")
andre_stAuthor Commented:
Unfortunately, I do not have the skills required for writing a script like that. That is a bit above my league...

I however found a "partial" solution.

If [INSTALLDIR] points to lets say: "c:\test"

And If I use a wildcard "*" after [INSTALLDIR], like this: "cacls.exe [INSTALLDIR]* /T /E /C /G" it would result in the msi-installer reading it like [INSTALLDIR] as: "c:\test\*"

In this way the msi-installer applies permissions to all files and folders beneath c:\test. This has worked for some of the application I am working with. But this does not actually give permission on the "c:\test" folder itself - only its files and subfolders. So if the program for example needs to create a new file in the c:\test folder - it will not succeed.

In case anyone trips over this question like I did, my solution was to use the following.  I was packaging eclipse and INSTALLDIR was going to C:\Program Files\eclipse

An 'Execute Program from destination' action with the following command line:

CACLS "[%ProgramFiles]\eclipse" /e /p Users:C

placed in Deferred Execution / System Context straight after CreateFolders in the Normal Execute sequence.
Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.

All Courses

From novice to tech pro — start learning today.