fleetdm / fleet

Open-source platform for IT, security, and infrastructure teams. (Linux, macOS, Chrome, Windows, cloud, data center)
https://fleetdm.com
Other
3.17k stars 433 forks source link

Fleet-maintained apps for macOS #18865

Closed noahtalerman closed 1 week ago

noahtalerman commented 6 months ago

Goal

User story
As an IT admin,
I want to select a Fleet-maintained app
so that I can install the app on my macOS hosts w/o having to upload a package on my own.

Context

This is user story applies to this Fleet Q2 OKR:

Changes

Product

Engineering

ℹ️  Please read this issue carefully and understand it. Pay special attention to UI wireframes, especially "dev notes".

QA

Risk assessment

Manual testing steps

Test Fleet UI changes -

End to End testing -

Testing notes

@noahtalerman: We learned that some Microsoft apps (ex. Excel) can point to an XML configuration file at install time to enable/disable certain settings. More info in the "Homebrew format" section in the Google doc here.

We decided to not point to XML in the default install script for these apps because we think the apps can still be installed and used by the end user w/o it. And, we can add Fleet feature for this later.

To be sure, as part of this story, we want to test the following:

As a result

Confirmation

  1. [ ] Engineer (@____): Added comment to user story confirming successful completion of QA.
  2. [x] QA (@PezHub ): Added comment to user story confirming successful completion of QA.
noahtalerman commented 6 months ago

User stories are derived from the workflows and problems we want to solve. These are documented here in the public Google doc: https://docs.google.com/document/d/13_xJzKldKiSbRknsDFADIf3sESNHapj-7D24Hyu4qro/edit

noahtalerman commented 6 months ago

