From 275af1349860a26d7bc9354a660ccca785491327 Mon Sep 17 00:00:00 2001 From: Geoffrey Wein Date: Tue, 24 Mar 2026 08:11:59 -0600 Subject: [PATCH 1/4] only install for managed users --- ... Install JumpCloud Password Manager App.md | 69 +++++++++++++++++-- 1 file changed, 65 insertions(+), 4 deletions(-) diff --git a/PowerShell/JumpCloud Commands Gallery/Mac Commands/Mac - Install JumpCloud Password Manager App.md b/PowerShell/JumpCloud Commands Gallery/Mac Commands/Mac - Install JumpCloud Password Manager App.md index 57019d7cf..341f49013 100644 --- a/PowerShell/JumpCloud Commands Gallery/Mac Commands/Mac - Install JumpCloud Password Manager App.md +++ b/PowerShell/JumpCloud Commands Gallery/Mac Commands/Mac - Install JumpCloud Password Manager App.md @@ -26,6 +26,44 @@ DownloadYamlFileUrl="https://cdn.pwm.jumpcloud.com/DA/release/latest-mac.yml" # Detect device architecture DeviceArchitecture=$(uname -m) +# JumpCloud managed users list (only these accounts receive install/update) +MANAGED_USERS_FILE="/opt/jc/managedUsers.json" +managed_usernames=() + +load_managed_usernames() { + managed_usernames=() + if [[ ! -f "$MANAGED_USERS_FILE" ]] || [[ ! -r "$MANAGED_USERS_FILE" ]]; then + echo "Error: Managed users file not found or not readable: $MANAGED_USERS_FILE" >&2 + return 1 + fi + + if command -v jq >/dev/null 2>&1; then + while IFS= read -r line || [[ -n "$line" ]]; do + [[ -n "$line" ]] && managed_usernames+=("$line") + done < <(jq -r '.[] | select(.username != null and .username != "") | .username' "$MANAGED_USERS_FILE" 2>/dev/null) + else + # Fallback without jq: extract "username":"..." (does not support escaped quotes inside usernames) + while IFS= read -r line || [[ -n "$line" ]]; do + [[ -n "$line" ]] && managed_usernames+=("$line") + done < <(grep -oe '"username"[[:space:]]*:[[:space:]]*"[^"]*"' "$MANAGED_USERS_FILE" 2>/dev/null | sed -E 's/^"username"[[:space:]]*:[[:space:]]*"([^"]*)".*$/\1/') + fi + + if [[ ${#managed_usernames[@]} -eq 0 ]]; then + echo "Error: No managed usernames could be parsed from $MANAGED_USERS_FILE" >&2 + return 1 + fi +} + +is_managed_user() { + local u="$1" + printf '%s\n' "${managed_usernames[@]}" | grep -Fxq -- "$u" +} + +list_local_users() { + dscl . list /Users | grep -vE 'root|daemon|nobody|^_' +} + +load_managed_usernames || exit 1 if [ "$DeviceArchitecture" = "arm64" ]; then DownloadUrl="https://cdn.pwm.jumpcloud.com/DA/release/arm64/JumpCloud-Password-Manager-latest.dmg" @@ -95,8 +133,12 @@ LatestAppVersion=$(curl -s "$DownloadYamlFileUrl" | \ # Array to track users who need update/reinstall users_need_update=() if [ "$UpdateToLatest" = true ]; then - for user in $(dscl . list /Users | grep -vE 'root|daemon|nobody|^_') + for user in $(list_local_users) do + if ! is_managed_user "$user"; then + echo "Skipping $user (not listed in $MANAGED_USERS_FILE)." + continue + fi APP_PATH="/Users/$user/Applications/JumpCloud Password Manager.app" InstalledAppVersion=$(mdls -name kMDItemVersion "$APP_PATH" 2>/dev/null | awk -F '"' '{print $2}') if [ -z "$InstalledAppVersion" ]; then @@ -126,6 +168,20 @@ if [ "$UpdateToLatest" = true ] && [ ${#users_need_update[@]} -eq 0 ]; then exit 0 fi +if [ "$UpdateToLatest" = false ]; then + any_managed_home=false + for user in $(list_local_users); do + if is_managed_user "$user" && [[ -d "/Users/$user" ]]; then + any_managed_home=true + break + fi + done + if [ "$any_managed_home" = false ]; then + echo "No local accounts match managed users in $MANAGED_USERS_FILE; nothing to install." + exit 0 + fi +fi + echo "Downloading JumpCloud Password Manager from $DownloadUrl" # Download File into Temp Folder curl -s -O "$DownloadUrl" @@ -182,7 +238,7 @@ echo "Located DMG Mount Point: $DMGMountPoint" cd "$DMGVolume" -AppName="$(ls | Grep .app)" +AppName="$(ls | grep '\.app$')" cd ~ @@ -193,8 +249,12 @@ DMGAppPath=$(find "$DMGVolume" -name "*.app" -depth 1) userInstall=false -for user in $(dscl . list /Users | grep -vE 'root|daemon|nobody|^_') +for user in $(list_local_users) do + if ! is_managed_user "$user"; then + echo "Skipping $user (not listed in $MANAGED_USERS_FILE)." + continue + fi APP_PATH="/Users/$user/Applications/JumpCloud Password Manager.app" if [[ -d /Users/$user ]]; then # Create ~/Applications folder @@ -254,7 +314,8 @@ echo "Used hdiutil to detach $DMGFile from $DMGMountPoint" err=$? if [ ${err} -ne 0 ]; then - abort "Could not detach DMG: $DMGMountPoint Error: ${err}" + echo "Could not detach DMG: $DMGMountPoint Error: ${err}" >&2 + exit 1 fi # Remove Temp Folder and download From 4e90fa30b8458785ecb1de1288e9d8eb733fbc09 Mon Sep 17 00:00:00 2001 From: Geoffrey Wein Date: Tue, 24 Mar 2026 11:25:46 -0600 Subject: [PATCH 2/4] managed user check --- ... Install JumpCloud Password Manager App.md | 44 ++++++++++--------- 1 file changed, 23 insertions(+), 21 deletions(-) diff --git a/PowerShell/JumpCloud Commands Gallery/Windows Commands/Windows - Install JumpCloud Password Manager App.md b/PowerShell/JumpCloud Commands Gallery/Windows Commands/Windows - Install JumpCloud Password Manager App.md index 5479d052c..b294c2aea 100644 --- a/PowerShell/JumpCloud Commands Gallery/Windows Commands/Windows - Install JumpCloud Password Manager App.md +++ b/PowerShell/JumpCloud Commands Gallery/Windows Commands/Windows - Install JumpCloud Password Manager App.md @@ -1,9 +1,7 @@ #### Name - Windows - Install and Update JumpCloud Password Manager App | v2.0.0 JCCG - #### commandType windows @@ -22,6 +20,13 @@ $updateToLatest = $true $loggedUser = Get-WmiObject -Class Win32_ComputerSystem | Select-Object -ExpandProperty UserName $loggedUser = $loggedUser -replace '.*\\' +# Get managed users list +$managedUsers = Get-Content -Path "$env:ProgramFiles\JumpCloud\Plugins\Contrib\managedUsers.json" | ConvertFrom-Json +if ($managedUsers.username -notcontains $loggedUser) { + Write-Output "User $loggedUser is not a managed user, exiting." + exit 1 +} + # Construct the Registry path using the user's SID $userSID = (New-Object System.Security.Principal.NTAccount($loggedUser)).Translate([System.Security.Principal.SecurityIdentifier]).Value $registryPath = "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList\$userSID" @@ -34,14 +39,12 @@ Write-Output "Logged On User Profile Path: $loggedOnUserProfileImagePath" $appDataPath = "$loggedOnUserProfileImagePath\AppData\Local\jcpwm" $installerURL = 'https://cdn.pwm.jumpcloud.com/DA/release/JumpCloud-Password-Manager-latest.exe' -$yamlFileURL = 'https://cdn.pwm.jumpcloud.com/DA/release/latest.yml' +$yamlFileURL = 'https://cdn.pwm.jumpcloud.com/DA/release/latest.yml' # If user already has the app installed and admin wants to update to latest if ((Test-Path "$appDataPath") -and ($updateToLatest -eq $true)) { $folderPrefix = "app-" - $versionFolders = Get-ChildItem -Path $appDataPath -Directory | - Where-Object { $_.Name -like "$($folderPrefix)*" } | - Sort-Object Name -Descending + $versionFolders = Get-ChildItem -Path $appDataPath -Directory | Where-Object { $_.Name -like "$($folderPrefix)*" } | Sort-Object Name -Descending if ($versionFolders.Count -gt 0) { # Get the name of the top (latest) matching folder (app-x.x.x) @@ -60,10 +63,9 @@ if (Test-Path "$loggedOnUserProfileImagePath\AppData\Local\Temp" ) { $installerTempLocation = "$loggedOnUserProfileImagePath\AppData\Local\Temp\JumpCloud-Password-Manager-latest.exe" $yamlFileTempLocation = "$loggedOnUserProfileImagePath\AppData\Local\Temp\jcpwm-latest.yml" Write-Output "Installer Location: $installerTempLocation" -} -else { +} else { Write-Output "Unable to determine user profile folder" - Exit 1 + exit 1 } if ($updateToLatest -eq $true) { @@ -78,7 +80,7 @@ if ($updateToLatest -eq $true) { try { Invoke-WebRequest -Uri $yamlFileURL -OutFile $yamlFileTempLocation } catch { - Write-Error "Unable to download Password Manager latest yml file to $yamlFileTempLocation." + Write-Output "Unable to download Password Manager latest yml file to $yamlFileTempLocation." exit 1 } Write-Output 'Finished downloading Password Manager installer.' @@ -91,15 +93,15 @@ if ($updateToLatest -eq $true) { Write-Output "Checking for version in YAML file: $yamlFileTempLocation" Write-Output "Version Line: $versionLine" if ($versionLine) { - # Extract the version number from the matched line - # The 'Groups[1]' captures the content after 'version: ' - [System.Version]$latestVersion = $versionLine.Matches[0].Groups[1].Value.Trim() - Write-Output "Latest version: $latestVersion" - # If the admin has previously installed the dogfood/beta version of the app for the users - # it might be greater than the version found under the $installerURL. - if ($currentInstalledAppVersion -ge $latestVersion) { + # Extract the version number from the matched line + # The 'Groups[1]' captures the content after 'version: ' + [System.Version]$latestVersion = $versionLine.Matches[0].Groups[1].Value.Trim() + Write-Output "Latest version: $latestVersion" + # If the admin has previously installed the dogfood/beta version of the app for the users + # it might be greater than the version found under the $installerURL. + if ($currentInstalledAppVersion -ge $latestVersion) { Write-Output "App is already up to date, exiting." - Exit 0 + exit 0 } } else { Write-Warning "Could not find 'version' in the YAML file, falling back to full download." @@ -120,7 +122,7 @@ if (-not(Test-Path -Path $installerTempLocation -PathType Leaf)) { try { Invoke-WebRequest -Uri $installerURL -OutFile $installerTempLocation } catch { - Write-Error "Unable to download Password Manager installer to $InstallerTempLocation." + Write-Output "Unable to download Password Manager installer to $installerTempLocation." exit 1 } Write-Output 'Finished downloading Password Manager installer.' @@ -152,7 +154,7 @@ $Command = { $LaunchPasswordManager = $true $installerTempLocation = "$loggedOnUserProfileImagePath\AppData\Local\Temp\JumpCloud-Password-Manager-latest.exe" if ($LaunchPasswordManager -eq $false) { - $env:QUIT_PWM_AFTER_INITIAL_INSTALL="true" + $env:QUIT_PWM_AFTER_INITIAL_INSTALL = "true" } . $installerTempLocation @@ -182,7 +184,7 @@ $Command = { $startMenuShortcut.WorkingDirectory = "$appDataPath" $startMenuShortcut.Save() Write-Output "Start Menu Shortcut created." - } + } } $Source = @' From 9b18a63868a08c544afbb2ad2568bec31ba4be87 Mon Sep 17 00:00:00 2001 From: Geoffrey Wein Date: Tue, 24 Mar 2026 11:27:36 -0600 Subject: [PATCH 3/4] versioning --- .../Mac - Install JumpCloud Password Manager App.md | 2 +- .../Windows - Install JumpCloud Password Manager App.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/PowerShell/JumpCloud Commands Gallery/Mac Commands/Mac - Install JumpCloud Password Manager App.md b/PowerShell/JumpCloud Commands Gallery/Mac Commands/Mac - Install JumpCloud Password Manager App.md index 341f49013..a62717335 100644 --- a/PowerShell/JumpCloud Commands Gallery/Mac Commands/Mac - Install JumpCloud Password Manager App.md +++ b/PowerShell/JumpCloud Commands Gallery/Mac Commands/Mac - Install JumpCloud Password Manager App.md @@ -1,6 +1,6 @@ #### Name -Mac - Install and Update JumpCloud Password Manager App | v2.0.1 JCCG +Mac - Install and Update JumpCloud Password Manager App | v2.0.2 JCCG #### commandType diff --git a/PowerShell/JumpCloud Commands Gallery/Windows Commands/Windows - Install JumpCloud Password Manager App.md b/PowerShell/JumpCloud Commands Gallery/Windows Commands/Windows - Install JumpCloud Password Manager App.md index b294c2aea..cf1f16691 100644 --- a/PowerShell/JumpCloud Commands Gallery/Windows Commands/Windows - Install JumpCloud Password Manager App.md +++ b/PowerShell/JumpCloud Commands Gallery/Windows Commands/Windows - Install JumpCloud Password Manager App.md @@ -1,6 +1,6 @@ #### Name -Windows - Install and Update JumpCloud Password Manager App | v2.0.0 JCCG +Windows - Install and Update JumpCloud Password Manager App | v2.0.1 JCCG #### commandType From 9580a7d45190244c3ee2c8cc2b503da2647d4e56 Mon Sep 17 00:00:00 2001 From: Geoffrey Wein Date: Tue, 24 Mar 2026 11:30:33 -0600 Subject: [PATCH 4/4] Update commands.json --- PowerShell/JumpCloud Commands Gallery/commands.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/PowerShell/JumpCloud Commands Gallery/commands.json b/PowerShell/JumpCloud Commands Gallery/commands.json index d97c72412..13dabdf1c 100644 --- a/PowerShell/JumpCloud Commands Gallery/commands.json +++ b/PowerShell/JumpCloud Commands Gallery/commands.json @@ -147,9 +147,9 @@ "description": "This command will download and install the Sentinel One Agent to the device if it isn't already installed.\n\nSpecifically for this command:\n\n1. Download the Sentinel One Agent installer and host it at a URL that your devices can access. Set this URL to the \"DownloadURL\" of this script.\n2. Extend the command timeout to a value that makes sense in your environment. The suggested command timeout for an environment with average network speeds on devices with average computing power is 10 minutes. Note that the command may timeout with a 124 error code in the command result window if not extended, but the script will continue to run." }, { - "name": "Mac - Install and Update JumpCloud Password Manager App | v2.0.1 JCCG", + "name": "Mac - Install and Update JumpCloud Password Manager App | v2.0.2 JCCG", "type": "mac", - "command": "#!/bin/bash\n# This script will install password manager in Users/$user/Applications for all user accounts based on their architecture (x64 or arm64)\n# Set LaunchAfterInstall to true ON LINE 4 if you wish to launch the password manager after installation\nLaunchAfterInstall=true\n\n# Set UpdateToLatest to true if you want to update to the latest version of JumpCloud Password Manager\n# Set UpdateToLatest to false if you want to re-install the JumpCloud Password Manager no matter your current version.\n# ********** DISCLAIMER: Setting UpdateToLatest to $false will NOT affect any user data **********\nUpdateToLatest=true\n\n\nDownloadUrl=\"https://cdn.pwm.jumpcloud.com/DA/release/JumpCloud-Password-Manager-latest.dmg\"\nDownloadYamlFileUrl=\"https://cdn.pwm.jumpcloud.com/DA/release/latest-mac.yml\"\n\n# Detect device architecture\nDeviceArchitecture=$(uname -m)\n\n\nif [ \"$DeviceArchitecture\" = \"arm64\" ]; then\n DownloadUrl=\"https://cdn.pwm.jumpcloud.com/DA/release/arm64/JumpCloud-Password-Manager-latest.dmg\"\n DownloadYamlFileUrl=\"https://cdn.pwm.jumpcloud.com/DA/release/arm64/latest-mac.yml\"\nfi\n\n# Kill any existing JumpCloud Password Manager processes\nkillall \"JumpCloud Password Manager\"\n\nregex='^https.*.dmg$'\nif [[ $DownloadUrl =~ $regex ]]; then\n echo \"URL points to direct DMG download\"\n validLink=\"True\"\nelse\n echo \"Searching headers for download links\"\n urlHead=$(curl -s --head $DownloadUrl)\n\n locationSearch=$(echo \"$urlHead\" | grep https:)\n\n if [ -n \"$locationSearch\" ]; then\n\n locationRaw=$(echo \"$locationSearch\" | cut -d' ' -f2)\n\n locationFormatted=\"$(echo \"${locationRaw}\" | tr -d '[:space:]')\"\n\n regex='^https.*'\n if [[ $locationFormatted =~ $regex ]]; then\n echo \"Download link found\"\n DownloadUrl=$(echo \"$locationFormatted\")\n else\n echo \"No https location download link found in headers\"\n exit 1\n fi\n\n else\n\n echo \"No location download link found in headers\"\n exit 1\n fi\n\nfi\n\n\n\n#Create Temp Folder\nDATE=$(date '+%Y-%m-%d-%H-%M-%S')\n\nTempFolder=\"Download-$DATE\"\n\nmkdir /tmp/$TempFolder\n\n# Navigate to Temp Folder\ncd /tmp/$TempFolder\n\nif [ -d /Applications/JumpCloud\\ Password\\ Manager.app ]; then\n # If JumpCloud Password Manager exists within this directory, force re-install as it is in the wrong directory\n # the script will continue and re-install it in the correct directory\n UpdateToLatest=false\nfi\n\nLatestAppVersion=$(curl -s \"$DownloadYamlFileUrl\" | \\\n grep '^version:' | \\\n sed -E 's/version:\\s*//g' | \\\n tr -d '\\r' | \\\n xargs)\n\n# Array to track users who need update/reinstall\nusers_need_update=()\nif [ \"$UpdateToLatest\" = true ]; then\n for user in $(dscl . list /Users | grep -vE 'root|daemon|nobody|^_')\n do\n APP_PATH=\"/Users/$user/Applications/JumpCloud Password Manager.app\"\n InstalledAppVersion=$(mdls -name kMDItemVersion \"$APP_PATH\" 2>/dev/null | awk -F '\"' '{print $2}')\n if [ -z \"$InstalledAppVersion\" ]; then\n echo \"Could not determine installed app version from '$APP_PATH' for $user.\"\n echo \"User $user will be treated as a re-install.\"\n users_need_update+=(\"$user\")\n # Skipping over the current user will prevent unnecessary logic handling.\n continue\n fi\n\n if [[ \"$(printf '%s\\n%s\\n' \"$InstalledAppVersion\" \"$LatestAppVersion\" | sort -V | head -n1)\" = \"$InstalledAppVersion\" && \"$InstalledAppVersion\" != \"$LatestAppVersion\" ]]; then\n echo \"Installed app ($InstalledAppVersion) is OLDER than the latest available ($LatestAppVersion).\"\n users_need_update+=(\"$user\")\n elif [[ \"$(printf '%s\\n%s\\n' \"$InstalledAppVersion\" \"$LatestAppVersion\" | sort -V | tail -n1)\" = \"$InstalledAppVersion\" && \"$InstalledAppVersion\" != \"$LatestAppVersion\" ]]; then\n echo \"Installed app ($InstalledAppVersion) is NEWER than the latest available ($LatestAppVersion).\"\n echo \"This might indicate a beta version.\"\n continue\n else\n echo \"Installed app is already the LATEST version ($InstalledAppVersion) for $user.\"\n continue\n fi\n done\nfi\n\nif [ \"$UpdateToLatest\" = true ] && [ ${#users_need_update[@]} -eq 0 ]; then\n echo \"All users are up to date, exiting.\"\n exit 0\nfi\n\necho \"Downloading JumpCloud Password Manager from $DownloadUrl\"\n# Download File into Temp Folder\ncurl -s -O \"$DownloadUrl\"\n\necho \"Download complete.\"\n# Capture name of Download File\nDownloadFile=\"$(ls)\"\n\necho \"Downloaded $DownloadFile to /tmp/$TempFolder\"\n\n# Verifies DMG File\nregex='\\.dmg$'\nif [[ $DownloadFile =~ $regex ]]; then\n DMGFile=\"$(echo \"$DownloadFile\")\"\n echo \"DMG File Found: $DMGFile\"\nelse\n echo \"File: $DownloadFile is not a DMG\"\n rm -r /tmp/$TempFolder\n echo \"Deleted /tmp/$TempFolder\"\n exit 1\nfi\n\n# Mount DMG File -nobrowse prevents the volume from popping up in Finder\n\nhdiutilAttach=$(hdiutil attach /tmp/$TempFolder/$DMGFile -nobrowse)\n\necho \"Used hdiutil to mount $DMGFile \"\n\nerr=$?\nif [ ${err} -ne 0 ]; then\n echo \"Could not mount $DMGFile Error: ${err}\"\n rm -r /tmp/$TempFolder\n echo \"Deleted /tmp/$TempFolder\"\n exit 1\nfi\n\nregex='\\/Volumes\\/.*'\nif [[ $hdiutilAttach =~ $regex ]]; then\n DMGVolume=\"${BASH_REMATCH[@]}\"\n echo \"Located DMG Volume: $DMGVolume\"\nelse\n echo \"DMG Volume not found\"\n rm -r /tmp/$TempFolder\n echo \"Deleted /tmp/$TempFolder\"\n exit 1\nfi\n\n# Identify the mount point for the DMG file\nDMGMountPoint=\"$(hdiutil info | grep \"$DMGVolume\" | awk '{ print $1 }')\"\n\necho \"Located DMG Mount Point: $DMGMountPoint\"\n\n# Capture name of App file\n\ncd \"$DMGVolume\"\n\nAppName=\"$(ls | Grep .app)\"\n\ncd ~\n\necho \"Located App: $AppName\"\n\n\nDMGAppPath=$(find \"$DMGVolume\" -name \"*.app\" -depth 1)\n\nuserInstall=false\n\nfor user in $(dscl . list /Users | grep -vE 'root|daemon|nobody|^_')\ndo\n APP_PATH=\"/Users/$user/Applications/JumpCloud Password Manager.app\"\n if [[ -d /Users/$user ]]; then\n # Create ~/Applications folder\n if [[ ! -d /Users/$user/Applications ]]; then\n mkdir /Users/$user/Applications\n fi\n if [[ -d $APP_PATH ]]; then\n # remove if exists\n rm -rf $APP_PATH\n fi\n\n # Copy the contents of the DMG file to /Users/$user/Applications/\n # Preserves all file attributes and ACLs\n cp -pPR \"$DMGAppPath\" /Users/$user/Applications/\n\n # Change ownership of the file and Applications folder to the user of this loop iteration\n chown -v \"$user\" /Users/$user/Applications\n chown -R -v \"$user\" \"$APP_PATH\" >/dev/null 2>&1 || true\n echo \"Changed ownership of $APP_PATH and /Users/$user/Applications to $user\"\n\n if [[ -d /Users/$user/Desktop/JumpCloud\\ Password\\ Manager.app ]]; then\n # remove alias on desktop if exists\n rm -rf /Users/$user/Desktop/JumpCloud\\ Password\\ Manager.app\n fi\n\n err=$?\n if [ ${err} -ne 0 ]; then\n echo \"Could not copy $DMGAppPath Error: ${err}\"\n hdiutil detach $DMGMountPoint\n echo \"Used hdiutil to detach $DMGFile from $DMGMountPoint\"\n rm -r /tmp/$TempFolder\n echo \"Deleted /tmp/$TempFolder\"\n exit 1\n fi\n\n userInstall=true\n echo \"Copied $DMGAppPath to /Users/$user/Applications\"\n if [ \"$LaunchAfterInstall\" = true ]; then\n sudo -u \"$user\" open \"$APP_PATH\" >/dev/null 2>&1 || true\n fi\n # Create an alias on desktop\n ln -s \"$APP_PATH\" \"/Users/$user/Desktop/JumpCloud Password Manager.app\"\n fi\ndone\n\n\n# Check if password manager is installed in /Applications; remove if we installed in user directory\nif [ -d /Applications/JumpCloud\\ Password\\ Manager.app ] && [ $userInstall = true ]; then\n # remove if exists\n rm -rf /Applications/JumpCloud\\ Password\\ Manager.app\nfi\n\n# Unmount the DMG file\nhdiutil detach $DMGMountPoint\n\necho \"Used hdiutil to detach $DMGFile from $DMGMountPoint\"\n\nerr=$?\nif [ ${err} -ne 0 ]; then\n abort \"Could not detach DMG: $DMGMountPoint Error: ${err}\"\nfi\n\n# Remove Temp Folder and download\nrm -r /tmp/$TempFolder\n\necho \"Deleted /tmp/$TempFolder\"\n\n\nexit", + "command": "#!/bin/bash\n# This script will install password manager in Users/$user/Applications for all user accounts based on their architecture (x64 or arm64)\n# Set LaunchAfterInstall to true ON LINE 4 if you wish to launch the password manager after installation\nLaunchAfterInstall=true\n\n# Set UpdateToLatest to true if you want to update to the latest version of JumpCloud Password Manager\n# Set UpdateToLatest to false if you want to re-install the JumpCloud Password Manager no matter your current version.\n# ********** DISCLAIMER: Setting UpdateToLatest to $false will NOT affect any user data **********\nUpdateToLatest=true\n\n\nDownloadUrl=\"https://cdn.pwm.jumpcloud.com/DA/release/JumpCloud-Password-Manager-latest.dmg\"\nDownloadYamlFileUrl=\"https://cdn.pwm.jumpcloud.com/DA/release/latest-mac.yml\"\n\n# Detect device architecture\nDeviceArchitecture=$(uname -m)\n\n# JumpCloud managed users list (only these accounts receive install/update)\nMANAGED_USERS_FILE=\"/opt/jc/managedUsers.json\"\nmanaged_usernames=()\n\nload_managed_usernames() {\n managed_usernames=()\n if [[ ! -f \"$MANAGED_USERS_FILE\" ]] || [[ ! -r \"$MANAGED_USERS_FILE\" ]]; then\n echo \"Error: Managed users file not found or not readable: $MANAGED_USERS_FILE\" >&2\n return 1\n fi\n\n if command -v jq >/dev/null 2>&1; then\n while IFS= read -r line || [[ -n \"$line\" ]]; do\n [[ -n \"$line\" ]] && managed_usernames+=(\"$line\")\n done < <(jq -r '.[] | select(.username != null and .username != \"\") | .username' \"$MANAGED_USERS_FILE\" 2>/dev/null)\n else\n # Fallback without jq: extract \"username\":\"...\" (does not support escaped quotes inside usernames)\n while IFS= read -r line || [[ -n \"$line\" ]]; do\n [[ -n \"$line\" ]] && managed_usernames+=(\"$line\")\n done < <(grep -oe '\"username\"[[:space:]]*:[[:space:]]*\"[^\"]*\"' \"$MANAGED_USERS_FILE\" 2>/dev/null | sed -E 's/^\"username\"[[:space:]]*:[[:space:]]*\"([^\"]*)\".*$/\\1/')\n fi\n\n if [[ ${#managed_usernames[@]} -eq 0 ]]; then\n echo \"Error: No managed usernames could be parsed from $MANAGED_USERS_FILE\" >&2\n return 1\n fi\n}\n\nis_managed_user() {\n local u=\"$1\"\n printf '%s\\n' \"${managed_usernames[@]}\" | grep -Fxq -- \"$u\"\n}\n\nlist_local_users() {\n dscl . list /Users | grep -vE 'root|daemon|nobody|^_'\n}\n\nload_managed_usernames || exit 1\n\nif [ \"$DeviceArchitecture\" = \"arm64\" ]; then\n DownloadUrl=\"https://cdn.pwm.jumpcloud.com/DA/release/arm64/JumpCloud-Password-Manager-latest.dmg\"\n DownloadYamlFileUrl=\"https://cdn.pwm.jumpcloud.com/DA/release/arm64/latest-mac.yml\"\nfi\n\n# Kill any existing JumpCloud Password Manager processes\nkillall \"JumpCloud Password Manager\"\n\nregex='^https.*.dmg$'\nif [[ $DownloadUrl =~ $regex ]]; then\n echo \"URL points to direct DMG download\"\n validLink=\"True\"\nelse\n echo \"Searching headers for download links\"\n urlHead=$(curl -s --head $DownloadUrl)\n\n locationSearch=$(echo \"$urlHead\" | grep https:)\n\n if [ -n \"$locationSearch\" ]; then\n\n locationRaw=$(echo \"$locationSearch\" | cut -d' ' -f2)\n\n locationFormatted=\"$(echo \"${locationRaw}\" | tr -d '[:space:]')\"\n\n regex='^https.*'\n if [[ $locationFormatted =~ $regex ]]; then\n echo \"Download link found\"\n DownloadUrl=$(echo \"$locationFormatted\")\n else\n echo \"No https location download link found in headers\"\n exit 1\n fi\n\n else\n\n echo \"No location download link found in headers\"\n exit 1\n fi\n\nfi\n\n\n\n#Create Temp Folder\nDATE=$(date '+%Y-%m-%d-%H-%M-%S')\n\nTempFolder=\"Download-$DATE\"\n\nmkdir /tmp/$TempFolder\n\n# Navigate to Temp Folder\ncd /tmp/$TempFolder\n\nif [ -d /Applications/JumpCloud\\ Password\\ Manager.app ]; then\n # If JumpCloud Password Manager exists within this directory, force re-install as it is in the wrong directory\n # the script will continue and re-install it in the correct directory\n UpdateToLatest=false\nfi\n\nLatestAppVersion=$(curl -s \"$DownloadYamlFileUrl\" | \\\n grep '^version:' | \\\n sed -E 's/version:\\s*//g' | \\\n tr -d '\\r' | \\\n xargs)\n\n# Array to track users who need update/reinstall\nusers_need_update=()\nif [ \"$UpdateToLatest\" = true ]; then\n for user in $(list_local_users)\n do\n if ! is_managed_user \"$user\"; then\n echo \"Skipping $user (not listed in $MANAGED_USERS_FILE).\"\n continue\n fi\n APP_PATH=\"/Users/$user/Applications/JumpCloud Password Manager.app\"\n InstalledAppVersion=$(mdls -name kMDItemVersion \"$APP_PATH\" 2>/dev/null | awk -F '\"' '{print $2}')\n if [ -z \"$InstalledAppVersion\" ]; then\n echo \"Could not determine installed app version from '$APP_PATH' for $user.\"\n echo \"User $user will be treated as a re-install.\"\n users_need_update+=(\"$user\")\n # Skipping over the current user will prevent unnecessary logic handling.\n continue\n fi\n\n if [[ \"$(printf '%s\\n%s\\n' \"$InstalledAppVersion\" \"$LatestAppVersion\" | sort -V | head -n1)\" = \"$InstalledAppVersion\" && \"$InstalledAppVersion\" != \"$LatestAppVersion\" ]]; then\n echo \"Installed app ($InstalledAppVersion) is OLDER than the latest available ($LatestAppVersion).\"\n users_need_update+=(\"$user\")\n elif [[ \"$(printf '%s\\n%s\\n' \"$InstalledAppVersion\" \"$LatestAppVersion\" | sort -V | tail -n1)\" = \"$InstalledAppVersion\" && \"$InstalledAppVersion\" != \"$LatestAppVersion\" ]]; then\n echo \"Installed app ($InstalledAppVersion) is NEWER than the latest available ($LatestAppVersion).\"\n echo \"This might indicate a beta version.\"\n continue\n else\n echo \"Installed app is already the LATEST version ($InstalledAppVersion) for $user.\"\n continue\n fi\n done\nfi\n\nif [ \"$UpdateToLatest\" = true ] && [ ${#users_need_update[@]} -eq 0 ]; then\n echo \"All users are up to date, exiting.\"\n exit 0\nfi\n\nif [ \"$UpdateToLatest\" = false ]; then\n any_managed_home=false\n for user in $(list_local_users); do\n if is_managed_user \"$user\" && [[ -d \"/Users/$user\" ]]; then\n any_managed_home=true\n break\n fi\n done\n if [ \"$any_managed_home\" = false ]; then\n echo \"No local accounts match managed users in $MANAGED_USERS_FILE; nothing to install.\"\n exit 0\n fi\nfi\n\necho \"Downloading JumpCloud Password Manager from $DownloadUrl\"\n# Download File into Temp Folder\ncurl -s -O \"$DownloadUrl\"\n\necho \"Download complete.\"\n# Capture name of Download File\nDownloadFile=\"$(ls)\"\n\necho \"Downloaded $DownloadFile to /tmp/$TempFolder\"\n\n# Verifies DMG File\nregex='\\.dmg$'\nif [[ $DownloadFile =~ $regex ]]; then\n DMGFile=\"$(echo \"$DownloadFile\")\"\n echo \"DMG File Found: $DMGFile\"\nelse\n echo \"File: $DownloadFile is not a DMG\"\n rm -r /tmp/$TempFolder\n echo \"Deleted /tmp/$TempFolder\"\n exit 1\nfi\n\n# Mount DMG File -nobrowse prevents the volume from popping up in Finder\n\nhdiutilAttach=$(hdiutil attach /tmp/$TempFolder/$DMGFile -nobrowse)\n\necho \"Used hdiutil to mount $DMGFile \"\n\nerr=$?\nif [ ${err} -ne 0 ]; then\n echo \"Could not mount $DMGFile Error: ${err}\"\n rm -r /tmp/$TempFolder\n echo \"Deleted /tmp/$TempFolder\"\n exit 1\nfi\n\nregex='\\/Volumes\\/.*'\nif [[ $hdiutilAttach =~ $regex ]]; then\n DMGVolume=\"${BASH_REMATCH[@]}\"\n echo \"Located DMG Volume: $DMGVolume\"\nelse\n echo \"DMG Volume not found\"\n rm -r /tmp/$TempFolder\n echo \"Deleted /tmp/$TempFolder\"\n exit 1\nfi\n\n# Identify the mount point for the DMG file\nDMGMountPoint=\"$(hdiutil info | grep \"$DMGVolume\" | awk '{ print $1 }')\"\n\necho \"Located DMG Mount Point: $DMGMountPoint\"\n\n# Capture name of App file\n\ncd \"$DMGVolume\"\n\nAppName=\"$(ls | grep '\\.app$')\"\n\ncd ~\n\necho \"Located App: $AppName\"\n\n\nDMGAppPath=$(find \"$DMGVolume\" -name \"*.app\" -depth 1)\n\nuserInstall=false\n\nfor user in $(list_local_users)\ndo\n if ! is_managed_user \"$user\"; then\n echo \"Skipping $user (not listed in $MANAGED_USERS_FILE).\"\n continue\n fi\n APP_PATH=\"/Users/$user/Applications/JumpCloud Password Manager.app\"\n if [[ -d /Users/$user ]]; then\n # Create ~/Applications folder\n if [[ ! -d /Users/$user/Applications ]]; then\n mkdir /Users/$user/Applications\n fi\n if [[ -d $APP_PATH ]]; then\n # remove if exists\n rm -rf $APP_PATH\n fi\n\n # Copy the contents of the DMG file to /Users/$user/Applications/\n # Preserves all file attributes and ACLs\n cp -pPR \"$DMGAppPath\" /Users/$user/Applications/\n\n # Change ownership of the file and Applications folder to the user of this loop iteration\n chown -v \"$user\" /Users/$user/Applications\n chown -R -v \"$user\" \"$APP_PATH\" >/dev/null 2>&1 || true\n echo \"Changed ownership of $APP_PATH and /Users/$user/Applications to $user\"\n\n if [[ -d /Users/$user/Desktop/JumpCloud\\ Password\\ Manager.app ]]; then\n # remove alias on desktop if exists\n rm -rf /Users/$user/Desktop/JumpCloud\\ Password\\ Manager.app\n fi\n\n err=$?\n if [ ${err} -ne 0 ]; then\n echo \"Could not copy $DMGAppPath Error: ${err}\"\n hdiutil detach $DMGMountPoint\n echo \"Used hdiutil to detach $DMGFile from $DMGMountPoint\"\n rm -r /tmp/$TempFolder\n echo \"Deleted /tmp/$TempFolder\"\n exit 1\n fi\n\n userInstall=true\n echo \"Copied $DMGAppPath to /Users/$user/Applications\"\n if [ \"$LaunchAfterInstall\" = true ]; then\n sudo -u \"$user\" open \"$APP_PATH\" >/dev/null 2>&1 || true\n fi\n # Create an alias on desktop\n ln -s \"$APP_PATH\" \"/Users/$user/Desktop/JumpCloud Password Manager.app\"\n fi\ndone\n\n\n# Check if password manager is installed in /Applications; remove if we installed in user directory\nif [ -d /Applications/JumpCloud\\ Password\\ Manager.app ] && [ $userInstall = true ]; then\n # remove if exists\n rm -rf /Applications/JumpCloud\\ Password\\ Manager.app\nfi\n\n# Unmount the DMG file\nhdiutil detach $DMGMountPoint\n\necho \"Used hdiutil to detach $DMGFile from $DMGMountPoint\"\n\nerr=$?\nif [ ${err} -ne 0 ]; then\n echo \"Could not detach DMG: $DMGMountPoint Error: ${err}\" >&2\n exit 1\nfi\n\n# Remove Temp Folder and download\nrm -r /tmp/$TempFolder\n\necho \"Deleted /tmp/$TempFolder\"\n\n\nexit", "link": "https://github.com/TheJumpCloud/support/blob/master/PowerShell/JumpCloud%20Commands%20Gallery/Mac%20Commands/Mac%20-%20Install%20JumpCloud%20Password%20Manager%20App.md", "description": "This command will download and install the JumpCloud Password Manager to the device if it isn't already installed. On slower networks, timeouts with exit code 127 can occur. Manually setting the default timeout limit to 600 seconds may be advisable." }, @@ -364,9 +364,9 @@ "description": "This command will download and install the Sentinel One Agent to the device if it isn't already installed.\n\nIn order to use this command:\n\n1. Download the Sentinel One Agent installer and host it at a URL that your devices can access.\n2. Edit the first two lines of the script to include your Customer ID (with checksum value) and the URL where you are hosting the installer.\n3. Extend the command timeout to a value that makes sense in your environment. The suggested command timeout for an environment with average network speeds on devices with average computing power is 10 minutes. Note that the command may timeout with a 124 error code in the command result window if not extended, but the script will continue to run." }, { - "name": "Windows - Install and Update JumpCloud Password Manager App | v2.0.0 JCCG", + "name": "Windows - Install and Update JumpCloud Password Manager App | v2.0.1 JCCG", "type": "windows", - "command": "# Set $LaunchPasswordManager to $false ON LINE 70 if you do not wish to launch the password manger after installation\n\n# Set $updateToLatest to $true if you want to update to the latest version of JumpCloud Password Manager\n# Set $updateToLatest to $false if you want to re-install the JumpCloud Password Manager no matter your current version.\n# ********** DISCLAIMER: Setting $updateToLatest to $false will NOT affect any user data **********\n$updateToLatest = $true\n# Get the current logged on User\n$loggedUser = Get-WmiObject -Class Win32_ComputerSystem | Select-Object -ExpandProperty UserName\n$loggedUser = $loggedUser -replace '.*\\\\'\n\n# Construct the Registry path using the user's SID\n$userSID = (New-Object System.Security.Principal.NTAccount($loggedUser)).Translate([System.Security.Principal.SecurityIdentifier]).Value\n$registryPath = \"HKLM:\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\ProfileList\\$userSID\"\n\n# Get the ProfileImagePath value from the Registry\n$loggedOnUserProfileImagePath = Get-ItemPropertyValue -Path $registryPath -Name 'ProfileImagePath'\nWrite-Output \"Logged On User Profile Path: $loggedOnUserProfileImagePath\"\n\n\n$appDataPath = \"$loggedOnUserProfileImagePath\\AppData\\Local\\jcpwm\"\n\n$installerURL = 'https://cdn.pwm.jumpcloud.com/DA/release/JumpCloud-Password-Manager-latest.exe'\n$yamlFileURL = 'https://cdn.pwm.jumpcloud.com/DA/release/latest.yml'\n\n# If user already has the app installed and admin wants to update to latest\nif ((Test-Path \"$appDataPath\") -and ($updateToLatest -eq $true)) {\n $folderPrefix = \"app-\"\n $versionFolders = Get-ChildItem -Path $appDataPath -Directory |\n Where-Object { $_.Name -like \"$($folderPrefix)*\" } |\n Sort-Object Name -Descending\n\n if ($versionFolders.Count -gt 0) {\n # Get the name of the top (latest) matching folder (app-x.x.x)\n $latestFolderName = $versionFolders[0].Name\n # Extract the version string by splitting the folder name\n # We split by the prefix and take the last part (which is the version)\n [System.Version]$currentInstalledAppVersion = ($latestFolderName.Split($folderPrefix, [System.StringSplitOptions]::RemoveEmptyEntries))[-1].Trim()\n } else {\n Write-Output \"App Folder is missing, revert to full download\"\n $updateToLatest = $false\n }\n}\n\nif (Test-Path \"$loggedOnUserProfileImagePath\\AppData\\Local\\Temp\" ) {\n\n $installerTempLocation = \"$loggedOnUserProfileImagePath\\AppData\\Local\\Temp\\JumpCloud-Password-Manager-latest.exe\"\n $yamlFileTempLocation = \"$loggedOnUserProfileImagePath\\AppData\\Local\\Temp\\jcpwm-latest.yml\"\n Write-Output \"Installer Location: $installerTempLocation\"\n}\nelse {\n Write-Output \"Unable to determine user profile folder\"\n Exit 1\n}\n\nif ($updateToLatest -eq $true) {\n # Remove existing YAML file to ensure fresh version check\n if (Test-Path -Path $yamlFileTempLocation) {\n Remove-Item -Path $yamlFileTempLocation -Force\n }\n\n if (-not(Test-Path -Path $yamlFileTempLocation -PathType Leaf)) {\n try {\n Write-Output 'Downloading Password Manager installer now.'\n try {\n Invoke-WebRequest -Uri $yamlFileURL -OutFile $yamlFileTempLocation\n } catch {\n Write-Error \"Unable to download Password Manager latest yml file to $yamlFileTempLocation.\"\n exit 1\n }\n Write-Output 'Finished downloading Password Manager installer.'\n } catch {\n throw $_.Exception.Message\n }\n }\n\n $versionLine = Get-Content -Path $yamlFileTempLocation | Select-String -Pattern 'version[^\\w:]*:\\s*(.*)$'\n Write-Output \"Checking for version in YAML file: $yamlFileTempLocation\"\n Write-Output \"Version Line: $versionLine\"\n if ($versionLine) {\n # Extract the version number from the matched line\n # The 'Groups[1]' captures the content after 'version: '\n [System.Version]$latestVersion = $versionLine.Matches[0].Groups[1].Value.Trim()\n Write-Output \"Latest version: $latestVersion\"\n # If the admin has previously installed the dogfood/beta version of the app for the users\n # it might be greater than the version found under the $installerURL.\n if ($currentInstalledAppVersion -ge $latestVersion) {\n Write-Output \"App is already up to date, exiting.\"\n Exit 0\n }\n } else {\n Write-Warning \"Could not find 'version' in the YAML file, falling back to full download.\"\n }\n}\n\nWrite-Output \"Ensuring a fresh installer download...\"\n# Remove existing installer file to ensure fresh download\nif (Test-Path -Path $installerTempLocation) {\n Remove-Item -Path $installerTempLocation -Force\n}\n\nWrite-Output 'Testing if Password Manager installer is downloaded'\n\nif (-not(Test-Path -Path $installerTempLocation -PathType Leaf)) {\n try {\n Write-Output 'Downloading Password Manager installer now.'\n try {\n Invoke-WebRequest -Uri $installerURL -OutFile $installerTempLocation\n } catch {\n Write-Error \"Unable to download Password Manager installer to $InstallerTempLocation.\"\n exit 1\n }\n Write-Output 'Finished downloading Password Manager installer.'\n } catch {\n throw $_.Exception.Message\n }\n}\n\nWrite-Output \"Checking if JumpCloud Password Manager is running.\"\n$process = Get-Process | Where-Object { $_.ProcessName -like \"*JumpCloud Password Manager*\" }\nif ($process) {\n Write-Output \"JumpCloud Password Manager is running. Terminating process before installation.\"\n Stop-Process -Name $process.ProcessName -Force\n # Clean the process\n Start-Sleep -Seconds 5\n}\n\nWrite-Output 'Installing Password Manager now, this may take a few minutes.'\n\n$Command = {\n # Get the current user's SID (Security Identifier)\n $loggedUser = Get-WmiObject -Class Win32_ComputerSystem | Select-Object -ExpandProperty UserName\n $loggedUser = $loggedUser -replace '.*\\\\'\n\n # Construct the Registry path using the user's SID\n $userSID = (New-Object System.Security.Principal.NTAccount($loggedUser)).Translate([System.Security.Principal.SecurityIdentifier]).Value\n $registryPath = \"HKLM:\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\ProfileList\\$userSID\"\n $loggedOnUserProfileImagePath = Get-ItemPropertyValue -Path $registryPath -Name 'ProfileImagePath'\n $LaunchPasswordManager = $true\n $installerTempLocation = \"$loggedOnUserProfileImagePath\\AppData\\Local\\Temp\\JumpCloud-Password-Manager-latest.exe\"\n if ($LaunchPasswordManager -eq $false) {\n $env:QUIT_PWM_AFTER_INITIAL_INSTALL=\"true\"\n }\n . $installerTempLocation\n\n if ($LaunchPasswordManager -eq $false) {\n # If the user does not want to launch the password manager after installation\n # no shortcut will be created so we'll have to do it manually.\n # Wait for the installer to finish before proceeding\n Write-Output \"Waiting for JumpCloud Password Manager installer to finish.\"\n Wait-Process -Name \"JumpCloud-Password-Manager-latest\"\n Write-Output \"Creating shortcut to JumpCloud Password Manager on the Desktop\"\n\n $appTargetPath = \"$appDataPath\\JumpCloud Password Manager.exe\"\n\n $shell = New-Object -comObject WScript.Shell\n $desktopShortcut = $shell.CreateShortcut(\"$loggedOnUserProfileImagePath\\Desktop\\JumpCloud Password Manager.lnk\")\n $desktopShortcut.TargetPath = \"$appTargetPath\"\n $desktopShortcut.WorkingDirectory = \"$appDataPath\"\n $desktopShortcut.Save()\n Write-Output \"Shortcut created on the Desktop.\"\n Write-Output \"Now Creating Start Menu Shortcut.\"\n $startMenuDirectory = \"$loggedOnUserProfileImagePath\\AppData\\Roaming\\Microsoft\\Windows\\Start Menu\\Programs\\JumpCloud Inc\"\n if (-not(Test-Path -Path $startMenuDirectory)) {\n New-Item -Path $startMenuDirectory -ItemType Directory\n }\n $startMenuShortcut = $shell.CreateShortcut(\"$startMenuDirectory\\JumpCloud Password Manager.lnk\")\n $startMenuShortcut.TargetPath = \"$appTargetPath\"\n $startMenuShortcut.WorkingDirectory = \"$appDataPath\"\n $startMenuShortcut.Save()\n Write-Output \"Start Menu Shortcut created.\"\n }\n}\n\n$Source = @'\nusing System;\nusing System.Runtime.InteropServices;\n\nnamespace murrayju.ProcessExtensions\n{\n public static class ProcessExtensions\n {\n #region Win32 Constants\n\n private const int CREATE_UNICODE_ENVIRONMENT = 0x00000400;\n private const int CREATE_NO_WINDOW = 0x08000000;\n\n private const int CREATE_NEW_CONSOLE = 0x00000010;\n\n private const uint INVALID_SESSION_ID = 0xFFFFFFFF;\n private static readonly IntPtr WTS_CURRENT_SERVER_HANDLE = IntPtr.Zero;\n\n #endregion\n\n #region DllImports\n\n [DllImport(\"advapi32.dll\", EntryPoint = \"CreateProcessAsUser\", SetLastError = true, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]\n private static extern bool CreateProcessAsUser(\n IntPtr hToken,\n String lpApplicationName,\n String lpCommandLine,\n IntPtr lpProcessAttributes,\n IntPtr lpThreadAttributes,\n bool bInheritHandle,\n uint dwCreationFlags,\n IntPtr lpEnvironment,\n String lpCurrentDirectory,\n ref STARTUPINFO lpStartupInfo,\n out PROCESS_INFORMATION lpProcessInformation);\n\n [DllImport(\"advapi32.dll\", EntryPoint = \"DuplicateTokenEx\")]\n private static extern bool DuplicateTokenEx(\n IntPtr ExistingTokenHandle,\n uint dwDesiredAccess,\n IntPtr lpThreadAttributes,\n int TokenType,\n int ImpersonationLevel,\n ref IntPtr DuplicateTokenHandle);\n\n [DllImport(\"userenv.dll\", SetLastError = true)]\n private static extern bool CreateEnvironmentBlock(ref IntPtr lpEnvironment, IntPtr hToken, bool bInherit);\n\n [DllImport(\"userenv.dll\", SetLastError = true)]\n [return: MarshalAs(UnmanagedType.Bool)]\n private static extern bool DestroyEnvironmentBlock(IntPtr lpEnvironment);\n\n [DllImport(\"kernel32.dll\", SetLastError = true)]\n private static extern bool CloseHandle(IntPtr hSnapshot);\n\n [DllImport(\"kernel32.dll\")]\n private static extern uint WTSGetActiveConsoleSessionId();\n\n [DllImport(\"Wtsapi32.dll\")]\n private static extern uint WTSQueryUserToken(uint SessionId, ref IntPtr phToken);\n\n [DllImport(\"wtsapi32.dll\", SetLastError = true)]\n private static extern int WTSEnumerateSessions(\n IntPtr hServer,\n int Reserved,\n int Version,\n ref IntPtr ppSessionInfo,\n ref int pCount);\n\n #endregion\n\n #region Win32 Structs\n\n private enum SW\n {\n SW_HIDE = 0,\n SW_SHOWNORMAL = 1,\n SW_NORMAL = 1,\n SW_SHOWMINIMIZED = 2,\n SW_SHOWMAXIMIZED = 3,\n SW_MAXIMIZE = 3,\n SW_SHOWNOACTIVATE = 4,\n SW_SHOW = 5,\n SW_MINIMIZE = 6,\n SW_SHOWMINNOACTIVE = 7,\n SW_SHOWNA = 8,\n SW_RESTORE = 9,\n SW_SHOWDEFAULT = 10,\n SW_MAX = 10\n }\n\n private enum WTS_CONNECTSTATE_CLASS\n {\n WTSActive,\n WTSConnected,\n WTSConnectQuery,\n WTSShadow,\n WTSDisconnected,\n WTSIdle,\n WTSListen,\n WTSReset,\n WTSDown,\n WTSInit\n }\n\n [StructLayout(LayoutKind.Sequential)]\n private struct PROCESS_INFORMATION\n {\n public IntPtr hProcess;\n public IntPtr hThread;\n public uint dwProcessId;\n public uint dwThreadId;\n }\n\n private enum SECURITY_IMPERSONATION_LEVEL\n {\n SecurityAnonymous = 0,\n SecurityIdentification = 1,\n SecurityImpersonation = 2,\n SecurityDelegation = 3,\n }\n\n [StructLayout(LayoutKind.Sequential)]\n private struct STARTUPINFO\n {\n public int cb;\n public String lpReserved;\n public String lpDesktop;\n public String lpTitle;\n public uint dwX;\n public uint dwY;\n public uint dwXSize;\n public uint dwYSize;\n public uint dwXCountChars;\n public uint dwYCountChars;\n public uint dwFillAttribute;\n public uint dwFlags;\n public short wShowWindow;\n public short cbReserved2;\n public IntPtr lpReserved2;\n public IntPtr hStdInput;\n public IntPtr hStdOutput;\n public IntPtr hStdError;\n }\n\n private enum TOKEN_TYPE\n {\n TokenPrimary = 1,\n TokenImpersonation = 2\n }\n\n [StructLayout(LayoutKind.Sequential)]\n private struct WTS_SESSION_INFO\n {\n public readonly UInt32 SessionID;\n\n [MarshalAs(UnmanagedType.LPStr)]\n public readonly String pWinStationName;\n\n public readonly WTS_CONNECTSTATE_CLASS State;\n }\n\n #endregion\n\n // Gets the user token from the currently active session\n private static bool GetSessionUserToken(ref IntPtr phUserToken)\n {\n var bResult = false;\n var hImpersonationToken = IntPtr.Zero;\n var activeSessionId = INVALID_SESSION_ID;\n var pSessionInfo = IntPtr.Zero;\n var sessionCount = 0;\n\n // Get a handle to the user access token for the current active session.\n if (WTSEnumerateSessions(WTS_CURRENT_SERVER_HANDLE, 0, 1, ref pSessionInfo, ref sessionCount) != 0)\n {\n var arrayElementSize = Marshal.SizeOf(typeof(WTS_SESSION_INFO));\n var current = pSessionInfo;\n\n for (var i = 0; i < sessionCount; i++)\n {\n var si = (WTS_SESSION_INFO)Marshal.PtrToStructure((IntPtr)current, typeof(WTS_SESSION_INFO));\n current += arrayElementSize;\n\n if (si.State == WTS_CONNECTSTATE_CLASS.WTSActive)\n {\n activeSessionId = si.SessionID;\n }\n }\n }\n\n // If enumerating did not work, fall back to the old method\n if (activeSessionId == INVALID_SESSION_ID)\n {\n activeSessionId = WTSGetActiveConsoleSessionId();\n }\n\n if (WTSQueryUserToken(activeSessionId, ref hImpersonationToken) != 0)\n {\n // Convert the impersonation token to a primary token\n bResult = DuplicateTokenEx(hImpersonationToken, 0, IntPtr.Zero,\n (int)SECURITY_IMPERSONATION_LEVEL.SecurityImpersonation, (int)TOKEN_TYPE.TokenPrimary,\n ref phUserToken);\n\n CloseHandle(hImpersonationToken);\n }\n\n return bResult;\n }\n\n public static bool StartProcessAsCurrentUser(string appPath, string cmdLine = null, string workDir = null, bool visible = true)\n {\n var hUserToken = IntPtr.Zero;\n var startInfo = new STARTUPINFO();\n var procInfo = new PROCESS_INFORMATION();\n var pEnv = IntPtr.Zero;\n int iResultOfCreateProcessAsUser;\n\n startInfo.cb = Marshal.SizeOf(typeof(STARTUPINFO));\n\n try\n {\n if (!GetSessionUserToken(ref hUserToken))\n {\n throw new Exception(\"StartProcessAsCurrentUser: GetSessionUserToken failed.\");\n }\n\n uint dwCreationFlags = CREATE_UNICODE_ENVIRONMENT | (uint)(visible ? CREATE_NEW_CONSOLE : CREATE_NO_WINDOW);\n startInfo.wShowWindow = (short)(visible ? SW.SW_SHOW : SW.SW_HIDE);\n startInfo.lpDesktop = \"winsta0\\\\default\";\n\n if (!CreateEnvironmentBlock(ref pEnv, hUserToken, false))\n {\n throw new Exception(\"StartProcessAsCurrentUser: CreateEnvironmentBlock failed.\");\n }\n\n if (!CreateProcessAsUser(hUserToken,\n appPath, // Application Name\n cmdLine, // Command Line\n IntPtr.Zero,\n IntPtr.Zero,\n false,\n dwCreationFlags,\n pEnv,\n workDir, // Working directory\n ref startInfo,\n out procInfo))\n {\n throw new Exception(\"StartProcessAsCurrentUser: CreateProcessAsUser failed.\\n\");\n }\n\n iResultOfCreateProcessAsUser = Marshal.GetLastWin32Error();\n }\n finally\n {\n CloseHandle(hUserToken);\n if (pEnv != IntPtr.Zero)\n {\n DestroyEnvironmentBlock(pEnv);\n }\n CloseHandle(procInfo.hThread);\n CloseHandle(procInfo.hProcess);\n }\n return true;\n }\n }\n}\n\n\n'@\n\nAdd-Type -ReferencedAssemblies 'System', 'System.Runtime.InteropServices' -TypeDefinition $Source -Language CSharp\n$ApplicationPath = 'C:\\Windows\\system32\\WindowsPowerShell\\v1.0\\powershell.exe'\n\n$bytes = [System.Text.Encoding]::Unicode.GetBytes($command)\n$encodedCommand = [Convert]::ToBase64String($bytes)\n$Arguments = '-NoLogo -NonInteractive -ExecutionPolicy ByPass -WindowStyle Hidden -encodedCommand ' + $encodedCommand\n[murrayju.ProcessExtensions.ProcessExtensions]::StartProcessAsCurrentUser($ApplicationPath, $Arguments)", + "command": "# Set $LaunchPasswordManager to $false ON LINE 70 if you do not wish to launch the password manger after installation\n\n# Set $updateToLatest to $true if you want to update to the latest version of JumpCloud Password Manager\n# Set $updateToLatest to $false if you want to re-install the JumpCloud Password Manager no matter your current version.\n# ********** DISCLAIMER: Setting $updateToLatest to $false will NOT affect any user data **********\n$updateToLatest = $true\n# Get the current logged on User\n$loggedUser = Get-WmiObject -Class Win32_ComputerSystem | Select-Object -ExpandProperty UserName\n$loggedUser = $loggedUser -replace '.*\\\\'\n\n# Get managed users list\n$managedUsers = Get-Content -Path \"$env:ProgramFiles\\JumpCloud\\Plugins\\Contrib\\managedUsers.json\" | ConvertFrom-Json\nif ($managedUsers.username -notcontains $loggedUser) {\n Write-Output \"User $loggedUser is not a managed user, exiting.\"\n exit 1\n}\n\n# Construct the Registry path using the user's SID\n$userSID = (New-Object System.Security.Principal.NTAccount($loggedUser)).Translate([System.Security.Principal.SecurityIdentifier]).Value\n$registryPath = \"HKLM:\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\ProfileList\\$userSID\"\n\n# Get the ProfileImagePath value from the Registry\n$loggedOnUserProfileImagePath = Get-ItemPropertyValue -Path $registryPath -Name 'ProfileImagePath'\nWrite-Output \"Logged On User Profile Path: $loggedOnUserProfileImagePath\"\n\n\n$appDataPath = \"$loggedOnUserProfileImagePath\\AppData\\Local\\jcpwm\"\n\n$installerURL = 'https://cdn.pwm.jumpcloud.com/DA/release/JumpCloud-Password-Manager-latest.exe'\n$yamlFileURL = 'https://cdn.pwm.jumpcloud.com/DA/release/latest.yml'\n\n# If user already has the app installed and admin wants to update to latest\nif ((Test-Path \"$appDataPath\") -and ($updateToLatest -eq $true)) {\n $folderPrefix = \"app-\"\n $versionFolders = Get-ChildItem -Path $appDataPath -Directory | Where-Object { $_.Name -like \"$($folderPrefix)*\" } | Sort-Object Name -Descending\n\n if ($versionFolders.Count -gt 0) {\n # Get the name of the top (latest) matching folder (app-x.x.x)\n $latestFolderName = $versionFolders[0].Name\n # Extract the version string by splitting the folder name\n # We split by the prefix and take the last part (which is the version)\n [System.Version]$currentInstalledAppVersion = ($latestFolderName.Split($folderPrefix, [System.StringSplitOptions]::RemoveEmptyEntries))[-1].Trim()\n } else {\n Write-Output \"App Folder is missing, revert to full download\"\n $updateToLatest = $false\n }\n}\n\nif (Test-Path \"$loggedOnUserProfileImagePath\\AppData\\Local\\Temp\" ) {\n\n $installerTempLocation = \"$loggedOnUserProfileImagePath\\AppData\\Local\\Temp\\JumpCloud-Password-Manager-latest.exe\"\n $yamlFileTempLocation = \"$loggedOnUserProfileImagePath\\AppData\\Local\\Temp\\jcpwm-latest.yml\"\n Write-Output \"Installer Location: $installerTempLocation\"\n} else {\n Write-Output \"Unable to determine user profile folder\"\n exit 1\n}\n\nif ($updateToLatest -eq $true) {\n # Remove existing YAML file to ensure fresh version check\n if (Test-Path -Path $yamlFileTempLocation) {\n Remove-Item -Path $yamlFileTempLocation -Force\n }\n\n if (-not(Test-Path -Path $yamlFileTempLocation -PathType Leaf)) {\n try {\n Write-Output 'Downloading Password Manager installer now.'\n try {\n Invoke-WebRequest -Uri $yamlFileURL -OutFile $yamlFileTempLocation\n } catch {\n Write-Output \"Unable to download Password Manager latest yml file to $yamlFileTempLocation.\"\n exit 1\n }\n Write-Output 'Finished downloading Password Manager installer.'\n } catch {\n throw $_.Exception.Message\n }\n }\n\n $versionLine = Get-Content -Path $yamlFileTempLocation | Select-String -Pattern 'version[^\\w:]*:\\s*(.*)$'\n Write-Output \"Checking for version in YAML file: $yamlFileTempLocation\"\n Write-Output \"Version Line: $versionLine\"\n if ($versionLine) {\n # Extract the version number from the matched line\n # The 'Groups[1]' captures the content after 'version: '\n [System.Version]$latestVersion = $versionLine.Matches[0].Groups[1].Value.Trim()\n Write-Output \"Latest version: $latestVersion\"\n # If the admin has previously installed the dogfood/beta version of the app for the users\n # it might be greater than the version found under the $installerURL.\n if ($currentInstalledAppVersion -ge $latestVersion) {\n Write-Output \"App is already up to date, exiting.\"\n exit 0\n }\n } else {\n Write-Warning \"Could not find 'version' in the YAML file, falling back to full download.\"\n }\n}\n\nWrite-Output \"Ensuring a fresh installer download...\"\n# Remove existing installer file to ensure fresh download\nif (Test-Path -Path $installerTempLocation) {\n Remove-Item -Path $installerTempLocation -Force\n}\n\nWrite-Output 'Testing if Password Manager installer is downloaded'\n\nif (-not(Test-Path -Path $installerTempLocation -PathType Leaf)) {\n try {\n Write-Output 'Downloading Password Manager installer now.'\n try {\n Invoke-WebRequest -Uri $installerURL -OutFile $installerTempLocation\n } catch {\n Write-Output \"Unable to download Password Manager installer to $installerTempLocation.\"\n exit 1\n }\n Write-Output 'Finished downloading Password Manager installer.'\n } catch {\n throw $_.Exception.Message\n }\n}\n\nWrite-Output \"Checking if JumpCloud Password Manager is running.\"\n$process = Get-Process | Where-Object { $_.ProcessName -like \"*JumpCloud Password Manager*\" }\nif ($process) {\n Write-Output \"JumpCloud Password Manager is running. Terminating process before installation.\"\n Stop-Process -Name $process.ProcessName -Force\n # Clean the process\n Start-Sleep -Seconds 5\n}\n\nWrite-Output 'Installing Password Manager now, this may take a few minutes.'\n\n$Command = {\n # Get the current user's SID (Security Identifier)\n $loggedUser = Get-WmiObject -Class Win32_ComputerSystem | Select-Object -ExpandProperty UserName\n $loggedUser = $loggedUser -replace '.*\\\\'\n\n # Construct the Registry path using the user's SID\n $userSID = (New-Object System.Security.Principal.NTAccount($loggedUser)).Translate([System.Security.Principal.SecurityIdentifier]).Value\n $registryPath = \"HKLM:\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\ProfileList\\$userSID\"\n $loggedOnUserProfileImagePath = Get-ItemPropertyValue -Path $registryPath -Name 'ProfileImagePath'\n $LaunchPasswordManager = $true\n $installerTempLocation = \"$loggedOnUserProfileImagePath\\AppData\\Local\\Temp\\JumpCloud-Password-Manager-latest.exe\"\n if ($LaunchPasswordManager -eq $false) {\n $env:QUIT_PWM_AFTER_INITIAL_INSTALL = \"true\"\n }\n . $installerTempLocation\n\n if ($LaunchPasswordManager -eq $false) {\n # If the user does not want to launch the password manager after installation\n # no shortcut will be created so we'll have to do it manually.\n # Wait for the installer to finish before proceeding\n Write-Output \"Waiting for JumpCloud Password Manager installer to finish.\"\n Wait-Process -Name \"JumpCloud-Password-Manager-latest\"\n Write-Output \"Creating shortcut to JumpCloud Password Manager on the Desktop\"\n\n $appTargetPath = \"$appDataPath\\JumpCloud Password Manager.exe\"\n\n $shell = New-Object -comObject WScript.Shell\n $desktopShortcut = $shell.CreateShortcut(\"$loggedOnUserProfileImagePath\\Desktop\\JumpCloud Password Manager.lnk\")\n $desktopShortcut.TargetPath = \"$appTargetPath\"\n $desktopShortcut.WorkingDirectory = \"$appDataPath\"\n $desktopShortcut.Save()\n Write-Output \"Shortcut created on the Desktop.\"\n Write-Output \"Now Creating Start Menu Shortcut.\"\n $startMenuDirectory = \"$loggedOnUserProfileImagePath\\AppData\\Roaming\\Microsoft\\Windows\\Start Menu\\Programs\\JumpCloud Inc\"\n if (-not(Test-Path -Path $startMenuDirectory)) {\n New-Item -Path $startMenuDirectory -ItemType Directory\n }\n $startMenuShortcut = $shell.CreateShortcut(\"$startMenuDirectory\\JumpCloud Password Manager.lnk\")\n $startMenuShortcut.TargetPath = \"$appTargetPath\"\n $startMenuShortcut.WorkingDirectory = \"$appDataPath\"\n $startMenuShortcut.Save()\n Write-Output \"Start Menu Shortcut created.\"\n }\n}\n\n$Source = @'\nusing System;\nusing System.Runtime.InteropServices;\n\nnamespace murrayju.ProcessExtensions\n{\n public static class ProcessExtensions\n {\n #region Win32 Constants\n\n private const int CREATE_UNICODE_ENVIRONMENT = 0x00000400;\n private const int CREATE_NO_WINDOW = 0x08000000;\n\n private const int CREATE_NEW_CONSOLE = 0x00000010;\n\n private const uint INVALID_SESSION_ID = 0xFFFFFFFF;\n private static readonly IntPtr WTS_CURRENT_SERVER_HANDLE = IntPtr.Zero;\n\n #endregion\n\n #region DllImports\n\n [DllImport(\"advapi32.dll\", EntryPoint = \"CreateProcessAsUser\", SetLastError = true, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]\n private static extern bool CreateProcessAsUser(\n IntPtr hToken,\n String lpApplicationName,\n String lpCommandLine,\n IntPtr lpProcessAttributes,\n IntPtr lpThreadAttributes,\n bool bInheritHandle,\n uint dwCreationFlags,\n IntPtr lpEnvironment,\n String lpCurrentDirectory,\n ref STARTUPINFO lpStartupInfo,\n out PROCESS_INFORMATION lpProcessInformation);\n\n [DllImport(\"advapi32.dll\", EntryPoint = \"DuplicateTokenEx\")]\n private static extern bool DuplicateTokenEx(\n IntPtr ExistingTokenHandle,\n uint dwDesiredAccess,\n IntPtr lpThreadAttributes,\n int TokenType,\n int ImpersonationLevel,\n ref IntPtr DuplicateTokenHandle);\n\n [DllImport(\"userenv.dll\", SetLastError = true)]\n private static extern bool CreateEnvironmentBlock(ref IntPtr lpEnvironment, IntPtr hToken, bool bInherit);\n\n [DllImport(\"userenv.dll\", SetLastError = true)]\n [return: MarshalAs(UnmanagedType.Bool)]\n private static extern bool DestroyEnvironmentBlock(IntPtr lpEnvironment);\n\n [DllImport(\"kernel32.dll\", SetLastError = true)]\n private static extern bool CloseHandle(IntPtr hSnapshot);\n\n [DllImport(\"kernel32.dll\")]\n private static extern uint WTSGetActiveConsoleSessionId();\n\n [DllImport(\"Wtsapi32.dll\")]\n private static extern uint WTSQueryUserToken(uint SessionId, ref IntPtr phToken);\n\n [DllImport(\"wtsapi32.dll\", SetLastError = true)]\n private static extern int WTSEnumerateSessions(\n IntPtr hServer,\n int Reserved,\n int Version,\n ref IntPtr ppSessionInfo,\n ref int pCount);\n\n #endregion\n\n #region Win32 Structs\n\n private enum SW\n {\n SW_HIDE = 0,\n SW_SHOWNORMAL = 1,\n SW_NORMAL = 1,\n SW_SHOWMINIMIZED = 2,\n SW_SHOWMAXIMIZED = 3,\n SW_MAXIMIZE = 3,\n SW_SHOWNOACTIVATE = 4,\n SW_SHOW = 5,\n SW_MINIMIZE = 6,\n SW_SHOWMINNOACTIVE = 7,\n SW_SHOWNA = 8,\n SW_RESTORE = 9,\n SW_SHOWDEFAULT = 10,\n SW_MAX = 10\n }\n\n private enum WTS_CONNECTSTATE_CLASS\n {\n WTSActive,\n WTSConnected,\n WTSConnectQuery,\n WTSShadow,\n WTSDisconnected,\n WTSIdle,\n WTSListen,\n WTSReset,\n WTSDown,\n WTSInit\n }\n\n [StructLayout(LayoutKind.Sequential)]\n private struct PROCESS_INFORMATION\n {\n public IntPtr hProcess;\n public IntPtr hThread;\n public uint dwProcessId;\n public uint dwThreadId;\n }\n\n private enum SECURITY_IMPERSONATION_LEVEL\n {\n SecurityAnonymous = 0,\n SecurityIdentification = 1,\n SecurityImpersonation = 2,\n SecurityDelegation = 3,\n }\n\n [StructLayout(LayoutKind.Sequential)]\n private struct STARTUPINFO\n {\n public int cb;\n public String lpReserved;\n public String lpDesktop;\n public String lpTitle;\n public uint dwX;\n public uint dwY;\n public uint dwXSize;\n public uint dwYSize;\n public uint dwXCountChars;\n public uint dwYCountChars;\n public uint dwFillAttribute;\n public uint dwFlags;\n public short wShowWindow;\n public short cbReserved2;\n public IntPtr lpReserved2;\n public IntPtr hStdInput;\n public IntPtr hStdOutput;\n public IntPtr hStdError;\n }\n\n private enum TOKEN_TYPE\n {\n TokenPrimary = 1,\n TokenImpersonation = 2\n }\n\n [StructLayout(LayoutKind.Sequential)]\n private struct WTS_SESSION_INFO\n {\n public readonly UInt32 SessionID;\n\n [MarshalAs(UnmanagedType.LPStr)]\n public readonly String pWinStationName;\n\n public readonly WTS_CONNECTSTATE_CLASS State;\n }\n\n #endregion\n\n // Gets the user token from the currently active session\n private static bool GetSessionUserToken(ref IntPtr phUserToken)\n {\n var bResult = false;\n var hImpersonationToken = IntPtr.Zero;\n var activeSessionId = INVALID_SESSION_ID;\n var pSessionInfo = IntPtr.Zero;\n var sessionCount = 0;\n\n // Get a handle to the user access token for the current active session.\n if (WTSEnumerateSessions(WTS_CURRENT_SERVER_HANDLE, 0, 1, ref pSessionInfo, ref sessionCount) != 0)\n {\n var arrayElementSize = Marshal.SizeOf(typeof(WTS_SESSION_INFO));\n var current = pSessionInfo;\n\n for (var i = 0; i < sessionCount; i++)\n {\n var si = (WTS_SESSION_INFO)Marshal.PtrToStructure((IntPtr)current, typeof(WTS_SESSION_INFO));\n current += arrayElementSize;\n\n if (si.State == WTS_CONNECTSTATE_CLASS.WTSActive)\n {\n activeSessionId = si.SessionID;\n }\n }\n }\n\n // If enumerating did not work, fall back to the old method\n if (activeSessionId == INVALID_SESSION_ID)\n {\n activeSessionId = WTSGetActiveConsoleSessionId();\n }\n\n if (WTSQueryUserToken(activeSessionId, ref hImpersonationToken) != 0)\n {\n // Convert the impersonation token to a primary token\n bResult = DuplicateTokenEx(hImpersonationToken, 0, IntPtr.Zero,\n (int)SECURITY_IMPERSONATION_LEVEL.SecurityImpersonation, (int)TOKEN_TYPE.TokenPrimary,\n ref phUserToken);\n\n CloseHandle(hImpersonationToken);\n }\n\n return bResult;\n }\n\n public static bool StartProcessAsCurrentUser(string appPath, string cmdLine = null, string workDir = null, bool visible = true)\n {\n var hUserToken = IntPtr.Zero;\n var startInfo = new STARTUPINFO();\n var procInfo = new PROCESS_INFORMATION();\n var pEnv = IntPtr.Zero;\n int iResultOfCreateProcessAsUser;\n\n startInfo.cb = Marshal.SizeOf(typeof(STARTUPINFO));\n\n try\n {\n if (!GetSessionUserToken(ref hUserToken))\n {\n throw new Exception(\"StartProcessAsCurrentUser: GetSessionUserToken failed.\");\n }\n\n uint dwCreationFlags = CREATE_UNICODE_ENVIRONMENT | (uint)(visible ? CREATE_NEW_CONSOLE : CREATE_NO_WINDOW);\n startInfo.wShowWindow = (short)(visible ? SW.SW_SHOW : SW.SW_HIDE);\n startInfo.lpDesktop = \"winsta0\\\\default\";\n\n if (!CreateEnvironmentBlock(ref pEnv, hUserToken, false))\n {\n throw new Exception(\"StartProcessAsCurrentUser: CreateEnvironmentBlock failed.\");\n }\n\n if (!CreateProcessAsUser(hUserToken,\n appPath, // Application Name\n cmdLine, // Command Line\n IntPtr.Zero,\n IntPtr.Zero,\n false,\n dwCreationFlags,\n pEnv,\n workDir, // Working directory\n ref startInfo,\n out procInfo))\n {\n throw new Exception(\"StartProcessAsCurrentUser: CreateProcessAsUser failed.\\n\");\n }\n\n iResultOfCreateProcessAsUser = Marshal.GetLastWin32Error();\n }\n finally\n {\n CloseHandle(hUserToken);\n if (pEnv != IntPtr.Zero)\n {\n DestroyEnvironmentBlock(pEnv);\n }\n CloseHandle(procInfo.hThread);\n CloseHandle(procInfo.hProcess);\n }\n return true;\n }\n }\n}\n\n\n'@\n\nAdd-Type -ReferencedAssemblies 'System', 'System.Runtime.InteropServices' -TypeDefinition $Source -Language CSharp\n$ApplicationPath = 'C:\\Windows\\system32\\WindowsPowerShell\\v1.0\\powershell.exe'\n\n$bytes = [System.Text.Encoding]::Unicode.GetBytes($command)\n$encodedCommand = [Convert]::ToBase64String($bytes)\n$Arguments = '-NoLogo -NonInteractive -ExecutionPolicy ByPass -WindowStyle Hidden -encodedCommand ' + $encodedCommand\n[murrayju.ProcessExtensions.ProcessExtensions]::StartProcessAsCurrentUser($ApplicationPath, $Arguments)", "link": "https://github.com/TheJumpCloud/support/blob/master/PowerShell/JumpCloud%20Commands%20Gallery/Windows%20Commands/Windows%20-%20Install%20JumpCloud%20Password%20Manager%20App.md", "description": "This command will download and install the JumpCloud Password Manager app to the device if it isn't already installed. On slower networks, timeouts with exit code 127 can occur. Manually setting the default timeout limit to 600 seconds may be advisable." },