Skip to main content
Question

Cisco Secure Client 5 Install for Jamf

  • April 24, 2024
  • 5 replies
  • 386 views

Forum|alt.badge.img+4

The documentation from Cisco is pretty abysmal, it took me hours to finally make sense of it all. I made my own guide and an all-in-one config profile to use for deployment via Jamf. I hope this helps anyone in the future. 

 

https://scribehow.com/shared/Create_Cisco_Secure_Client_Jamf_Installer_pkg__ODli2lu5QiyylY4S5aCvMQ

5 replies

Forum|alt.badge.img+4
  • Author
  • New Contributor
  • April 24, 2024

AJPinto
Forum|alt.badge.img+26
  • Legendary Contributor
  • April 25, 2024

Doing the lord's work. Err, I mean vendors work.

 

Cisco's documentation has been garbage as long as I can remember. I am very happy my organization finally dumped cisco last year.


Forum|alt.badge.img+4
  • Author
  • New Contributor
  • April 26, 2024

Doing the lord's work. Err, I mean vendors work.

 

Cisco's documentation has been garbage as long as I can remember. I am very happy my organization finally dumped cisco last year.


Thank you! My MSP chooses vendors who are Windows-First and it drives me nuts. Most of them have awful documentation and support for Macs, but this was just awful. I spent about 8 hours of straight work to make sense of it all, so I hope someone furiously Googling (like me) will come across this someday! 


Bretterson
Forum|alt.badge.img+4
  • New Contributor
  • April 22, 2026

The documentation from Cisco is pretty abysmal, it took me hours to finally make sense of it all. I made my own guide and an all-in-one config profile to use for deployment via Jamf. I hope this helps anyone in the future. 

 

https://scribehow.com/shared/Create_Cisco_Secure_Client_Jamf_Installer_pkg__ODli2lu5QiyylY4S5aCvMQ

This is so much better. Well done. That said, I didn’t want to go this route because it seemed really tedious/annoying to have to rebuild it for each new release. @talkingmoose shared a really nice script with me on another thread that I was able to use to automate the entire installation process (I’ve since had success using it for other installs that work with a choices XML file). Now all I have to do when an update is released is copy the package and upload it to Jamf.