This story is related to the "Automate Zoom updates" story (#18961).

The user story "Automate Zoom updates" story will be addressed by this story.

marko-lisica commented 6 months ago

Hey @dherder, we're missing customer/prospect labels for this one. Could you please add labels when you get a chance?

dherder commented 6 months ago

@marko-lisica this looks to be a duplicate of https://github.com/fleetdm/fleet/issues/17129

marko-lisica commented 6 months ago

Thanks @dherder! I think we should keep both, since this one will be focused on software install in case of policy failure. @noahtalerman What's your take on this?

noahtalerman commented 6 months ago

I think we should keep both

Agreed.

@dherder this story enables this workflow: policy failure => trigger software install (software you've previously uploaded to Fleet). No Tines needed.

I think #17129 is similar but for script: policy failure => trigger script. No Tines needed.

What do you think? Any feedback?

dherder commented 6 months ago

sounds good to me, @noahtalerman.

marko-lisica commented 5 months ago

@dherder This one didn't make to estimation. We plan to prioritize this in the next design sprint.

JoStableford commented 5 months ago

Related to a Slack conversation

spokanemac commented 5 months ago

Relying on policies to update software creates a lot of overhead in this process. We should be smart enough to determine the version on disk, and if it's \< version Fleet has, then install it. This would enable merely uploading new software versions without updating a related policy. Related: fleetdm/confidential#6916

noahtalerman commented 5 months ago

From @pacamaster:

Include “edit” either package or pre/post-install scripts - currently need to delete and re-add versioning of packages that already exist/upgrading and management of packages

noahtalerman commented 5 months ago

Hey @marko-lisica, just had a call w/ a prospect and they showed us the app catalog (aka auto-apps) feature from another MDM solution. More evidence that this is where we should be headed.

Gong recording is here (internal): https://us-65885.app.gong.io/call?id=3559600865527920986

dherder commented 5 months ago

@noahtalerman do you know of an existing FR for establishment of a Fleet managed app catalog?

lukeheath commented 5 months ago

@noahtalerman @marko-lisica Why are we choosing to tie software patches to failing policies? That requires a distinct policy for every software item that would have to be maintained separately. Have we considered using a more open standardize format like recipes?

cc @spokanemac

lukeheath commented 5 months ago

@noahtalerman Just watched the Gong video you referenced showing app catalog. I think under the hood, they are using something like Autopkg + recipes. That's what allows you to select software without uploading an installer, and also select "auto update". With this type of system there is no need to have a failing policy because the recipe itself indicates when a change is required, and what Fleet should do is defined in the app catalog (i.e. automatically update, manually hold on a specific version, etc.). So, in this workflow, a failing policy would be unnecessary and a repetitive assertion.

From a dogfooding perspective, it's a huge task and not sustainable for me to go download and upload new binaries to Fleet every time a new version of any software comes out. Recipes exist so I can declaratively set how I want Fleet to behave, and then it handles the rest for me.

georgekarrv commented 5 months ago

Do we want to build on a third party tool like Autopkg? looks like it's python based and requires git (since they clone repo's to manage installs and updates)

I'm not 100% sure what it would take to integrate this atm.

noahtalerman commented 5 months ago

Why are we choosing to tie software patches to failing policies?

Hey @lukeheath this is no longer the plan. At first, this was the plan to take baby steps towards a full blow Fleet library. Now the plan is to design the full blown Fleet library and then cut it down to fit it into one sprint.

I updated the user story to reflect this.

I think under the hood, they are using something like Autopkg + recipes.

That's what I thought too but they might not be according to @nonpunctual. Chatted about this w/ briefly during product office hours.

noahtalerman commented 5 months ago

FYI @marko-lisica ^

nonpunctual commented 5 months ago

@lukeheath @noahtalerman @marko-lisica I am not saying they are repackaging everything, because they have to maintain signatures, but, what they used to do was repackage everything.

The way to tell would be to somehow get the packages they are installing from the temp locations during install & inspect them to see what the signatures are. It's possible they are getting things from 3rd party sources.

Another tool to look at is this: https://patchdocs.alectrona.com/docs/welcome

The binary I created for this at previous company worked more like installomator. I've shared it w Roberto. I'll probably never use it now so here's the script-y version. :) What does it have?

#!/bin/bash
#shellcheck disable=SC2028,SC2034,SC2154,SC2207

# DownSide.sh
# © 2022, Brock Walters, ... All rights reserved.
# version: 17

### MIT LICENSE ###

#  PERMISSION IS HEREBY GRANTED, FREE OF CHARGE, TO ANY PERSON OBTAINING A COPY OF THIS SOFTWARE AND ASSOCIATED
#  DOCUMENTATION FILES (THE "SOFTWARE"), TO DEAL IN THE SOFTWARE WITHOUT RESTRICTION, INCLUDING WITHOUT LIMITATION
#  THE RIGHTS TO USE, COPY, MODIFY, MERGE, PUBLISH, DISTRIBUTE, SUBLICENSE, AND/OR SELL COPIES OF THE SOFTWARE,
#  AND TO PERMIT PERSONS TO WHOM THE SOFTWARE IS FURNISHED TO DO SO, SUBJECT TO THE FOLLOWING CONDITIONS:
# 
#  THE ABOVE COPYRIGHT NOTICE AND THIS PERMISSION NOTICE SHALL BE INCLUDED IN ALL COPIES OR SUBSTANTIAL PORTIONS
#  OF THE SOFTWARE.
# 
#  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
#  TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
# 
#  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
#  IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
#  OR OTHER DEALINGS IN THE SOFTWARE.

#  !!! WARNING !!!

#  Applications & packages (along with their certificates & page sources) MUST BE INSPECTED & TESTED BEFORE BEING
#  DEPLOYED WITH A MANAGEMENT SYSTEM. DO NOT USE THIS TOOL TO DEPLOY SOFTWARE WITHOUT VALIDATING ITS DOWNLOAD SOURCE
#  & TESTING INSTALLATION. "Suspicious Package" & "Apparency" are free tools for inspecting applications & packages
#  available from Mothers Ruin Software @ https://www.mothersruin.com 

### SYNOPSIS ###

#  If the variables in the INSTALLATION table below are correctly populated this script will download & install the
#  most current version of the publicly available macOS application or binary executable specified.
# 
#  No modifications to the script are needed other than populating the INSTALLATION variables correctly. Doing so is
#  not trivial. To see examples execute the script with the help argument, e.g., "./DownSide --help".
# 
#  Default behavior:
#
#  Installation proceeds if the application is not currently installed.
# 
#  A version comparison is performed if the application is being updated (as opposed to being installed for the first
#  time.) If the installed application version matches or is newer than the currently available application download
#  version (e.g., a beta version) installation is aborted & the script will exit.
#
#  Validated file container attributes:
#
#   • Disk Images
#   
#       Checksum of the "read-only" or "compressed" image compared against the value stored in the image
#
#   • Other containers
#
#       The downloaded file matches the macOS file type

### OPTIONS ###

#  1) Attempt to validate the downloaded application.
# 
#  Validated file attributes:
#   
#   • Application bundles:
#   
#       Executable files exist in "$apppath"/Contents/MacOS/
#   
#       The application bundle identifier is written to "$apppath"/Contents/Info.plist
#           
#   • Binary executables:
#   
#       "$apppath" is an executable file
#   
#   • macOS Package Installers
#   
#       Package file contains an inspectable bom
#   
#       Package file is an xar archive or bundle that can be executed by the macOS installer binary
#    
#  Validated code signing attributes:
#    
#   • Apple Developer Certificate team identifier & developer name
#       https://developer.apple.com/support/developer-id/
#    
#   • Apple Developer Certificate signature status
#       https://developer.apple.com/support/code-signing/
#       https://developer.apple.com/documentation/xcode/using-the-latest-code-signature-format
#    
#   • Notarization
#       https://developer.apple.com/documentation/security/notarizing_macos_software_before_distribution
#    
#  If the dwnhash variable is null hash validation will be skipped. If an md5 hash is available from the download
#  source use it to populate the dwnhash variable. A comparison will be made to the md5 hash of the download.
#    
#  If any attribute fails validation installation is aborted & the script will exit. If, after a manual inspection,
#  a download is found to be safe disable validation to install.
#    
#       To use validation set the appchck variable to "enabled". Set "appchck" TO ANYTHING OTHER THAN "enabled"
#       to disable (e.g., appchck='' or appchck='disabled')
#    
#  2) Attempt to prevent installation of the application if it is open on the target computer.
#    
#  The script checks for running processes related to the specified application & presents deferral options via
#  jamfhelper to the currently logged-in user (assuming that the script is executed by a Jamf policy.) If the
#  application is open & deferral is disabled installation is aborted & the script will exit.
#       
#       To use deferral set the appdfrl variable to "enabled". Set "appdfrl" TO ANYTHING OTHER THAN "enabled"
#       to disable (e.g., appdfrl='' or appdfrl='disabled')

### TABLE: INSTALLATION ###

# | STRING  | DESCRIPTION               | NOTES
# | appname | display name              | exact match of the displayed application name in the macOS GUI after installation
# | applurl | page source / web site    | the application page source will not necessarily have an easily obtained download URL
# | crntvrs | current version           | the application page source will not necessarily have easily obtained version information
# | appdown | download URL              | the application download URL may be complex & require concatenating strings from multiple sources
# | appctnr | container                 | often, but not always, a flat file containing the application or application installer
# | apppath | bundle / file / installer | if an .app or .pkg or binary file can be directly downloaded set apppath to value of appctnr

### TABLE: VALIDATION ###

# | STRING  | DESCRIPTION          | NOTES
# | appchck | enable validation    | switch for enabling or disabling application validation
# | bndlstr | bundle identifier    | bundle identifier string from the installed application Info.plist (CFBundleIdentifier)
# | crtdvnm | developer name       | the organization to which the Apple Developer certificate was issued
# | crtteam | team identifier      | team identifier string found in software publisher's Apple Developer Certificate
# | dwnhash | downloaded file hash | an entered md5 hash value that is compared to an md5 hash of the download (optional)
# | infovrs | installed version    | key label for the installed application Info.plist value that matches the current available version

### TABLE: DEFERRAL ###

# | STRING  | DESCRIPTION          | NOTES
# | appdfrl | enable deferral      | switch for enabling or disabling interactive deferral for the end user
# | jamfdly | deferral time        | a comma-separated list of integers (seconds) to create a menu of deferral times
# | jamftrg | Jamf policy trigger  | Jamf Pro policy custom event string (required if deferral is enabled)
# | mssgdfr | deferral message     | display message to users explaining selection of deferral time options
# | mssgopn | installation message | display message to users explaining deferral options due to open installed application
# | mssgttl | title message        | title on title bar of jamfhelper display window

### INSTALLATION ###

appname='Citrix Workspace'
applurl='https://www.citrix.com/downloads/workspace-app/mac/workspace-app-for-mac-latest.html#ctx-dl-eula-external'
crntvrs="$(/usr/bin/curl -LsS "$applurl" | /usr/bin/xmllint -html -xpath "//p[contains(text(),'Version:')]/text()" - | /usr/bin/sed 's/Version: //' | /usr/bin/awk -F '.' '{print $1"."$2"."$3}')"; /bin/sleep 2
appdown="https:$(/usr/bin/curl -LsS "$applurl" | /usr/bin/xmllint -html -xpath "string(//a[contains(@rel, 'dmg?')]/@rel)" -)"; /bin/sleep 2
appctnr="/private/tmp/$appname.dmg"
apppath="/Volumes/$appname/Install $appname.pkg"

### VALIDATION ###

appchck='enabled'
bndlstr='com.citrix.receiver.nomas'
crtdvnm='Citrix Systems, Inc.'
crtteam='S272Y5R93J'
dwnhash=''
infovrs='CFBundleShortVersionString'

### DEFERRAL ###

appdfrl='enabled'
jamfdly='., 300, 900, 3600, 14400, 28800, 43200, 86400, 172800'
jamftrg=''
mssgdfr='Please select a time to start the update from the pulldown menu.

Selecting "Start Now" will quit the app & start the update.

After making a selection click "Install".

'
mssgopn='
... ACS is attempting to update an app that is open.

The update will take approximately 3-5 minutes to complete.

Click "Install Now" to quit the app & start the update immediately.

Click "Install Later" to select a time to update the app. At the selected time the update will start automatically.

Thanks for helping us stay more secure!'
mssgttl='... ACS Application Update'

### DO NOT MODIFY BELOW ###

# if script is not executed as root, exit
if [ "$EUID" != 0 ]
then
    >&2 echo "\n$(/usr/bin/basename "$0")\n© $(/bin/date '+%Y') Brock Walters, ... LLC. All rights reserved.\n\nThis script must be executed as the root user.\nexiting..."; exit
fi

# strings & variables
appexec='Mach-O'
appinfo="/Applications/$appname.app/Contents/Info.plist"
appprgp="$(/usr/bin/pgrep -ai "$appname")"
apptrfm="$(echo "$appname" | /usr/bin/sed 's/ /./' | /usr/bin/tr '[:upper:]' '[:lower:]')"
bndlchk="$apppath/Contents/Info.plist"
crntusr="$(/usr/bin/stat -f %Su /dev/console)"
icnsclk='/System/Library/CoreServices/CoreTypes.bundle/Contents/Resources/Clock.icns'
icnsdir='/System/Library/CoreServices/CoreTypes.bundle/Contents/Resources/ApplicationsFolderIcon.icns'
icnsprb='/System/Library/CoreServices/Problem Reporter.app/Contents/Resources/ProblemReporter.icns'
jmfhelp='/Library/Application Support/JAMF/bin/jamfHelper.app/Contents/MacOS/jamfHelper'
jmfplst="/Library/LaunchDaemons/com.s.DownSide.$apptrfm.plist"
jmfsrvc="$(/usr/bin/basename "${jmfplst%.*}")"
jmfwarn="PLEASE SAVE YOUR WORK NOW\!

$appname will quit after clicking \"Install\"."
lgtxt64='/private/tmp/DownSide.b64'
lgtxtpl='/private/tmp/DownSide.log.plist'
plstbdy='/usr/libexec/PlistBuddy'
pkgtype='xar archive'
utlarch='/System/Library/CoreServices/Applications/Archive Utility.app'
utlplst="/Users/$crntusr/Library/Containers/com.apple.archiveutility/Data/Library/Preferences/com.apple.archiveutility.plist"
if [[ "$appctnr" =~ ^\([^.]+\)$ ]]
then
    xtnctnr='bin'
else
    xtnctnr="${appctnr##*.}"
fi
case "$xtnctnr" in
'app'|'bin'|'pkg' ) apppath="$appctnr" ;;
esac
if [[ "$apppath" =~ ^\([^.]+\)$ ]]
then
    xtnpath='bin'
else
    xtnpath="${apppath##*.}"
fi

# logging (add these to baser binary with scripts...)
/bin/cat << "EOF" > "$lgtxt64"
PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPCFET0NUWVBFIHBsaXN0IFBVQkxJQyAiLS8vQXBwbGUvL0RURCBQTElTVCAxLjAvL0VOIiAiaHR0cDovL3d3dy5hcHBsZS5jb20vRFREcy9Qcm9wZXJ0eUxpc3QtMS4wLmR0ZCI+CjxwbGlzdCB2ZXJzaW9uPSIxLjAiPgo8ZGljdD4KCTxrZXk+YWRvYmVfaW5zdGFsbF9jbG91ZDwva2V5PgoJPGRhdGE+CglWR2hsSUVGa2IySmxJRU55WldGMGFYWmxJRU5zYjNWa0lFbHVjM1JoYkd4bGNpQnBjeUJoSUdSbGJHbGpZWFJsSUdacwoJYjNkbGNpQW1JRzExYzNRZ1ltVWdkSEpsWVhSbFpDQmhjeUJ6ZFdOb0xpNHUKCTwvZGF0YT4KCTxrZXk+YWRvYmVfaW5zdGFsbF9zbGVlcDwva2V5PgoJPGRhdGE+CglkMkZwZEdsdVp5Qm1iM0lnUVdSdlltVWdhVzV6ZEdGc2JHVnlMaTR1Cgk8L2RhdGE+Cgk8a2V5PmFkb2JlX2luc3RhbGxfdGltZXI8L2tleT4KCTxkYXRhPgoJTGk0dVFXUnZZbVVnUTNKbFlYUnBkbVVnUTJ4dmRXUWdhVzV6ZEdGc2JDQjBiMjlySUNSVFJVTlBUa1JUSUhObFkyOXUKCVpITXUKCTwvZGF0YT4KCTxrZXk+YXBwX2NoZWNrX2N1cmxfZGF0YTwva2V5PgoJPGRhdGE+CglZMmhsWTJ0cGJtY2dZWEJ3SUdSaGRHRXVMaTQ9Cgk8L2RhdGE+Cgk8a2V5PmFwcF9ja19jdXJyZW50X3ZlcnM8L2tleT4KCTxkYXRhPgoJTGk0dWRHaGxJR04xY25KbGJuUWdZWFpoYVd4aFlteGxJQ1JoY0hCdVlXMWxJSFpsY25OcGIyNGdkMkZ6SUc1dmRDQmoKCWIyeHNaV04wWldRdUNtVjRhWFJwYm1jdUxpND0KCTwvZGF0YT4KCTxrZXk+YXBwX2NrX2Rvd25sb2FkX3VybDwva2V5PgoJPGRhdGE+CglMaTR1WkdGMFlTQjNZWE1nYm05MElHTnZiR3hsWTNSbFpDQm1jbTl0SUhSb1pTQWtZWEJ3Ym1GdFpTQmtiM2R1Ykc5aAoJWkNCVlVrd3VDbVY0YVhScGJtY3VMaTQ9Cgk8L2RhdGE+Cgk8a2V5PmFwcF9pbnN0YWxsX3ZlcnNpb248L2tleT4KCTxkYXRhPgoJWTI5c2JHVmpkR2x1WnlCMlpYSnphVzl1SUdSaGRHRXVMaTQ9Cgk8L2RhdGE+Cgk8a2V5PmFwcF9raWxsX3J1bm5pbmdfcHM8L2tleT4KCTxkYXRhPgoJYTJsc2JHbHVaeUFrWVhCd2JtRnRaU0J3Y205alpYTnpaWE11TGk0PQoJPC9kYXRhPgoJPGtleT5hcHBfbm9faW5zdGFsbGF0aW9uPC9rZXk+Cgk8ZGF0YT4KCVlYQndiR2xqWVhScGIyNGdibTkwSUdsdWMzUmhiR3hsWkM0dUxncGhkSFJsYlhCMGFXNW5JQ1JoY0hCdVlXMWxMbUZ3CgljQ0IyWlhKemFXOXVJQ1JqY201MGRuSnpJR2x1YzNSaGJHeGhkR2x2Ymk0dUxnPT0KCTwvZGF0YT4KCTxrZXk+YXBwX3ZlcnNpb25fY29tcGFyZTwva2V5PgoJPGRhdGE+CglKR0Z3Y0c1aGJXVTZDaWNnSUNBZ0oybHVjM1JoYkd4bFpDQjJaWEp6YVc5dUlEMGdKR2x1YzNSMmNuTUtKeUFnSUNBbgoJWTNWeWNtVnVkQ0JoZG1GcGJHRmliR1VnWkc5M2JteHZZV1FnZG1WeWMybHZiaUE5SUNSamNtNTBkbkp6Cgk8L2RhdGE+Cgk8a2V5PmFwcF92ZXJzaW9uX2V4aXRpbmc8L2tleT4KCTxkYXRhPgoJTGk0dWRYQXRkRzh0WkdGMFpTRUtaWGhwZEdsdVp5NHVMZz09Cgk8L2RhdGE+Cgk8a2V5PmFwcGxpY2F0aW9uX3VwZGF0ZXI8L2tleT4KCTxkYXRhPgoJYm04Z0pHRndjRzVoYldVZ2NISnZZMlZ6YzJWeklITmxaVzBnZEc4Z1ltVWdjblZ1Ym1sdVp5NHVMZ3BoZEhSbGJYQjAKCWFXNW5JSFJ2SUhWd1pHRjBaU0F2UVhCd2JHbGpZWFJwYjI1ekx5UmhjSEJ1WVcxbExtRndjQ0IwYnlCMlpYSnphVzl1CglJQ1JqY201MGRuSnoKCTwvZGF0YT4KCTxrZXk+Y29kZXNpZ25fdGVzdHNfZmFpbDwva2V5PgoJPGRhdGE+CglMaTR1WTI5a1pTQnphV2R1YVc1bklIWmhiR2xrWVhScGIyNGdabUZwYkdWa0xncGxlR2wwYVc1bkxpNHUKCTwvZGF0YT4KCTxrZXk+Y29kZXNpZ25fdGVzdHNfcGFzczwva2V5PgoJPGRhdGE+CglKeUFnSUNBbmRHVmhiU0JwWkdWdWRHbG1hV1Z5SUQwZ2Iyc0tKeUFnSUNBblpHVjJaV3h2Y0dWeUlHNWhiV1VnUFNCdgoJYXdvbklDQWdJQ2R1YjNSaGNtbDZZWFJwYjI0Z1BTQnZhd29uSUNBZ0lDZHphV2R1WVhSMWNtVWdjM1JoZEhWeklEMGcKCWIycz0KCTwvZGF0YT4KCTxrZXk+Y3VybF9vdXRwdXRfZXh0cmFjdDwva2V5PgoJPGRhdGE+CglMaTR1Wkc5M2JteHZZV1JwYm1jZ0p5WW5JR1Y0ZEhKaFkzUnBibWNnSkNndmRYTnlMMkpwYmk5aVlYTmxibUZ0WlNBawoJWVhCd1kzUnVjaWtnZG1WeWMybHZiaUFrWTNKdWRIWnljdz09Cgk8L2RhdGE+Cgk8a2V5PmRlZmVyX2Rpc2FibGVkX2V4aXQ8L2tleT4KCTxkYXRhPgoJSkdGd2NHNWhiV1VnY0hKdlkyVnpjMlZ6SUdGeVpTQnlkVzV1YVc1bkxpNHVDbVY0YVhScGJtY3VMaTQ9Cgk8L2RhdGE+Cgk8a2V5PmRlZmVyX2xhdW5jaF9kYWVtb248L2tleT4KCTxkYXRhPgoJWTNKbFlYUnBibWNnWkdWbVpYSnlZV3dnYkdGMWJtTm9JR1JoWlcxdmJpQnpaWFFnZEc4Z1pYaGxZM1YwWlNCcGJpQWsKCVpHWnlkR2x0WlNCelpXTnZibVJ6TGk0dQoJPC9kYXRhPgoJPGtleT5kZWZlcl9vcHRpb25fZGlhbG9nPC9rZXk+Cgk8ZGF0YT4KCUpHRndjRzVoYldVZ2NISnZZMlZ6YzJWeklISjFibTVwYm1jdUxpNEtjSEpsYzJWdWRHbHVaeUJrWldabGNuSmhiQ0J2CgljSFJwYjI1ekxpNHUKCTwvZGF0YT4KCTxrZXk+ZGVmZXJfcGVyaW9kX2xhcHNlZDwva2V5PgoJPGRhdGE+CglSRVZHUlZKU1FVd2dWRWxOUlNCSVFWTWdSVXhCVUZORlJDRWhJUXBoZEhSbGJYQjBhVzVuSUhSdklIVndaR0YwWlNBdgoJUVhCd2JHbGpZWFJwYjI1ekx5UmhjSEJ1WVcxbExtRndjQ0IwYnlCMlpYSnphVzl1SUNSamNtNTBkbkp6Cgk8L2RhdGE+Cgk8a2V5PmRlZmVyX3Nob3dfZGVmZXJyYWw8L2tleT4KCTxkYXRhPgoJSnlBZ0lDQW5kWE5sY2lCelpXeGxZM1JsWkNCSmJuTjBZV3hzSUV4aGRHVnkKCTwvZGF0YT4KCTxrZXk+ZGVmZXJfc2tpcF9kZWZlcnJhbDwva2V5PgoJPGRhdGE+CglKeUFnSUNBbmRYTmxjaUJ6Wld4bFkzUmxaQ0JKYm5OMFlXeHNJRTV2ZHc9PQoJPC9kYXRhPgoJPGtleT5kZWZlcnJhbF9pc19kaXNhYmxlPC9rZXk+Cgk8ZGF0YT4KCVpHVm1aWEp5WVd3Z2FYTWdaR2x6WVdKc1pXUXVMaTQ9Cgk8L2RhdGE+Cgk8a2V5PmRlZmVycmFsX2lzX2VuYWJsZWQ8L2tleT4KCTxkYXRhPgoJWkdWbVpYSnlZV3dnYVhNZ1pXNWhZbXhsWkM0dUxnPT0KCTwvZGF0YT4KCTxrZXk+ZG93bmxvYWRfaGFzaF9lcnJvcjwva2V5PgoJPGRhdGE+CgliV1ExSUdoaGMyZ2diV0YwWTJnZ1ptRnBiR1ZrTGdwMlpYSnBabmtnZEdoaGRDQjBhR1VnWkhkdWFHRnphQ0IyWVd4MQoJWlNCcGN5QmpiM0p5WldOMExpNHVDbVY0YVhScGJtY3VMaTQ9Cgk8L2RhdGE+Cgk8a2V5PmRvd25sb2FkX2hhc2hfbWF0Y2g8L2tleT4KCTxkYXRhPgoJYldRMUlHaGhjMmdnYldGMFkyaGxaQzR1TGc9PQoJPC9kYXRhPgoJPGtleT5kb3dubG9hZF92YWxpZGF0aW9uPC9rZXk+Cgk8ZGF0YT4KCVlYUjBaVzF3ZEdsdVp5QjBieUIyWVd4cFpHRjBaU0FrWVhCd2JtRnRaU0JrYjNkdWJHOWhaQzR1TGc9PQoJPC9kYXRhPgoJPGtleT5maWxlX2NvbnRhaW5lcl90eXBlPC9rZXk+Cgk8ZGF0YT4KCUp5QWdJQ0FuWTI5dWRHRnBibVZ5SUhSNWNHVTZJQ1I0ZEc1amRHNXkKCTwvZGF0YT4KCTxrZXk+ZmlsZV9leHRyYWN0ZWRfdHlwZTwva2V5PgoJPGRhdGE+CglKeUFnSUNBblptbHNaU0IwZVhCbE9pQWtlSFJ1Y0dGMGFBPT0KCTwvZGF0YT4KCTxrZXk+ZmlsZV90eXBlX25vdF92YWxpZDwva2V5PgoJPGRhdGE+CglabWxzWlNCMllXeHBaR0YwYVc5dUlHWmhhV3hsWkM0S1pYaHBkR2x1Wnk0dUxnPT0KCTwvZGF0YT4KCTxrZXk+bG9nZ2VkX2luX3VzZXJfbmFtZTwva2V5PgoJPGRhdGE+CglZM1Z5Y21WdWRDQnNiMmRuWldRdGFXNGdkWE5sY2pvZ0pHTnliblIxYzNJS0lBPT0KCTwvZGF0YT4KCTxrZXk+c2NyaXB0X25hbWVfdmVyc2lvbjwva2V5PgoJPGRhdGE+CglJQW9rS0M5MWMzSXZZbWx1TDJKaGMyVnVZVzFsSUNRd0tRckNxU0FrS0M5aWFXNHZaR0YwWlNBckpWa3BJRUp5YjJOcgoJSUZkaGJIUmxjbk1zSUZOMFlYSjZJRVZ1ZEdWeWRHRnBibTFsYm5Rc0lFeE1ReTRnUVd4c0lISnBaMmgwY3lCeVpYTmwKCWNuWmxaQzRLSUE9PQoJPC9kYXRhPgoJPGtleT5zY3JpcHRfcmVxdWlyZW1lbnRzPC9rZXk+Cgk8ZGF0YT4KCVVrVlJWVWxTUlVRZ1ZrRlNTVUZDVEVWVElFRlNSU0JPVlV4TUlTRWhDbE5sWlNCMGFHVWdTVTVUVkVGTVRFRlVTVTlPCglJSFJoWW14bElHbHVJSFJvWlNCamIyMXRaVzUwY3lCaGRDQjBhR1VnZEc5d0lHOW1JSFJvWlNCelkzSnBjSFF1Q21WNAoJYVhScGJtY3VMaTRnCgk8L2RhdGE+Cgk8a2V5PnVubW91bnRfdW5sb2FkX3JtcmY8L2tleT4KCTxkYXRhPgoJWTJ4bFlXNXBibWNnZFhBdUxpND0KCTwvZGF0YT4KPC9kaWN0Pgo8L3BsaXN0Pgo=
EOF
/usr/bin/base64 -D "$lgtxt64" > "$lgtxtpl"; /bin/chmod 400 "$lgtxtpl"; /usr/sbin/chown 0:0 "$lgtxtpl"; /bin/rm -rf "$lgtxt64"

# functions

# kill application processes if running & remove application
apprm(){
lgfnc app_kill_running_ps; IFS=$'\n'; for i in $(/bin/ps -ax | /usr/bin/awk "/$appname/&&/$bndlstr/&&\!/awk/{print \$1}"); do kill -9 "$i"; done; /bin/sleep 2; /usr/bin/pkill -ail "$appname"; /bin/sleep 2; /bin/rm -rf "/Applications/$appname.app"; /bin/sleep 2
}

# check application bundle for executables & check bundle identifier string (validation)
ckapp(){
if /usr/bin/file "$apppath"/Contents/MacOS/* | /usr/bin/grep -q "$appexec" && "$plstbdy" -c "print CFBundleIdentifier" "$bndlchk" | /usr/bin/grep -q "$bndlstr"
then
    >&2 /usr/bin/codesign --verify --verbose=2 "$apppath"; >&2 /usr/bin/codesign --display --deep --verbose=99 "$apppath"; crttype='Application'; spctext="$(/usr/sbin/spctl -a -vv "$apppath" 2>&1)"
else
    lgfnc file_type_not_valid; clnxt
fi
}

# check if file is an executable binary (validation)
ckbin(){
if /usr/bin/file "$apppath" | /usr/bin/grep -q "$appexec"
then
    spctext="$(/usr/sbin/spctl -a -vv -t execute "$apppath" 2>&1)"
else
    lgfnc file_type_not_valid; clnxt
fi
}

# check md5 hash if populated (validation)
ckhsh(){
if [ -n "$dwnhash" ]
then
    case "$(/sbin/md5 "$appctnr" | /usr/bin/awk '{print $NF}')" in
    "$dwnhash" ) lgfnc download_hash_match ;;
             * ) lgfnc download_hash_error; clnxt ;; 
    esac
fi 
}

# check package for bom & file type (validation)
ckpkg(){
if /usr/sbin/pkgutil --verbose --bom "$apppath" && /usr/bin/file "$apppath" | /usr/bin/grep -q "$pkgtype" 
then
    >&2 /usr/sbin/pkgutil --verbose --check-signature "$apppath"; crttype='Installer'; spctext="$(/usr/sbin/spctl -a -vv -t install "$apppath" 2>&1)"
else
    lgfnc file_type_not_valid; clnxt
fi
}

# check code signing (validation)
cksgn(){
export "$(echo "$spctext" | /usr/bin/awk '/origin=/{print}')" "$(echo "$spctext" | /usr/bin/awk '/source=/{print}')"; chcktid="$(echo "$origin" | /usr/bin/awk '{print $NF}' | /usr/bin/sed 's/)//;s/(//')"; chckdev="$(echo "$origin" | /usr/bin/sed "s/Developer ID $crttype: //;s/ ($chcktid)//")"; chckntr="$source"; chcksts="$(echo "$spctext" | /usr/bin/awk "/$(/usr/bin/basename "$apppath")/{print \$NF}")"
if [ "$chcktid" = "$crtteam" ] && [ "$chckdev" = "$crtdvnm" ] && [ "$chckntr" = 'Notarized Developer ID' ] && [ "$chcksts" = 'accepted' ] 
then
    lgfnc codesign_tests_pass
else
    lgfnc codesign_tests_fail; clnxt
fi
}

# unmount volumes & remove files after validation checks or application install & unload launch daemon if deferral time has elapsed
clnxt(){
lgfnc unmount_unload_rmrf
if [ "$xtnctnr" = 'dmg' ] && [ -e "$apppath" ]
then
    /usr/bin/hdiutil detach -force -verbose "$(/usr/bin/dirname "$apppath")"; /bin/sleep 2;
fi
if [ -z "$dfrbttn" ] && /bin/launchctl list | /usr/bin/grep -q "$jmfsrvc"
then
    /bin/launchctl unload -F "$jmfsrvc"; /bin/sleep 2; /bin/rm -rf "$jmfplst"
fi
/bin/rm -rf -v "$appctnr" "$apppath" "$lgtxtpl" /private/tmp/*.pkg.boms.*
exit
}

# installation
instl(){
case "$xtnpath" in
'app' ) opapp ;;
'bin' ) opbin ;;
'pkg' ) oppkg ;;
    * ) lgfnc file_type_not_valid ;;
esac; /bin/sleep 2
}

# logging
lgfnc(){
IFS=$'\n'; arr=($("$plstbdy" -c "print $1" "$lgtxtpl")); for j in "${arr[@]}"; do eval echo "$j"; done
}

# ditto application to /Applications/ unless it is the Adobe Creative Cloud Installer... (installation)
opapp(){
lgfnc file_extracted_type
if "$plstbdy" -c "print CFBundleIdentifier" "$bndlchk" | /usr/bin/grep -q 'com.adobe.cc.Install'
then
    lgfnc adobe_install_cloud
    SECONDS=0; /usr/bin/open "$apppath"; /bin/sleep 2; adobeps="$(/bin/ps -ax  | /usr/bin/awk '/\/Volumes\/Creative Cloud\/Install.app\/Contents\/MacOS\/Install/{print $1}')"
    while true
    do
        if /bin/kill -s 0 "$adobeps"
        then
            lgfnc adobe_install_sleep; /bin/sleep 20
        else
            lgfnc adobe_install_timer; break
        fi
    done
else
    /usr/bin/ditto -v "$apppath" "/Applications/$appname.app"; /bin/sleep 2
fi
}

# open valid containers with Archive Utility (extraction)
oparc(){
lgfnc file_container_type
if /usr/bin/file "$appctnr" | /usr/bin/grep -q "$ctnrtyp"
then
    /usr/bin/pkill -ail 'Archive Utility'; /bin/sleep 2; /usr/bin/sudo -u "$crntusr" /usr/bin/defaults write "$utlplst" dearchive-reveal-after -bool FALSE; /bin/sleep 2; /usr/bin/open -a "$utlarch" "$appctnr"; /bin/sleep 2; /usr/bin/sudo -u "$crntusr" /usr/bin/defaults write "$utlplst" dearchive-reveal-after -bool TRUE; /bin/sleep 2; /usr/bin/defaults read "$utlplst"
else
    lgfnc file_type_not_valid; clnxt
fi
}

# ditto binary to /usr/local/bin/ (installation)
opbin(){
lgfnc file_extracted_type; applbin="/usr/local/bin/$(/usr/bin/basename "$appctnr")"; /usr/bin/ditto -v "$appctnr" "$applbin"; /bin/sleep 2; /bin/chmod 755 "$applbin"; /usr/bin/xattr -c "$applbin"
}

# mount valid disk image (extraction)
opdmg(){
lgfnc file_container_type
if /usr/bin/hdiutil verify "$appctnr"
then
    /usr/bin/hdiutil attach -nobrowse -readonly "$appctnr"; /bin/sleep 2
else
    lgfnc file_type_not_valid; clnxt
fi
}

# collect deferral time selection & write out deferral launch daemon (deferral)
opltr(){
lgfnc defer_show_deferral; dfrslct="$("$jmfhelp" -windowType 'hud' -title "$mssgttl" -heading "$appname Update" -alignHeading 'natural' -description "$mssgdfr" -icon "$icnsclk" -showDelayOptions "$jamfdly" -button1 'Install' -lockHUD)"; dfrtime="$(echo "$dfrslct" | /usr/bin/sed 's/.$//')"
lgfnc defer_launch_daemon
/bin/cat << EOF > "$jmfplst"
<?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"><dict><key>Label</key><string>$jmfsrvc</string><key>RunAtLoad</key><true/><key>ProgramArguments</key><array><string>/usr/local/jamf/bin/jamf</string><string>policy</string><string>-event</string><string>$jamftrg</string></array><key>StartInterval</key><integer>$dfrtime</integer></dict></plist>
EOF
/bin/chmod 644 "$jmfplst"; /usr/sbin/chown 0:0 "$jmfplst"; /usr/bin/plutil -convert binary1 "$jmfplst"; /bin/launchctl load -F "$jmfplst"
}

# install application immediately after presenting deferral options if application was open (deferral)
opnow(){
lgfnc defer_skip_deferral
"$jmfhelp" -windowType 'hud' -title "$mssgttl" -heading "$appname Update" -alignHeading 'natural' -description "$jmfwarn" -icon "$icnsprb" -button1 'Install' -defaultButton 1 -lockHUD
apprm; instl; clnxt
}

# execute package with macOS installer binary (installation)
oppkg(){
lgfnc file_extracted_type
/usr/sbin/installer -dumplog -pkg "$apppath" -tgt /; /bin/sleep 2
}

# operations

# write script information & currently logged-in user to jamf policy log
lgfnc script_name_version; lgfnc logged_in_user_name

# if required variables are not populated, exit
if [ -z "$appctnr" ] || [ -z "$applurl" ] || [ -z "$appname" ] || [ -z "$apppath" ]
then
    lgfnc script_requirements; clnxt
fi

# if required curl data was not collected, exit
lgfnc app_check_curl_data
if [ -z "$appdown" ]
then
    lgfnc app_ck_download_url; clnxt
fi
if [ -z "$crntvrs" ]
then
    lgfnc app_ck_current_vers; clnxt
fi

# if the application is installed get the version string from the Info.plist matching the format of the currently available application download version string 
lgfnc app_install_version
if [ -e "$appinfo" ]
then
    instvrs="$("$plstbdy" -c "print $infovrs" "$appinfo")"
else
    instvrs=0
fi

# if the installed version matches or is newer than the currently available application download version, exit
lgfnc app_version_compare
appckvr="$(echo "$instvrs\n$crntvrs" | /usr/bin/sort -r --version-sort | /usr/bin/sed -n '1p')"
if [ "$instvrs" = "$crntvrs" ] || [ "$instvrs" = "$appckvr" ]
then
    lgfnc app_version_exiting; clnxt
fi

# downloading
lgfnc curl_output_extract
/usr/bin/curl -LsS "$appdown" -o "$appctnr"; /bin/sleep 2

# extraction
case "$xtnctnr" in
            'bz2' ) ctnrtyp='bzip2'; oparc ;;
            'dmg' ) opdmg ;;
       'gz'|'tgz' ) ctnrtyp='gzip'; oparc ;;
'ipa'|'jar'|'zip' ) ctnrtyp='zip'; oparc ;;
            'pkg' ) : ;;
                * ) lgfnc file_type_not_valid; clnxt ;;
esac

# validation
if [ "$appchck" = 'enabled' ]
then
    lgfnc download_validation
    case "$xtnpath" in
    'app' ) ckapp ;;
    'bin' ) ckbin ;;
    'pkg' ) ckpkg ;;
    esac
    cksgn
    ckhsh
fi

# install conditions

# if the application is not installed, install
if [ "$instvrs" = 0 ]
then
    lgfnc app_no_installation; instl; clnxt
fi

# if the application is installed but not running, install
if [ "$instvrs" != 0 ] && [ -z "$appprgp" ]
then
    lgfnc application_updater; apprm; instl; clnxt
fi

# deferral logging 
case "$appdfrl" in
'enabled' ) lgfnc deferral_is_enabled ;;
        * ) lgfnc deferral_is_disable ;;
esac

# if deferral is disabled & the application is installed & is running, exit
if [ "$appdfrl" != 'enabled' ] && [ "$instvrs" != 0 ] && [ -n "$appprgp" ]
then
    lgfnc defer_disabled_exit; clnxt
fi

# if deferral is enabled & the application is installed & is running & there is no deferral launch daemon installed, present deferral options
if [ "$appdfrl" = 'enabled' ] && [ "$instvrs" != 0 ] && [ -n "$appprgp" ] && ! [ -e "$jmfplst" ] && ! /bin/launchctl list | /usr/bin/grep -q "$jmfsrvc"
then
    lgfnc defer_option_dialog
    dfrbttn="$("$jmfhelp" -windowType 'hud' -title "$mssgttl" -heading "$appname Update" -alignHeading 'natural' -description "$mssgopn" -icon "$icnsdir" -button1 'Install Later' -button2 'Install Now' -lockHUD)"
    case "$dfrbttn" in
    '0' ) opltr ;;
    '2' ) opnow ;;
    esac
fi

# if deferral is enabled & the application is installed & a deferral launch daemon is installed, the deferral time has elapsed, install
if [ "$appdfrl" = 'enabled' ] && [ "$instvrs" != 0 ] && [ -e "$jmfplst" ] && /bin/launchctl list | /usr/bin/grep -q "$jmfsrvc"
then
    lgfnc defer_period_lapsed; apprm; instl; clnxt
fi
noahtalerman commented 4 months ago

Start w/ 10 apps in the library. Cross-platform.

Noah and Marko riffing: Slack, Google Chrome, Figma, Zoom ...

Whatever the cross over is between most popular productivity apps and what we use at Fleet. Lean more heavily towards what we use a Fleet so we can dogfood and get quick feedback cycles.

cc @spokanemac @lukeheath

mostlikelee commented 4 months ago

Regarding cross-platform support: I would separate efforts between platforms so we're not blocking the release of a macOS app because we're still working on the Windows app. I imagine the velocity of using a framework like Autopkg will be vastly faster than adding apps on other platforms.

If we use a framework, be aware of what customers expect in terms of detection frequency. For example, if an app has a 0-day patch available, we cannot wait for the framework to update the recipe 2 days later (common case in homebrew, for example)

Lastly, HOW we update is important. Consider a notification component so end-users do not lose their work or are significantly interrupted (closing zoom during a meeting) because of an update.

nonpunctual commented 4 months ago

@mostlikelee 100% agree. Another thing that my script has above is logic for install deferral.

noahtalerman commented 4 months ago

From @pintomi1989:

customer-flacourtia's requested apps:

Hey Mike, do you know which Adobe apps they want to manage? (ex. Acrobat)

noahtalerman commented 3 months ago

@lukeheath and I met and we think these are the capabilities that will make Fleet's app library reach mission critical Apple MDM maturity:

  1. Add an app from the app library (powered by Homebrew)
  2. Add new "software" policy type that doesn't require writing an osquery query (SQL). Fleet can automatically add this policy for the user.
  3. Add option to tell Fleet to keep an app from the app library up-to-date.
  4. Add fleetctl command to convert apps in app library to Fleet's YAML (for GitOps)

In the first iteration, Fleet will address (1). Why? Addressing all 4 (mainly the first 3) are significant design and engineering efforts.

We want to bring (1) into the current engineering sprint (2 weeks remaining) so that we (Fleet) can dogfood the app library and customers can test and give feedback sooner.

This means that when we ship (1). The IT admin can add an app from the app library, install it manually on a host, or automatically install it on policy failure.

Then plan is to followup and address the other capabilities listed above in iterations coming soon.

Please feel free to schedule time w/ Luke or I if you have concerns or feedback on the above.

cc @dherder @nonpunctual @nonpunctual @spokanemac

spokanemac commented 3 months ago

Putting this here for reference: Using AutoPkg for Windows Software 2.0 - MacAdmins 2023.

noahtalerman commented 3 months ago

Hey @georgekarrv, chatting w/ @lukeheath and we think this story is ready for specs.

We assigned you and Luke.

Heads up that there are still some UI TODOs that @marko-lisica is tackling next Monday. You can find those here in the design review doc.

lukeheath commented 3 months ago

@georgekarrv This is ready to get started on, but there's still a fair amount of frontend work being designed. But, we can get started building the backend for software ingestion and processing. Since I've been involved in the design process for this I'll work with you to spec this one out on Tuesday so we can have it lined up for the team when they're ready. I'll schedule a call with you on Tuesday to review.

noahtalerman commented 2 months ago

customer-preston wants to upload apps packaged as .zip filed. This is now part of this story.

@ddribeiro heads up, since uploading .zips is now part of this story, I added the cusotmer-preston label to this story.

noahtalerman commented 2 months ago
  • [ ] Add the 20 apps listed in the public Google doc here to Fleet's app library for macOS.

Hey @marko-lisica @lukeheath and @georgekarrv I added the above to the "Product" section in this issue's description.

(heads up that I also made the doc public so we can share w/ customers/prospects)

I think let's use this Google doc as the source of truth for 20 apps we'll support in this iteration. Note that these apps might change as we start building and learn more about what it takes to support each one.

Heads up that this doc also has great proposals on how we might build some of the technical pieces (.dmg upload and Homebrew processor). Please feel free to edit the doc and/or mark up w/ comments.

Marko, heads up that I made some minor tweaks to the doc (added a top section and renamed a couple sections)

noahtalerman commented 2 months ago

@noahtalerman: We learned that some Microsoft apps (ex. Excel) can point to an XML configuration file at install time to enable/disable certain settings. More info in the "Homebrew format" section in the Google doc here.

We decided to not point to XML in the default install script for these apps because we think the apps can still be installed and used by the end user w/o it. And, we can add Fleet feature for this later.

To be sure, as part of this story, we want to test the following:

  • Can the apps that point to an XML config file be installed and used by the end user w/o pointing to the file?
  • Can an IT admin update the settings remotely that are normally set in the XML config file after install? Maybe by delivering a profile or running a script?

Hey @georgekarrv and @PezHub I just added the above to the "QA" section in this issue.

Is this something we can test as part of this story? Please schedule some time w/ @marko-lisica and I if you have any questions. Thanks :)

lukeheath commented 2 months ago

Design questions coming out of spec:

  1. On the Fleet app library list, how will the user know which apps have already been added either through the library, VPP, or manual upload?
image.png
  1. When adding software from the library, we don't need to show upload progress because the upload is happening on the Fleet server, not the client. Instead, we should likely save it immediately and set a status on it. In that case, a design quesiton is "What should the status be"? And what happens if we fail to download it? If we wanted to show the progress, it would require more dev because we'd have to create an API endpoint to make download progress available.

It seems like we need a "Status" column to show if it's already added, currently being added, or failed to add. We haven't worried about this previously because the software item didn't appear until after it was successfully uploaded.

image.png
  1. If we add a status column to the Fleet app library, do we also need to add it to the list of software that has already been added?
lukeheath commented 2 months ago

@georgekarrv I spoke with @noahtalerman about our design questions and we've decided to move forward as-is with some small tweaks:

  1. In the Fleet app library list, only showed items that have not yet been installed. This is the same behavior as a VPP app list.
  2. We're going to keep the add software modal, but use a loading spinning since we don't readily have the progress.
    • We recognize this isn't ideal. We are planning on creating an async operations drawer in the UI where things like this can live, and we want to wait to design a holistic solution instead of a temporary solution we'll need to undo later.
    • We think these apps will upload pretty quickly since they won't be contrained by user bandwidth, so we'd like to see what the wait time is before making workflow decisions.
noahtalerman commented 2 months ago

Thanks @lukeheath!

In the Fleet app library list, only showed items that have not yet been installed. This is the same behavior as a VPP app list.

I added a dev note for this here. This is consistent w/ the UX for App Store (VPP) apps.

Later, we might show some status on the "Fleet library" page.

We're going to keep the add software modal, but use a loading spinning since we don't readily have the progress.

Figma is updated here.

cc @georgekarrv @marko-lisica

lukeheath commented 2 months ago

@noahtalerman @marko-lisica It looks like we need some additional REST API specs:

  1. We need an endpoint that will return a list of all Fleet app library software.

    • This should include something to indicate if the software has been added, so the frontend can hide items that have already been added.
    • Or, we could only return items that haven't been added yet, but it seems like an endpoint to retrieve all Fleet app library software would be good to have.
  2. We need to add an endpoint to hit when the user selects to "Add" a Fleet app library app. This could be the existing Add Software endpoint with a new parameter indicating it should install from a Fleet app library ID instead of providing the file. Or, it could be a new independent endpoint just for adding app library items.

noahtalerman commented 2 months ago

Thanks @lukeheath!

Hey @marko-lisica, since moving this story to ready for dev is design team's top priority can you please start w/ API designs for this tomorrow?

If you can't, please let Luke or I know so that we can take it :)

This could be the existing Add Software endpoint with a new parameter indicating it should install from a Fleet app library ID instead of providing the file. Or, it could be a new independent endpoint just for adding app library items.

My first though is new API endpoint b/c, to be consistent w/ other Fleet API endpoints, we'd want to put the app library ID in the URL.

cc @georgekarrv

nonpunctual commented 2 months ago

@lukeheath @noahtalerman per https://github.com/fleetdm/fleet/issues/18865#issuecomment-2327428045 just letting you know I think this is on-track. For years the Jamf API endpoint to upload to the app distribution point was hidden but in use by systems like Munki, AutoPkg, etc. Having a dedicated, published endpoint for app uploads is the best solution for all Fleet app management features.

marko-lisica commented 2 months ago

In the Fleet app library list, only showed items that have not yet been installed. This is the same behavior as a VPP app list.

@noahtalerman @lukeheath I think if we want to have this same as VPP, then we want to exclude software that's uploaded to a team already (not installed). E.g. if a user adds Microsoft Excel to some team, the software title with the package will be created, and we won't show it in the library for that team anymore.

marko-lisica commented 2 months ago

I added a dev note for this here. This is consistent w/ the UX for App Store (VPP) apps.

@noahtalerman Oh, I see that you already specified it as I mentioned in the comment above. Feel free to ignore.

marko-lisica commented 2 months ago

Thanks @lukeheath!

Hey @marko-lisica, since moving this story to ready for dev is design team's top priority can you please start w/ API designs for this tomorrow?

If you can't, please let Luke or I know so that we can take it :)

This could be the existing Add Software endpoint with a new parameter indicating it should install from a Fleet app library ID instead of providing the file. Or, it could be a new independent endpoint just for adding app library items.

My first though is new API endpoint b/c, to be consistent w/ other Fleet API endpoints, we'd want to put the app library ID in the URL.

cc @georgekarrv

@noahtalerman Here's the PR: https://github.com/fleetdm/fleet/pull/21801

marko-lisica commented 2 months ago
  • Can an IT admin update the settings remotely that are normally set in the XML config file after install? Maybe by delivering a profile or running a script?

@noahtalerman I just found that IT admin can change preferences for Microsoft AutoUpdate after the installation, via configuration profile.

Microsoft docs: Configuration preferences & Deploy preferences for Office for Mac

Also good to know that iMazing Profile Editor has bunch of options for Microsoft products. See screenshot below

Screenshot 2024-09-04 at 16 55 52

I think we're good to go without choices (we don't need to translate that from Homebrew recipes) in this iteration. I think we need to learn if there are some apps that the admin needs to change choices before installing in order to decide if we need to support this.

noahtalerman commented 2 months ago

I just found that IT admin can change preferences for Microsoft AutoUpdate after the installation, via configuration profile.

Thanks @marko-lisica I updated the "QA" section in this issue w/ this new info.

marko-lisica commented 2 months ago

@georgekarrv We'll need to update the HTTP timeout limit to 10 or 15 minutes, to make sure large files (since we'll allow up to 3GB) can be uploaded without interruption.

I think we want this for dogfood and cloud customers. Self-hosted customers will need to make sure that they increase it on their own but if upload fails they'll see an error message explaining that they need to bump the HTTP timeout limit on both server and load balancer.

@mikermcneil proposed that we bump it even more (like 1hr) because weird stuff can happen on the client during upload (@lukeheath has more context on this I think).

I added this as requirement to Product section.

cc @noahtalerman

lukeheath commented 2 months ago

We will also need to put in a new ticket to #g-customer-success when we are ready to increase the LB timeout.

noahtalerman commented 2 months ago

We will also need to put in a new ticket to #g-customer-success when we are ready to increase the LB timeout.

Tracked in a confidential issue here: https://github.com/fleetdm/confidential/issues/7911

noahtalerman commented 2 months ago

Hey @lukeheath, @georgekarrv, and @marko-lisica I met w/ @mikermcneil to review wireframes for this story.

We want to make the following changes:

Any concerns w/ making these changes now?

I added this to tomorrow's design review in case y'all want to discuss further or don't have time to take a look async today.

noahtalerman commented 2 months ago
  • [ ] Documentation changes: Yes
    • We will need a guide written for the Fleet app library (or the existing software library guide is updated to include all options uploads/vpp/library)
    • In the guide, make it clear that adding software in Fleet makes it "available for install." And explain the two ways to install:
    • manually install for an individual host
      • go to Host details page and click "install"
      • or self-service via "My device" page
    • install automatically
    • go to Policies page and have a policy failure trigger it

Hey @georgekarrv, I added the 2nd bullet above to the requirements for the guide. This is important for sales-enablement (for demos) and IT admins to get the full picture.

lukeheath commented 2 months ago

@noahtalerman Sounds like a minor change and makes sense.

marko-lisica commented 2 months ago

@noahtalerman Sounds good to me. I made all changes related to ("Fleet library apps" -> "Fleet maintained apps") in the UI and API.

noahtalerman commented 2 months ago

@zayhanlon, @alexmitchelliii, @pintomi1989, @ddribeiro, and @nonpunctual heads up on the change in "Fleet library" to "Fleet-maintained" messaging for this feature: https://github.com/fleetdm/fleet/issues/18865#issuecomment-2332392292

The goal here is to use consistent language when talking about this feature internally and externally.

noahtalerman commented 2 months ago

I made all changes related to ("Fleet library apps" -> "Fleet maintained apps") in the UI and API.

Hey @marko-lisica, I think let's use "packages" instead of "apps" in the UI b/c it's more explicit.

UPDATE: I think I was wrong. "Fleet-maintained apps" makes sense (noahtalerman)