Using installer choices.xml to modify AnyConnect and McAfee deployments

[edit] I have written a follow up article explaining how to wrap a vendor package and custom choices.xml in a wrapper package.[/edit]

I have seen several posts on MacAdmin Slack asking for help deploying only components of big packages that the business wants or needs.  There are often several ways of handling this.  For example, from the McAfee ePO console, your admin can give you a Threat Prevention only installer instead of the full Endpoint Security package.  That is great if you can grab that yourself or the admin is helpful and able to get it for you.  This isn’t always the case.  Another route is to install the full package and then uninstall the pieces that you don’t want/need.  The Cisco AnyConnect Secure Mobility client installer does put uninstall scripts for each piece of the package in /opt/cisco/anyconnect/bin.  Both of these options can get your Macs to the end state you want, but they do have potential drawbacks/complications.  Using the Apple provided installer command line tool, we can see what options are available in these packages and then create a file to set which pieces we want.  This does take some work upfront, but we have all the tools we need.  

To start we need an installer package that has choices.  We will focus on 2, Cisco’s AnyConnect Secure Mobility Client 4.3.03086 and McAfee’s Endpoint Security for Mac 10.2.1.  The choices show up in the GUI as selectable check boxes.

To see these choices from the command line and generate the information we need, we use the installer command with the ‑showChoicesXML option and pass it the package we are interested in.  Depending on the package, we may also need to specify the target, so I generally always do.  Be careful with the target.  Some installers will behave differently if some or all of the software is already installed. Installing on a clean system (maybe a VM you can role back) is a good way to see the real default behavior. Notice in the Cisco installer picture above, VPN and WebSecurity are unchecked and grayed out.  That is because my target already has those pieces installed on it.
Our command is:
installer ‑showChoicesXML ‑pkg /path/to/AnyConnect.pkg ‑target /
That will print a bunch of information to standard out (the terminal window), so I will often redirect it to a file for saving and easier reading/searching like so:
installer ‑showChoicesXML ‑pkg /path/to/AnyConnect.pkg ‑target / > ~/Desktop/anyconnect_choices.xml
That will output something like:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"&gt;
<plist version="1.0">
<array>
<dict>
<key>childItems</key>
<array>
<dict>
<key>childItems</key>
<array/>
<key>choiceDescription</key>
<string>Installs the module that enables VPN capabilities.</string>
<key>choiceIdentifier</key>
<string>choice_vpn</string>
<key>choiceIsEnabled</key>
<false/>
<key>choiceIsSelected</key>
<integer>0</integer>
<key>choiceIsVisible</key>
<true/>
<key>choiceSizeInKilobytes</key>
<integer>15951</integer>
<key>choiceTitle</key>
<string>VPN</string>
<key>pathsOfActivePackagesInChoice</key>
<array>
<string>file://localhost/path/to/AnyConnect.pkg#vpn_module.pkg</string>
</array>
</dict>
<dict>
<key>childItems</key>
<array/>
<key>choiceDescription</key>
<string>Installs the WebSecurity module that enables cloud scanning of web content to protect against malware and enforce acceptable use policies via the ScanSafe cloud proxies.</string>
<key>choiceIdentifier</key>
<string>choice_websecurity</string>
<key>choiceIsEnabled</key>
<false/>
<key>choiceIsSelected</key>
<integer>0</integer>
<key>choiceIsVisible</key>
<true/>
<key>choiceSizeInKilobytes</key>
<integer>2863</integer>
<key>choiceTitle</key>
<string>Web Security</string>
<key>pathsOfActivePackagesInChoice</key>
<array>
<string>file://localhost/path/to/AnyConnect.pkg#websecurity_module.pkg</string>
</array>
</dict>
<dict>
<key>childItems</key>
<array/>
<key>choiceDescription</key>
<string>Installs the AMP Enabler module that downloads and deploys AMP for Endpoints, as configured by the administrator.</string>
<key>choiceIdentifier</key>
<string>choice_fireamp</string>
<key>choiceIsEnabled</key>
<true/>
<key>choiceIsSelected</key>
<integer>1</integer>
<key>choiceIsVisible</key>
<true/>
<key>choiceSizeInKilobytes</key>
<integer>760</integer>
<key>choiceTitle</key>
<string>AMP Enabler</string>
<key>pathsOfActivePackagesInChoice</key>
<array>
<string>file://localhost/path/to/AnyConnect.pkg#fireamp_module.pkg</string>
</array>
</dict>
<dict>
<key>childItems</key>
<array/>
<key>choiceDescription</key>
<string>Installs the diagnostics module that collects AnyConnect Secure Mobility Client troubleshooting information.</string>
<key>choiceIdentifier</key>
<string>choice_dart</string>
<key>choiceIsEnabled</key>
<true/>
<key>choiceIsSelected</key>
<integer>1</integer>
<key>choiceIsVisible</key>
<true/>
<key>choiceSizeInKilobytes</key>
<integer>1964</integer>
<key>choiceTitle</key>
<string>Diagnostics and Reporting Tool</string>
<key>pathsOfActivePackagesInChoice</key>
<array>
<string>file://localhost/path/to/AnyConnect.pkg#dart_module.pkg</string>
</array>
</dict>
<dict>
<key>childItems</key>
<array/>
<key>choiceDescription</key>
<string>Installs the module that provides the AnyConnect Secure Mobility Client with the ability to identify the operating system, antivirus, antispyware, and firewall software installed on the host prior to creating a remote access connection to the secure gateway.</string>
<key>choiceIdentifier</key>
<string>choice_posture</string>
<key>choiceIsEnabled</key>
<true/>
<key>choiceIsSelected</key>
<integer>1</integer>
<key>choiceIsVisible</key>
<true/>
<key>choiceSizeInKilobytes</key>
<integer>13789</integer>
<key>choiceTitle</key>
<string>Posture</string>
<key>pathsOfActivePackagesInChoice</key>
<array>
<string>file://localhost/path/to/AnyConnect.pkg#posture_module.pkg</string>
</array>
</dict>
<dict>
<key>childItems</key>
<array/>
<key>choiceDescription</key>
<string>Installs the module that provides the AnyConnect Secure Mobility Client with the functionality needed to authenticate to wired or wireless networks controlled by the Identity Services Engine, including examination and any needed remediation of the connecting host environment.</string>
<key>choiceIdentifier</key>
<string>choice_iseposture</string>
<key>choiceIsEnabled</key>
<true/>
<key>choiceIsSelected</key>
<integer>1</integer>
<key>choiceIsVisible</key>
<true/>
<key>choiceSizeInKilobytes</key>
<integer>3055</integer>
<key>choiceTitle</key>
<string>ISE Posture</string>
<key>pathsOfActivePackagesInChoice</key>
<array>
<string>file://localhost/path/to/AnyConnect.pkg#iseposture_module.pkg</string>
</array>
</dict>
<dict>
<key>childItems</key>
<array/>
<key>choiceDescription</key>
<string>Installs the Network Visibility Module which collects application telemetry data.</string>
<key>choiceIdentifier</key>
<string>choice_nvm</string>
<key>choiceIsEnabled</key>
<false/>
<key>choiceIsSelected</key>
<integer>0</integer>
<key>choiceIsVisible</key>
<true/>
<key>choiceSizeInKilobytes</key>
<integer>1059</integer>
<key>choiceTitle</key>
<string>Network Visibility</string>
<key>pathsOfActivePackagesInChoice</key>
<array>
<string>file://localhost/path/to/AnyConnect.pkg#nvm_module.pkg</string>
</array>
</dict>
<dict>
<key>childItems</key>
<array/>
<key>choiceDescription</key>
<string>Installs the module that enables Umbrella Roaming Security.</string>
<key>choiceIdentifier</key>
<string>choice_umbrella</string>
<key>choiceIsEnabled</key>
<true/>
<key>choiceIsSelected</key>
<integer>1</integer>
<key>choiceIsVisible</key>
<true/>
<key>choiceSizeInKilobytes</key>
<integer>5412</integer>
<key>choiceTitle</key>
<string>Umbrella Roaming Security</string>
<key>pathsOfActivePackagesInChoice</key>
<array>
<string>file://localhost/path/to/AnyConnect.pkg#umbrella_module.pkg</string>
</array>
</dict>
</array>
<key>choiceIdentifier</key>
<string>__ROOT_CHOICE_IDENT_AnyConnect Secure Mobility Client</string>
<key>choiceIsEnabled</key>
<true/>
<key>choiceIsSelected</key>
<integer>-1</integer>
<key>choiceIsVisible</key>
<true/>
<key>choiceSizeInKilobytes</key>
<integer>0</integer>
<key>choiceTitle</key>
<string>AnyConnect Secure Mobility Client</string>
<key>pathsOfActivePackagesInChoice</key>
<array/>
</dict>
</array>
</plist>