Here’s a basic run-through:

  1. Copy the package from the Cisco Secure Client DMG to a local folder. Upload it to Jamf, as-is.
  2. Open Terminal and run: 
    installer -pkg "/private/tmp/Cisco Secure Client.pkg" -showChoiceChangesXML > ~/Downloads/install_choices.xml
  3. Open the XML file you just created in your text editor of choice.
  4. Change the integer value for any module you don’t want to install to 0, leave it as 1 for all the modules you do want (we only wanted Umbrella, but I’m pretty sure you need to install choice_anyconnect_vpn regardless):
    <dict>
    <key>attributeSetting</key>
    <integer>1</integer>
    <key>choiceAttribute</key>
    <string>selected</string>
    <key>choiceIdentifier</key>
    <string>choice_secure_umbrella</string>
    </dict>
  5. Create a new script in Jamf and name it. Copy the raw text of the Deploy Choices XML.zsh script from here: https://gist.github.com/talkingmoose/3926e86332e32eb7d05a161c3f7e8f69
  6. Since I’ve used this script in other policies, I replaced the value of the installerPackage variable on line 45 to use a Jamf parameter. I also replaced every mention of TeamViewer (lines 79 and 90) with a parameter. This way you can have multiple policies point at one script and simply enter the name of the package and application. For this particular installation I use a dedicated script because of other additions in subsequent steps that are unique to Cisco Secure Client.

    I’m going to chop this up just to keep the code block shorter because I find it annoying when I have to scroll through a bunch of them on a page (I’ll include the full thing at the end of the post):
    # provide the name of an Apple Installer package file that supports Choices XML
    installerPackage="$4"

    -------

    function cleanup() {
    /bin/rm -Rf "$tempDirectory"
    logmessage "Removed temporary items." "Failed removing temporary items."
    /bin/rm -f "/Library/Application Support/JAMF/Waiting Room/$installerPackage" && /bin/rm -Rf "/Library/Application Support/JAMF/Waiting Room/$installerPackage.cache.xml"
    logmessage "Removed $5 package and supporting files from Jamf Waiting Room." "Failed Removing $5 package and supporting files from Jamf Waiting Room."
    }

    -------

    /usr/sbin/installer -pkg "/Library/Application Support/JAMF/Waiting Room/$installerPackage" -applyChoiceChangesXML "$tempDirectory/choices.xml" -target /
    logmessage "Installed $5 package with choices." "Failed to install $5 package with choices."
  7. Replace lines 49-62 with the full contents of the XML file from step 5. I’m going to cut a big chunk of the text here but, again, I’ll include the full thing at the end:
    # modify the choiceIdentifier string with the app identifier (CFBundleIdentifier) to exclude from installation
    # duplicate the full dictionary to include additional apps
    choicesXML='<?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">
    <plist version="1.0">
    <array>
    <dict>
    <key>attributeSetting</key>
    <true/>
    <key>choiceAttribute</key>
    <string>visible</string>
    <key>choiceIdentifier</key>
    <string>choice_anyconnect_vpn</string>
    </dict>

    ------

    <dict>
    <key>attributeSetting</key>
    <integer>0</integer>
    <key>choiceAttribute</key>
    <string>selected</string>
    <key>choiceIdentifier</key>
    <string>choice_zta</string>
    </dict>
    </array>
    </plist>'
  8. We aren’t using the VPN, so I added stuff to have the script take care of that as well.
    I put this bit immediately after the block from step 7:
    # Contents for ACTransforms.xml
    ACTransforms='<Transforms>
    <DisableVPN>true</DisableVPN>
    <DisableCustomerExperienceFeedback>true</DisableCustomerExperienceFeedback>
    </Transforms>'

    And this between the cleanup function block and the “create temporary working directory” line (line 82 in the original):

    # create ACTransforms.xml
    echo "$ACTransforms" > "/Library/Application Support/JAMF/Waiting Room/ACTransforms.xml"
    logmessage "Created ACTransforms.xml file in JAMF Waiting Room." "Created ACTransforms.xml file in JAMF Waiting Room."
  9. I have it generating the OrgInfo.json file as well! Add this before the line where cleanup is called (originally line 97):
    # create the OrgInfo.json file for Umbrella
    echo '{
    "organizationId" : "ORG_ID_HERE",
    "fingerprint" : "FINGERPRINT_HERE",
    "userId" : "USER_ID_HERE"
    }' >> /opt/cisco/secureclient/umbrella/OrgInfo.json
    logmessage "Created OrgInfo.json file." "Failed to create OrgInfo.json file."
  10. We don’t have a need for the Secure Client UI at all, so I completely remove the launch agent (also means no icon on the Menu Bar or Dock). I have this right after the block from step 9:
    # unload Secure Client launch agent, quit the UI, then delete agent to eliminate Menu Bar and Dock icon
    processName="Cisco Secure Client"
    processID=$(pgrep $processName)
    plistPath=/Library/LaunchAgents/com.cisco.secureclient.gui.plist
    loggedInUser=$( scutil <<< "show State:/Users/ConsoleUser" | awk '/Name :/ && ! /loginwindow/ { print $3 }' )
    currentID=$(id -u ${loggedInUser})

    kill $processID

    if [ -f $plistPath ]; then
    echo "$processName launch agent has been located, unloading it now..."
    launchctl bootout gui/${currentID}/com.cisco.secureclient.gui && echo "Successfully unloaded the agent, will proceed to removal"
    else
    echo "$processName launch agent is NOT loaded, removing the agents now..."
    fi

    launchctl disable gui/${currentID}/com.cisco.secureclient.gui && echo "Successfully disabled the agent"
    rm -f /Library/LaunchAgents/com.cisco.secureclient.gui.plist
    You can remove the last line if you’d like the option to reenable the agent later.
    Ok, done with the script itself!
  11. Go to the Options tab at the top. I’m not sure if this one matters a ton, but I set the Priority to “After”.
    For the Parameter Labels I have “filename of installer package” in number 4 and “name of application” in number 5.
    Save it!
  12. Create a new policy and name it. Set the Triggers and Execution Frequency to your liking. Go to the Packages section and add the package that was uploaded in step 1.
    IMPORTANT!: Change the Action option to “Cache”.
  13. Go to the Scripts section and add the script. Change the Priority to “After”.
    Enter the name of the ACTUAL filename of the package uploaded in step 1. (I rename it to “Cisco Secure Client.pkg" before uploading so I never have to change it in the policy, but it’s up to you.)
    Enter the name of the application (I have “Cisco Secure Client”).
  14. I set it to update inventory in the Maintenance section, but that’s optional.
  15. Scope it to fit your needs and add it to Self Service if you like.
  16. Follow Cisco’s steps to create a configuration profile that will silently approve the content filters and System Extension: https://www.cisco.com/c/en/us/td/docs/security/vpn_client/anyconnect/Cisco-Secure-Client-5/admin/guide/b-cisco-secure-client-admin-guide-5-1/macos11-on-ac.html#Cisco_Reference.dita_1548760e-fe64-4a35-a644-b74649d2b6cf

    I also add a Notifications payload to auto-allow them.
    I deploy the profile to everyone and have a smart group that looks for machines that have the profile. I scope the policy to that group.
  17. If you’re using Umbrella then you’ll also need to deploy the root certificate in a profile (I have no idea where it comes from, it was provided to me). Once you have it you can either add it to the profile from step 16 or make a separate one (ours is separate, I don’t remember why). Just add the Certificate payload to a profile and give it a name. Select Upload and then click Upload Certificate to select your file.
  18. DONE! YAY!

Bretterson
Forum|alt.badge.img+4
  • New Contributor
  • April 22, 2026

Here’s my final script:

#!/bin/zsh

:<<ABOUT_THIS_SCRIPT
-----------------------------------------------------------------------

Written by: William Smith
Partner Program Manager
Jamf
bill@talkingmoose.net
https://gist.github.com/talkingmoose/3926e86332e32eb7d05a161c3f7e8f69

Originally posted: May 5, 2023

Purpose: Deploys a Choices XML file for use with the specified Apple
Installer package.

Instructions:

1. Create a new script in Jamf Pro with the entirety of
this script.

2. Edit the installerPackage name on line 45 with the name of the
Apple installer package you're deploying.

3. Edit the app bundle identifier on line 59 with the CFBundleIdentifier
of the app you wish to prevent installing. To add a second app,
duplicate the entire dictionary ( from <dict> to </dict> )
and edit its choiceIdentifier string.

4. In a new Jamf Pro policy, add both the package and the script.

5. Set the package to Cache not Install.

6. Add any additional policy settings you need, scope, and deploy.

Except where otherwise noted, this work is licensed under
http://creativecommons.org/licenses/by/4.0/

“A problem well stated is a problem half solved."

-----------------------------------------------------------------------
ABOUT_THIS_SCRIPT

# provide the name of an Apple Installer package file that supports Choices XML
installerPackage="$4"

# modify the choiceIdentifier string with the app identifier (CFBundleIdentifier) to exclude from installation
# duplicate the full dictionary to include additional apps
choicesXML='<?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">
<plist version="1.0">
<array>
<dict>
<key>attributeSetting</key>
<true/>
<key>choiceAttribute</key>
<string>visible</string>
<key>choiceIdentifier</key>
<string>choice_anyconnect_vpn</string>
</dict>
<dict>
<key>attributeSetting</key>
<false/>
<key>choiceAttribute</key>
<string>enabled</string>
<key>choiceIdentifier</key>
<string>choice_anyconnect_vpn</string>
</dict>
<dict>
<key>attributeSetting</key>
<integer>1</integer>
<key>choiceAttribute</key>
<string>selected</string>
<key>choiceIdentifier</key>
<string>choice_anyconnect_vpn</string>
</dict>
<dict>
<key>attributeSetting</key>
<true/>
<key>choiceAttribute</key>
<string>visible</string>
<key>choiceIdentifier</key>
<string>choice_fireamp</string>
</dict>
<dict>
<key>attributeSetting</key>
<false/>
<key>choiceAttribute</key>
<string>enabled</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_fireamp</string>
</dict>
<dict>
<key>attributeSetting</key>
<true/>
<key>choiceAttribute</key>
<string>visible</string>
<key>choiceIdentifier</key>
<string>choice_dart</string>
</dict>
<dict>
<key>attributeSetting</key>
<false/>
<key>choiceAttribute</key>
<string>enabled</string>
<key>choiceIdentifier</key>
<string>choice_dart</string>
</dict>
<dict>
<key>attributeSetting</key>
<integer>1</integer>
<key>choiceAttribute</key>
<string>selected</string>
<key>choiceIdentifier</key>
<string>choice_dart</string>
</dict>
<dict>
<key>attributeSetting</key>
<true/>
<key>choiceAttribute</key>
<string>visible</string>
<key>choiceIdentifier</key>
<string>choice_secure_firewall_posture</string>
</dict>
<dict>
<key>attributeSetting</key>
<false/>
<key>choiceAttribute</key>
<string>enabled</string>
<key>choiceIdentifier</key>
<string>choice_secure_firewall_posture</string>
</dict>
<dict>
<key>attributeSetting</key>
<integer>0</integer>
<key>choiceAttribute</key>
<string>selected</string>
<key>choiceIdentifier</key>
<string>choice_secure_firewall_posture</string>
</dict>
<dict>
<key>attributeSetting</key>
<true/>
<key>choiceAttribute</key>
<string>visible</string>
<key>choiceIdentifier</key>
<string>choice_iseposture</string>
</dict>
<dict>
<key>attributeSetting</key>
<false/>
<key>choiceAttribute</key>
<string>enabled</string>
<key>choiceIdentifier</key>
<string>choice_iseposture</string>
</dict>
<dict>
<key>attributeSetting</key>
<integer>0</integer>
<key>choiceAttribute</key>
<string>selected</string>
<key>choiceIdentifier</key>
<string>choice_iseposture</string>
</dict>
<dict>
<key>attributeSetting</key>
<true/>
<key>choiceAttribute</key>
<string>visible</string>
<key>choiceIdentifier</key>
<string>choice_nvm</string>
</dict>
<dict>
<key>attributeSetting</key>
<false/>
<key>choiceAttribute</key>
<string>enabled</string>
<key>choiceIdentifier</key>
<string>choice_nvm</string>
</dict>
<dict>
<key>attributeSetting</key>
<integer>0</integer>
<key>choiceAttribute</key>
<string>selected</string>
<key>choiceIdentifier</key>
<string>choice_nvm</string>
</dict>
<dict>
<key>attributeSetting</key>
<true/>
<key>choiceAttribute</key>
<string>visible</string>
<key>choiceIdentifier</key>
<string>choice_secure_umbrella</string>
</dict>
<dict>
<key>attributeSetting</key>
<false/>
<key>choiceAttribute</key>
<string>enabled</string>
<key>choiceIdentifier</key>
<string>choice_secure_umbrella</string>
</dict>
<dict>
<key>attributeSetting</key>
<integer>1</integer>
<key>choiceAttribute</key>
<string>selected</string>
<key>choiceIdentifier</key>
<string>choice_secure_umbrella</string>
</dict>
<dict>
<key>attributeSetting</key>
<true/>
<key>choiceAttribute</key>
<string>visible</string>
<key>choiceIdentifier</key>
<string>choice_thousandeyes</string>
</dict>
<dict>
<key>attributeSetting</key>
<false/>
<key>choiceAttribute</key>
<string>enabled</string>
<key>choiceIdentifier</key>
<string>choice_thousandeyes</string>
</dict>
<dict>
<key>attributeSetting</key>
<integer>0</integer>
<key>choiceAttribute</key>
<string>selected</string>
<key>choiceIdentifier</key>
<string>choice_thousandeyes</string>
</dict>
<dict>
<key>attributeSetting</key>
<false/>
<key>choiceAttribute</key>
<string>visible</string>
<key>choiceIdentifier</key>
<string>choice_duo</string>
</dict>
<dict>
<key>attributeSetting</key>
<true/>
<key>choiceAttribute</key>
<string>enabled</string>
<key>choiceIdentifier</key>
<string>choice_duo</string>
</dict>
<dict>
<key>attributeSetting</key>
<integer>0</integer>
<key>choiceAttribute</key>
<string>selected</string>
<key>choiceIdentifier</key>
<string>choice_duo</string>
</dict>
<dict>
<key>attributeSetting</key>
<true/>
<key>choiceAttribute</key>
<string>visible</string>
<key>choiceIdentifier</key>
<string>choice_zta</string>
</dict>
<dict>
<key>attributeSetting</key>
<false/>
<key>choiceAttribute</key>
<string>enabled</string>
<key>choiceIdentifier</key>
<string>choice_zta</string>
</dict>
<dict>
<key>attributeSetting</key>
<integer>0</integer>
<key>choiceAttribute</key>
<string>selected</string>
<key>choiceIdentifier</key>
<string>choice_zta</string>
</dict>
</array>
</plist>'

# Contents for ACTransforms.xml
ACTransforms='<Transforms>
<DisableVPN>true</DisableVPN>
<DisableCustomerExperienceFeedback>true</DisableCustomerExperienceFeedback>
</Transforms>'

function logmessage() {
if [ $? = 0 ] ; then
echo "$1"
else
echo "$2"
echo "Aborting script"
cleanup
exit 1
fi
}

function cleanup() {
/bin/rm -Rf "$tempDirectory"
logmessage "Removed temporary items." "Failed removing temporary items."
/bin/rm -f "/Library/Application Support/JAMF/Waiting Room/$installerPackage" && /bin/rm -Rf "/Library/Application Support/JAMF/Waiting Room/$installerPackage.cache.xml" && /bin/rm -Rf "/Library/Application Support/JAMF/Waiting Room/ACTransforms.xml"
logmessage "Removed $5 package and supporting files from Jamf Waiting Room." "Failed to remove $5 package and supporting files from Jamf Waiting Room."
}

# create ACTransforms.xml
echo "$ACTransforms" > "/Library/Application Support/JAMF/Waiting Room/ACTransforms.xml"
logmessage "Created ACTransforms.xml file in JAMF Waiting Room." "Created ACTransforms.xml file in JAMF Waiting Room."

# create temporary working directory
workDirectory=$( /usr/bin/basename $0 )
tempDirectory=$( /usr/bin/mktemp -d "/private/tmp/$workDirectory.XXXXXX" )
logmessage "Created working directory '$tempDirectory'." "Failed to create working directory '$tempDirectory'."

# change directory to temporary working directory
cd "$tempDirectory"
logmessage "Changed directory to working directory '$tempDirectory'." "Failed to change directory to working directory '$tempDirectory'."

echo "$choicesXML" > "$tempDirectory/choices.xml"
logmessage "Created choices.xml file in '$tempDirectory'." "Created choices.xml file in '$tempDirectory'."

/usr/sbin/installer -pkg "/Library/Application Support/JAMF/Waiting Room/$installerPackage" -applyChoiceChangesXML "$tempDirectory/choices.xml" -target /
logmessage "Installed $5 package with choices." "Failed to install $5 package with choices."

# create the OrgInfo.json file for Umbrella
echo '{
"organizationId" : "ORG_ID_HERE",
"fingerprint" : "FINGERPRINT_HERE",
"userId" : "USER_ID_HERE"
}' >> /opt/cisco/secureclient/umbrella/OrgInfo.json
logmessage "Created OrgInfo.json file." "Failed to create OrgInfo.json file."

# quit the UI, unload Secure Client launch agent, then delete agent to eliminate Menu Bar and Dock icon
processName="Cisco Secure Client"
processID=$(pgrep $processName)
plistPath=/Library/LaunchAgents/com.cisco.secureclient.gui.plist
loggedInUser=$( scutil <<< "show State:/Users/ConsoleUser" | awk '/Name :/ && ! /loginwindow/ { print $3 }' )
currentID=$(id -u ${loggedInUser})

kill $processID

if [ -f $plistPath ]; then
echo "$processName launch agent has been located, unloading it now..."
launchctl bootout gui/${currentID}/com.cisco.secureclient.gui && echo "Successfully unloaded the agent, will proceed to removal"
else
echo "$processName launch agent is NOT loaded, removing the agents now..."
fi

launchctl disable gui/${currentID}/com.cisco.secureclient.gui && echo "Successfully disabled the agent"
rm -f /Library/LaunchAgents/com.cisco.secureclient.gui.plist

###

cleanup

exit 0