Using Environment Variables in bundles can often present a challenge. Sometimes an administrator wants a variable name such as %USERNAME% to be used by a ZCM process. The ZCM administrator may want that variable resolved in the context of the logged on user, in the context of an elevated process or perhaps never resolved. This article will help demonstrate how to achieve all of these results in a number of different scenarios.
As a general rule, ZCM will always evaluate a variable such as %USERNAME% in the context of the logged on user and as SYSTEM if there is not a logged on user. When a process is launched by ZCM, that process will generally resolve in the context in which the process is running. If a specific action is configurable to run in alternate contexts such as "System" or "Dynamic Administrator", this is an indication that the variable will usually resolve in that context in the specific elevated process itself only. One factor that adds complexity to this process is that some actions, such as script actions, will first generate a script and resolve the variables to generate the script. This will occur in the logged on user's context, so the value of the variable is evaluated as the logged on user for script generation. To ensure the variable is entered literally into the script for resolution later, the variable will need to be escaped. In other cases, an administrator may want a variable to never be evaluated in any part of the process and always used literally. This may require further escaping of the variable.
I have created a bundle called "Environmental_Variable_Use_In_Bundles", that demonstrates the use of the %USERNAME% variable in six different types of bundle actions. There are a total of six different types of bundle actions that create a total of fifteen different registry keys demonstrating how to ensure the bundle handles the variable as desired.
In the demo bundle, all the sample registry keys are created under HKLM\Software\ACME, as shown above. The very first bundle action is one which deletes the HKLM\Software\ACME registry key. This is done to ensure any entries that exist under HKLM\Software\ACME exist due to the current running of the bundle and are not artifacts from prior events on the device. Below, the article will discuss each of the six different type of bundle actions and how and why %USERNAME% evaluated to the results above in each of the fifteen different registry keys.
TIP: The Bundle has a "System Requirement" setting for "Environment Variable Exists: LOGONSERVER: YES". While not required for this bundle, it is a useful setting to set if you want to ensure that a particular bundle or particular action only runs when a user is logged onto a system. When there is not a logged on user on a system, this variable will not exist.
Method 1 - Registry Edit Bundle
The 2nd and 3rd actions in the bundle demonstrate how to use variables with the "Registry Edit Action". On the "Advanced" tab of the action, it is VERY important to review the settings for "Run Action As". The most commonly desired behavior will be achieved by setting the action to run as "System" and to check the boxes to resolve variables as the logged on user and to write any "HKCU" entries to the logged on user. However, sometimes it may be preferred to resolve environment variables in the system context, in which case that box should not be checked. Furthermore, SYSTEM actually has its own "HKCU" portion of the registry which is ".DEFAULT", which should not be confused with "Default User" settings for newly created profiles. If the REGEDIT action runs as system and you prefer HKCU writes to the logged on user instead of SYSTEM's own HKCU portion of the registry, make sure this box is checked.
When using the USERNAME variable in the sample bundle, it is entered in two different formats to achieve to different results.
%USERNAME% causes the variable to be evaluated and the value placed in the registry as is seen in the 2nd and 3rd values in the registry key picture above.
%%USERNAME%% is escaped with surrounding quotes so that %USERNAME% is literally entered into the registry as seen in the 1st registry value above.
Method 2 - Dynamic Batch File
This is one of the most mind bending bundles with multiple levels of escaping in some cases required to achieve the results desired. The first step in processing the dynamic script action is to create a temporary batch file based on the contents defined in the action. Any environment variables used in the action are evaluated as the logged on user at the time the script is created, which may result in the value instead of the variable name being written to the batch file. Any variables that actually get written to the temporary batch file, will be evaluated at run-time (microseconds later) in the defined security context of the bundle (Logged-on-User, System, or DAU).
%%%USERNAME%%% in the bundle would output: %%USERNAME%% to the batch file.
Thus, %USERNAME% would use the logged on user's name in the batch file, REGARDLESS, of the security context in which the bundle runs.
%%USERNAME%% would cause the script to use the variable %USERNAME% so that the value would match security context. If it ran as logged on user, it would be "CRAIG" and if it ran as SYSTEM it would evaluate as the computer name.
If one wishes to use a literal variable name in a batch file so that the variable is not evaluated at run-time, that variable should be escaped in quotes. Thus the third example of %%% is used when the batch file should not evaluate the variable. (Note: This escape behavior is native to batch files and is not ZCM specific.)
Based on the above behavior, the results of the bundle action shown below should make far more sense.
Method 3 - Executing Existing Batch Files
The example in Method 3 will make a slight modification to the example seen above. In this case, we are going to pass the environmental variable %USERNAME% as a parameter to the batch file. This is an important ability in cases where the batch file is an existing script that is not created on the fly. This example does include an action to create the script, but that is simply to ensure the demo bundle is simple to run as well as fully self-contained. In the script creation action, not shown here, we will insert %%1%%, which will get written to the batch file as %1%.
The first parameter to the script file will be reflected in the %1% variable. This parameter is passed using the "Script Parameters" section. The value of the variable in this location is evaluated as the logged on user if one exists.
Method 4 - Launch Executable Actions
Method 4 is very similar to method 3 above, but the examples here show examples where we desire to pass the VALUE of the variable as soon above as well a second example where we desire to pass the literal variable name to the process.
In both examples, the "Command" is "Reg.exe". The differing parameters are noted below.
In this case, to escape %USERNAME% so the literal variable is passed instead of being evaluated, it is necessary to place the percentage signs in quotes. This behavior mimics the behavior outside ZCM and the same syntax would be used if running "Reg.exe" manually from a command-line.
Method 5 - Fixed PowerShell Script
The first action creates the script below, which will be explained:
Set-ItemProperty -Path 'HKLM:\Software\Acme' -Name M5_PowerShell_1_ValueReturnsSytemUserName -Value $env:UserName
The first line configures to accept a command line parameter passing in an argument called "$CurrentUser", which will be used to accept the %USERNAME% being passed via the command-line in the followup action which executes this script.
The second line makes a call to the operating system to retrieve the value of the environment variable USERNAME and insert it into a registry value.
On the third line, As part of the PowerShell script creation process, %%USERNAME%% will be evaluated so that %USERNAME% is placed in the actual script. Since PowerShell does not recognize that format as a variable, it will insert that literally into the designated registry value.
The final line of the script will populate the designated value with the contents of $CurrentUser which is a value it will receive as part of the command line in the next action below.
The important part of the action above is the "Script Parameters:" line. This will evaluate the current value of %username% based upon the logged on user and pass that value via the "-CurrentUser" parameter previously defined in the script.
Method 6 - Dynamic PowerShell Script
The final example does not introduce any new concepts. During the generation of the PowerShell script, %UserName% will evaluate to 'craig' and be written literally to the PowerShell script that will be executed. %%UserName%% will evaluate to %UserName% and be written literally to the PowerShell script that will be executed. PowerShell does not evaluate strings between quotes as variables, so the values written to the script will be entered identically into the registry.
To find other articles by Craig Wilson simply follow the link below:
If you find this article useful, please be sure to give it a like at the bottom of the page!