Notice the individual dictionaries:

<dict>
<key>childItems</key>
<array/>
<key>choiceDescription</key>
<string>Installs the module that enables Umbrella Roaming Security.</string>
<key>choiceIdentifier</key>
<string>choice_umbrella</string>
<key>choiceIsEnabled</key>
<true/>
<key>choiceIsSelected</key>
<integer>1</integer>
<key>choiceIsVisible</key>
<true/>
<key>choiceSizeInKilobytes</key>
<integer>5412</integer>
<key>choiceTitle</key>
<string>Umbrella Roaming Security</string>
<key>pathsOfActivePackagesInChoice</key>
<array>
<string>file://localhost/path/to/AnyConnect.pkg#umbrella_module.pkg</string>
</array>
</dict>

This is one of the choices in the package (Umbrella).  Notice that the choiceIsSelected key is set to 1.  This means that by default on this volume (the target from the command), this choice will be installed.  We want to tell the installer command to skip this choice so we want to set choiceIsSelected to 0.  To do that we will create our own xml file that has the values we want.  The keys that we need are the choiceIdentifier, choiceIsSelected, and whether we want the choice to install (set value to 1) or not install (set value to 0).  Our file needs to be valid XML, so we need the appropriate file header.  You can create a valid XML file with the built in defaults command but for this, we can just copy the skeleton of our choices.xml we output from installer.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"&gt;
<plist version="1.0">
<array>
</array>
</plist>
view raw xml_skeleton hosted with ❤ by GitHub

