Skip to content

Import items from AutoPkg to Omnissa Workspace ONE (formerly VMware, Airwatch)

Notifications You must be signed in to change notification settings

codeskipper/WorkSpaceOneImporter

 
 

Repository files navigation

WorkSpaceOneImporter processor and recipes


Code style: black

A set of recipes and a custom processor for use with Omnissa Workspace ONE UEM. Formerly known as a VMware business and product. Going back further, Airwatch was the product name for the MDM product.

WorkSpaceOneImporter is a custom AutoPkg Processor for automating import of Mac product installer packages into Workspace ONE UEM. It can also assign them to one or multiple smart groups, and set certain deployment options such as Push Mode. It has support for automated staging to assignment groups. Automated pruning of old versions can also be enabled and configured in detail if needed.

Adapted from jprichards/AirWatchImporter.


Roadmap

Project is working stable in production. You can reach me as @mart in MacAdmins Slack. Issues and PRs welcome in GitHub.

Done:

  • testing API calls involved for WS1 using Postman -> success
  • forked this repo from jprichards/AirWatchImporter
  • add roadmap to Readme.md
  • rename files, classes, functions, variable names, and comments to reflect update to WS1 from Airwatch, update license, copyright
  • added stub recipe so shared processor can be found in recipes from other repos
  • get call as Autopkg Shared Processor stub to work from other repo
  • try library dependency install for Autopkg as suggested here -> working
  • update for Python3
  • update API calls for WS1 as tested with Postman
  • milestone: get POC working
  • test new input "ws1_console_url" and code that produces link to imported app
  • get force_import working
  • add code to find icon file to upload
  • merged PR#1 from @SoxIn4 - ability to supply base64 pre-encoded api username and password
  • added support for Oauth
  • added support to specify advanced app assignment (API v.2) settings and update on schedule
  • added production ready example recipes (moved from my autopkg-recipe repo)
  • added support for re-using OAuth tokens
  • new feature to prune old software versions from WS1 UEM
  • cleanup code, confirm to Autopkg codestyle standards, added pre-commit

ToDo:

  • add to Autopkg repo recipe subfolder
  • cleanup code, consistent use of f-strings
  • expand usage documentation in wiki
  • maybe establish separate demo repo

Dependencies

Server Side

You must be running AirWatch Console 9.3.0.0 or higher.

Client/AutoPkg Side

Currently, in order to run AirWatchImporter, you must first install two Python libraries:

  • The requests library
  • The requests_toolbelt library

These can be installed by running: (Thanks)

sudo -H /Library/AutoPkg/Python3/Python.framework/Versions/Current/bin/pip3 install requests
sudo -H /Library/AutoPkg/Python3/Python.framework/Versions/Current/bin/pip3 install requests_toolbelt

AutoPkg Shared Processor

As of AutoPkg 0.4.0 you can use this processor as a shared processor.

Add the processor repo:

autopkg repo-add https://github.com/codeskipper/WorkSpaceOneImporter.git

Then use this as the processor in your recipes:

com.github.codeskipper.WorkSpaceOneImporter/WorkSpaceOneImporter

Sensitive input variables

The processor currently requires sensitive keys like password and API token to be populated in your recipe (override) Input variables, or by command line keys.

Instead of keeping secrets in plain text recipe override files, they can be added to a macOS keychain. I took a peek at - this and now have a working launcher shell script for a Python wrapper, and I'll share when I can - it's currently in a private repo for GitHub CI as described below. This is useful for testing, in production I use a CI/CD pipeline on a self-hosted GitHub runner.

best with CI/CD

You can use a CI/CD tool like GitHub actions to wrap credentials securely as secrets and inject to your Autopkg action(script). I'm running an adapted version of the example provided by Gusto in production. Sharing a public version of the adapted code as documentation and/or demo is on the roadmap.


Available Input Variables

All start with "ws1_"

When working to set up GitHub CI with this processor, it became clear consistent naming for input variables will make reading logs etc. much easier.

Choose your input vars

You'll need to specify credentials for either Oauth or Basic authentication.

ws1_force_import and ws1_import_new_only are intended for troubleshooting (new) recipes.

ws1_console_url is there as a convenience, so you can get a direct link to a newly imported package in the WS1 console.

ws1_smart_group_name and ws1_push_mode let you make simple App Assignments to Assignment Groups, while ws1_app_assignments gives you complete control over the App Assignment settings, but needs more settings in the recipe override.

ws1_app_versions_prune lets you prune old software versions, it is set to dry_run per default. Behaviour can be controlled in detail by setting ws1_app_versions_to_keep and ws1_app_versions_to_keep_default.

List available input variables

You can list the custom processor info, including input variables from cli like so:

autopkg processor-info WorkSpaceOneImporter --recipe com.github.codeskipper.WorkSpaceOneImporter

Sample Processor

<key>Process</key>
<array>
	<dict>
		<key>Processor</key>
		<string>com.github.codeskipper.WorkSpaceOneImporter/WorkSpaceOneImporter</string>
		<key>Arguments</key>
		<dict>
			<key>ws1_munki_repo_path</key>
			<string>MUNKI_REPO_PATH_HERE</string>
			<key>ws1_api_token</key>
			<string>API_TOKEN_HERE</string>
			<key>ws1_api_url</key>
			<string>WORKSPACEONE_API_URL_HERE</string>
			<key>ws1_console_url</key>
			<string>WORKSPACEONE_CONSOLE_URL_HERE</string>
			<key>ws1_api_username</key>
			<string>API_USERNAME_HERE</string>
			<key>ws1_api_password</key>
			<string>API_PASSWORD_HERE</string>
			<key>ws1_b64encoded_api_credentials</key>
		    <string>Basic QVBJX1VTRVJOQU1FX0hFUkU6QVBJX1BBU1NXT1JEX0hFUkU=</string>
			<key>ws1_groupid</key>
			<string>GROUP_ID_HERE</string>
			<key>ws1_smart_group_name</key>
			<string>SMART_GROUP_NAME</string>
			<key>ws1_push_mode</key>
			<string>PUSH_MODE, Auto or On-Demand</string>
		</dict>
	</dict>