Now we need to add a dictionary for each setting we want to set in this form:

<dict>
<key>attributeSetting</key>
<integer>0</integer>
<key>choiceAttribute</key>
<string>selected</string>
<key>choiceIdentifier</key>
<string>choice_umbrella</string>
</dict>
view raw a_choice.txt hosted with ❤ by GitHub

We need to match the choiceIdentifier values to the those in the output of the installer command.  This AnyConnect package is setup nicely for easy reading.  Not all packages are (more on that later).  Then we have our choiceAttribute key (the key we want to change from default) set to ‘selected’ and finally what we want to set that attribute to in the attributeSetting key (in this case we are setting it to 0 so this choice doesn’t install). We only need to add entries for things we want to change from the default. In this case the choices file specifies settings for all possible choices. Once we build up our file, we get something like:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"&gt;
<plist version="1.0">
<array>
<dict>
<key>attributeSetting</key>
<integer>1</integer>
<key>choiceAttribute</key>
<string>selected</string>
<key>choiceIdentifier</key>
<string>choice_vpn</string>
</dict>
<dict>
<key>attributeSetting</key>
<integer>0</integer>
<key>choiceAttribute</key>
<string>selected</string>
<key>choiceIdentifier</key>
<string>choice_websecurity</string>
</dict>
<dict>
<key>attributeSetting</key>
<integer>0</integer>
<key>choiceAttribute</key>
<string>selected</string>
<key>choiceIdentifier</key>
<string>choice_fireamp</string>
</dict>
<dict>
<key>attributeSetting</key>
<integer>0</integer>
<key>choiceAttribute</key>
<string>selected</string>
<key>choiceIdentifier</key>
<string>choice_dart</string>
</dict>
<dict>
<key>attributeSetting</key>
<integer>0</integer>
<key>choiceAttribute</key>
<string>selected</string>
<key>choiceIdentifier</key>
<string>choice_posture</string>
</dict>
<dict>
<key>attributeSetting</key>
<integer>0</integer>
<key>choiceAttribute</key>
<string>selected</string>
<key>choiceIdentifier</key>
<string>choice_iseposture</string>
</dict>
</array>
</plist>
This file tells installer to install VPN and skip the rest.

Now we are ready to test the package.  To first verify that things look like they will work, we can use the ‑showChoicesAfterApplyingChangesXML option for installer and see what it will try to do.
installer ‑showChoicesAfterApplyingChangesXML /path/to/choicesForAnyConnect.xml ‑pkg /path/to/AnyConnect.pkg ‑target /

<dict>
<key>attributeSetting</key>
<true/>
<key>choiceAttribute</key>
<string>visible</string>
<key>choiceIdentifier</key>
<string>choice_umbrella</string>
</dict>
<dict>
<key>attributeSetting</key>
<true/>
<key>choiceAttribute</key>
<string>enabled</string>
<key>choiceIdentifier</key>
<string>choice_umbrella</string>
</dict>
<dict>
<key>attributeSetting</key>
<integer>0</integer>
<key>choiceAttribute</key>
<string>selected</string>
<key>choiceIdentifier</key>
<string>choice_umbrella</string>
</dict>
In this output we will see more than one entry for each choice.  In this output I have 3, enabled, visible, and selected.  Enabled means will the choice be allowed to be changed in the GUI and visible means will it even show up in the GUI.  We are only worried about selected as we aren’t going to use the GUI.  So confirm that the choices we want to install have selected set to 1 and the choices we want to skip have selected set to 0. Once that all looks correct, we can actually deploy with ‑applyChoiceChangesXML.  That looks like
installer ‑applyChoiceChangesXML /path/to/choicesForAnyConnect.xml ‑pkg /path/to/AnyConnect.pkg ‑target /
This can be packaged up for deployment through a management tool pretty easily.  Basically you want to put the default package and your choices.xml into the packages resources area and then have a post install script that runs the above command.  There is a lot more detail possible, but that might be another blog post.

Cisco’s AnyConnect Secure Mobility installer is pretty friendly when it comes to repackaging/making choices.  If we try to apply the above to the McAfee Endpoint Security 10.2.1 installer, we will see some additional complexity.
installer ‑showChoicesXML ‑pkg /path/to/McAfee_Endpoint_Security\ 10.2.1/1\ Source/McAfee-Endpoint-Security-for-Mac-10.2.1-RTW-standalone-2632.pkg ‑target /