</array>

Example AutoPkg Recipe

Example recipe below for Suspicious Package.ws1.recipe is from: https://github.com/codeskipper/WorkSpaceOneImporter/blob/main/ws1-plist/SuspiciousPackage.ws1-plist.recipe

<?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>Description</key>
	<string>Creates a munki package for Suspicious Package, imports it into WorkSpace ONE.</string>
	<key>Identifier</key>
	<string>com.github.codeskipper.ws1.SuspiciousPackage</string>
	<key>Input</key>
	<dict>
		<key>WS1_API_TOKEN</key>
		<string>API_TOKEN_HERE</string>
		<key>WS1_API_USERNAME</key>
		<string>API_USERNAME_HERE</string>
		<key>WS1_API_PASSWORD</key>
		<string>API_PASSWORD_HERE</string>
		<key>WS1_MUNKI_REPO_PATH</key>
		<string>MUNKI_REPO_PATH_HERE</string>
		<key>WS1_API_URL</key>
		<string>WORKSPACEONE_API_URL_HERE</string>
		<key>WS1_CONSOLE_URL</key>
		<string>WORKSPACEONE_CONSOLE_URL_HERE</string>
		<key>WS1_GROUPID</key>
		<string>GROUP_ID_HERE</string>
		<key>WS1_SMART_GROUP_NAME</key>
		<string>SMART_GROUP_NAME_HERE</string>
		<key>WS1_PUSH_MODE</key>
		<string>PUSH_MODE</string>
	</dict>
	<key>ParentRecipe</key>
	<string>com.github.codeskipper.munki.SuspiciousPackage</string>
	<key>MinimumVersion</key>
	<string>0.4.0</string>
	<key>Process</key>
	<array>
		<dict>
			<key>Processor</key>
			<string>com.github.codeskipper.WorkSpaceOneImporter/WorkSpaceOneImporter</string>
			<key>Arguments</key>
			<dict>
				<key>ws1_munki_repo_path</key>
				<string>%WS1_MUNKI_REPO_PATH%</string>
				<key>ws1_api_token</key>
				<string>%WS1_API_TOKEN%</string>
				<key>ws1_api_username</key>
				<string>%WS1_API_USERNAME%</string>
				<key>ws1_api_password</key>
				<string>%WS1_API_PASSWORD%</string>
				<key>ws1_api_url</key>
				<string>%WS1_API_URL%</string>
				<key>ws1_groupid</key>
				<string>%WS1_GROUPID%</string>
				<key>ws1_console_url</key>
				<string>%WS1_CONSOLE_URL%</string>
				<key>ws1_smart_group_name</key>
				<string>%WS1_SMART_GROUP_NAME%</string>
				<key>ws1_push_mode</key>
				<string>%WS1_PUSH_MODE%</string>
			</dict>
		</dict>
	</array>
</dict>
</plist>

yaml format in recipes

My ws1 recipes are in yaml format thanks to the convincing examples from Graham Pugh's recipes. His plist-yaml-plist conversion tool has helped me as well, especially when writing new recipes.


Create a recipe override like this first (if you prefer plist format): autopkg make-override SuspiciousPackage.ws1.recipe

Again yaml format is easier to deal with, especially if you leave only the input variables you need to override and strip away the rest. autopkg make-override --format=yaml SuspiciousPackage.ws1.recipe.yaml


Edit it for settings to fit your environment

open -a bbedit SuspiciousPackage.ws1.recipe.yaml


Testing

You can run autopkg like this, but be aware: verbose level > 2 will show your password etc. in plaintext on screen

autopkg run -vvvv --key ws1_import_new_only=false --key ws1_update_assignments=true SuspiciousPackage.ws1.recipe.yaml

When testing, you may need to specify an increasing number of settings. You may find it helpful to set these like shell environment variables like so.

export AUTOPKG_verbose=2
export AUTOPKG_ws1_import_new_only=false
export AUTOPKG_ws1_update_assignments=True
autopkg run SuspiciousPackage.ws1.recipe.yaml

During development, I've used a launcher script to store (secret) settings in a dedicated keychain and fetch just before calling autopkg. Sharing this script is on the roadmap.


code-style and pre-commit hooks

Uses the same environment as Autopkg.

Install the pre-commit hook like so:

/Library/AutoPkg/Python3/Python.framework/Versions/Current/bin/pre-commit install --install-hooks

isort needed profile = "black" in .isort.cfg and args: ["--profile", "black"] in .pre-commit-config.yaml to avoid conflict with black

line-length was set at 120 in .isort.cfg and in .flake8 just because it was the default I got used to in PyCharm

Found a useful hint to integrate flake8 in PyCharm as external tool here.

Check all the files:

/Library/AutoPkg/Python3/Python.framework/Versions/Current/bin/pre-commit run --all-files

About

Import items from AutoPkg to Omnissa Workspace ONE (formerly VMware, Airwatch)

Topics

Resources

Stars

Watchers

Forks

Languages

  • Python 100.0%