**************************
Running GenerateProductConfig
A higher version of the agent than 5.0.4.283 is already installed. Agent installation will be skipped
OS Name is MacOSX
OS Minor Version is 12
OS Minor Ver is >= 8
FMPInstall check - retval is 0
Checking if we have FMP already installed
FMP higher build number already installed on this machine. So, installer will skip FMP package installation.
UIInstall Check - retval is 0
Checking if we have MSCUI already installed
MSCUI higher build number already installed on this machine. So, installer will skip MSCUI package installation.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"&gt;
<plist version="1.0">
<array>
<dict>
<key>childItems</key>
<array>
<dict>
<key>childItems</key>
<array/>
<key>choiceDescription</key>
<string></string>
<key>choiceIdentifier</key>
<string>installer_choice_1</string>
<key>choiceIsEnabled</key>
<false/>
<key>choiceIsSelected</key>
<integer>1</integer>
<key>choiceIsVisible</key>
<false/>
<key>choiceSizeInKilobytes</key>
<integer>0</integer>
<key>choiceTitle</key>
<string>InstallationManager</string>
<key>pathsOfActivePackagesInChoice</key>
<array>
<string>file://localhost/path/to/McAfee-Endpoint-Security-for-Mac-10.2.1-RTW-standalone-2632.pkg#InstallationManager.pkg</string>
</array>
</dict>
<dict>
<key>childItems</key>
<array/>
<key>choiceDescription</key>
<string></string>
<key>choiceIdentifier</key>
<string>installer_choice_2</string>
<key>choiceIsEnabled</key>
<false/>
<key>choiceIsSelected</key>
<integer>0</integer>
<key>choiceIsVisible</key>
<false/>
<key>choiceSizeInKilobytes</key>
<integer>24530</integer>
<key>choiceTitle</key>
<string>McAfee Agent</string>
<key>pathsOfActivePackagesInChoice</key>
<array>
<string>file://localhost/path/to/McAfee-Endpoint-Security-for-Mac-10.2.1-RTW-standalone-2632.pkg#cma_raw.pkg</string>
</array>
</dict>
<dict>
<key>childItems</key>
<array/>
<key>choiceDescription</key>
<string></string>
<key>choiceIdentifier</key>
<string>installer_choice_3</string>
<key>choiceIsEnabled</key>
<false/>
<key>choiceIsSelected</key>
<integer>0</integer>
<key>choiceIsVisible</key>
<false/>
<key>choiceSizeInKilobytes</key>
<integer>57149</integer>
<key>choiceTitle</key>
<string>FMP</string>
<key>pathsOfActivePackagesInChoice</key>
<array>
<string>file://localhost/path/to/McAfee-Endpoint-Security-for-Mac-10.2.1-RTW-standalone-2632.pkg#FMP.pkg</string>
</array>
</dict>
<dict>
<key>childItems</key>
<array/>
<key>choiceDescription</key>
<string></string>
<key>choiceIdentifier</key>
<string>installer_choice_4</string>
<key>choiceIsEnabled</key>
<true/>
<key>choiceIsSelected</key>
<integer>1</integer>
<key>choiceIsVisible</key>
<true/>
<key>choiceSizeInKilobytes</key>
<integer>127586</integer>
<key>choiceTitle</key>
<string>Threat Prevention</string>
<key>pathsOfActivePackagesInChoice</key>
<array>
<string>file://localhost/path/to/McAfee-Endpoint-Security-for-Mac-10.2.1-RTW-standalone-2632.pkg#AntiMalware.pkg</string>
</array>
</dict>
<dict>
<key>childItems</key>
<array/>
<key>choiceDescription</key>
<string></string>
<key>choiceIdentifier</key>
<string>installer_choice_5</string>
<key>choiceIsEnabled</key>
<true/>
<key>choiceIsSelected</key>
<integer>1</integer>
<key>choiceIsVisible</key>
<true/>
<key>choiceSizeInKilobytes</key>
<integer>20957</integer>
<key>choiceTitle</key>
<string>Firewall</string>
<key>pathsOfActivePackagesInChoice</key>
<array>
<string>file://localhost/path/to/McAfee-Endpoint-Security-for-Mac-10.2.1-RTW-standalone-2632.pkg#StatefulFirewall.pkg</string>
</array>
</dict>
<dict>
<key>childItems</key>
<array/>
<key>choiceDescription</key>
<string></string>
<key>choiceIdentifier</key>
<string>installer_choice_6</string>
<key>choiceIsEnabled</key>
<true/>
<key>choiceIsSelected</key>
<integer>1</integer>
<key>choiceIsVisible</key>
<true/>
<key>choiceSizeInKilobytes</key>
<integer>18130</integer>
<key>choiceTitle</key>
<string>Web Control</string>
<key>pathsOfActivePackagesInChoice</key>
<array>
<string>file://localhost/path/to/McAfee-Endpoint-Security-for-Mac-10.2.1-RTW-standalone-2632.pkg#WebProtection.pkg</string>
</array>
</dict>
<dict>
<key>childItems</key>
<array/>
<key>choiceDescription</key>
<string></string>
<key>choiceIdentifier</key>
<string>installer_choice_7</string>
<key>choiceIsEnabled</key>
<false/>
<key>choiceIsSelected</key>
<integer>0</integer>
<key>choiceIsVisible</key>
<false/>
<key>choiceSizeInKilobytes</key>
<integer>14239</integer>
<key>choiceTitle</key>
<string>UIFramework</string>
<key>pathsOfActivePackagesInChoice</key>
<array>
<string>file://localhost/path/to/McAfee-Endpoint-Security-for-Mac-10.2.1-RTW-standalone-2632.pkg#MSCUI.pkg</string>
</array>
</dict>
</array>
<key>choiceIdentifier</key>
<string>__ROOT_CHOICE_IDENT_DISTRIBUTION_TITLE</string>
<key>choiceIsEnabled</key>
<true/>
<key>choiceIsSelected</key>
<integer>-1</integer>
<key>choiceIsVisible</key>
<true/>
<key>choiceSizeInKilobytes</key>
<integer>0</integer>
<key>choiceTitle</key>
<string>McAfee Endpoint Security for Mac</string>
<key>pathsOfActivePackagesInChoice</key>
<array/>
</dict>
</array>
</plist>
Notice that the choiceIdentifier is the not so helpful installer_choice_5.  We can still see what the choice is by the choiceTitle, but now when we create our choices.xml, we need to be very careful about which choices we are selecting and which we are not.  Your end result will look something like:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"&gt;
<plist version="1.0">
<array>
<dict>
<key>attributeSetting</key>
<integer>0</integer>
<key>choiceAttribute</key>
<string>selected</string>
<key>choiceIdentifier</key>
<string>installer_choice_5</string>
</dict>
<dict>
<key>attributeSetting</key>
<integer>0</integer>
<key>choiceAttribute</key>
<string>selected</string>
<key>choiceIdentifier</key>
<string>installer_choice_6</string>
</dict>
</array>
</plist>

In this file, installer_choice_5 is the Firewall and installer_choice_6 is Web Control. For this type of package it can be important to save the output of showChoicesXML as a reference for which choice is which. This is an example of only putting in dictionaries for the choices we want to change.  I would say this is probably not a best practice as different systems can end up with different defaults, but it can work.

9 thoughts on “Using installer choices.xml to modify AnyConnect and McAfee deployments

  1. I’ve been looking for this information for exactly these two .pkgs! This is great. Thank very much for it. I’ll work on it tomorrow!
    – Scott
    Rogers Communications MacAnalyst

  2. Pingback: Creating a wrapper package for a choices.xml file | My Thoughts

  3. Time has passed and now the AnyConnect installer has more choices. Here’s an updated choices.xml file, for version 4.8, that excludes all but the VPN:

    attributeSetting
    1
    choiceAttribute
    selected
    choiceIdentifier
    choice_vpn

    attributeSetting
    0
    choiceAttribute
    selected
    choiceIdentifier
    choice_websecurity

    attributeSetting
    0
    choiceAttribute
    selected
    choiceIdentifier
    choice_fireamp

    attributeSetting
    0
    choiceAttribute
    selected
    choiceIdentifier
    choice_dart

    attributeSetting
    0
    choiceAttribute
    selected
    choiceIdentifier
    choice_posture

    attributeSetting
    0
    choiceAttribute
    selected
    choiceIdentifier
    choice_iseposture

    attributeSetting
    0
    choiceAttribute
    selected
    choiceIdentifier
    choice_nvm

    attributeSetting
    0
    choiceAttribute
    selected
    choiceIdentifier
    choice_umbrella

  4. Really struggling with this… I’m not sure what I’m doing wrong but packaging seems to be really confusing.. I can run the installer with the files, all works as expected but as soon as it comes to packaging this up… despite following the above instructions, I get errors.

  5. I’ve setup my XML to disable all but the VPN, however after using the ‑showChoicesAfterApplyingChangesXML option, it comes back as showing the VPN selection as 0. I can’t figure out why my XML choice file isn’t being applied

  6. Pingback: McAfee macOS Configuration Profiles - EddiesNotes.com

  7. Pingback: Using AutoPkg to build a Cisco AnyConnect installer | Der Flounder

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s