Mobile Jon’s Final Act in VMware EUC: Android Secure Launcher Password Rotation

goodbye

Hello all. Over the last 7-8 years I have written 220+ VMware EUC articles amassing something like 300k+ views. Today, this all comes to an end. As I wrote recently on LinkedIN, VMware decided that I was not good enough to be part of their new VMware EUC Experts program (god that name is…) despite being widely known as one of the top evangelists for EUC and Workspace ONE. I won’t go into the reasons behind everything that has transpired between me and VMware since August 2023, but it’s not been great. You can reach out to me at [email protected] if you want to discuss it, but I am moving on and refocusing as a Microsoft MVP and my relationships with other vendors who value my contributions. Yes, to be clear I have decided to no longer write or evangelize VMware products as a whole (especially Workspace ONE).

Today, we’re here to discuss a script I recently wrote that will help address the challenges around password rotation on Android Launcher profiles in Workspace ONE. Let’s get into the code and break it down piece by piece.

Authenticating with the Workspace ONE API

The code below, you may know from other scripts on my Github. Basically, we do the following:

  • Declare TLS 1.2
  • set the WS1 environment
  • Prompt for username, password, and API key
  • Convert the password into a secure string that can be used
  • Encode the username and password into a cred variable
  • Set the script headers
#----------------------------------------------------------[Declarations]----------------------------------------------------------
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
#-----------------------------------------------------------[Functions]------------------------------------------------------------
if ([string]::IsNullOrEmpty($wsoserver))
  {
    $script:WSOServer = "asXXX.awmdm.com/API"
    
  }
 if ([string]::IsNullOrEmpty($header))
  {
   $Username = Read-Host -Prompt 'Enter the Username'
    $Password = Read-Host -Prompt 'Enter the Password' -AsSecureString
    $apikey = Read-Host -Prompt 'Enter the API Key' -AsSecureString


    #Convert the Password
    $BSTR = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($Password)
    $UnsecurePassword = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto($BSTR)


    #Base64 Encode AW Username and Password
    $combined = $Username + ":" + $UnsecurePassword
    $encoding = [System.Text.Encoding]::ASCII.GetBytes($combined)
    $cred = [Convert]::ToBase64String($encoding)


    $script:header = @{
    "Authorization"  = "Basic $cred";
    "aw-tenant-code" = $apikey;
    "Accept"         = "application/json;version=2";
    "Content-Type"   = "application/json";}
  }

The Workspace ONE Secure Launcher Profile Selection Utility

This code will prompt the user to enter a search term to find the launcher profile. You will notice that we scope it to the Android platform and use the searchtext parameter to return the results we’re looking for.

The user will then select the profile they want, by pressing a number. Once done, it cleans up the formatting and gets the JSON results of the profile. We’ll be using this in a few minutes:

##Find and Select the Launcher Profile##

$profile = Read-Host -Prompt 'Enter the Launcher Profile you want to rotate the password for'

try {

    
  $sresult = Invoke-RestMethod -Method Get -Uri "https://asXXX.awmdm.com/api/mdm/profiles/search?platform=android&searchtext=$profile" -ContentType "application/json" -Header $header

}

catch {
  Write-Host "An error occurred when logging on $_"
  break
}
$menu = @{}
for ($i=1;$i -le $sresult.ProfileList.count; $i++) 
{ Write-Host "$i. Profiles: $($sresult.ProfileList[$i-1].ProfileName), id: $($sresult.ProfileList[$i-1].ProfileId)"
$menu.Add($i,($sresult.ProfileList[$i-1].ProfileId))}
[int]$id = Read-Host 'Enter selection'
$ProfileId = $menu.Item($id)
##Get the Details of the Existing Profile##
$body = Invoke-RestMethod -Method Get -Uri "https://asXXX.awmdm.com/api/mdm/profiles/$ProfileId" -ContentType "application/json" -Header $header | ConvertTo-Json
$fixBody = $body -replace "@", ""
# Convert JSON string to PowerShell object
$newBody = $fixBody | ConvertFrom-Json

Formatting the Body and Rotating the Workspace ONE Android Secure Launcher Password

This final code will use code to format aspects of the JSON body to ensure it matches the requirements for the body to update the code. You will see how it creates a random 16 digit password, writes it to the body, and performs the POST command to update the profile.

# Check if General is an array and has at least one element
if ($newBody.General -is [System.Array] -and $newBody.General.Count -gt 0) {
    # Reconstruct the first object in the General array and add the new property
    $firstItem = $newBody.General[0] | Select-Object *,@{Name='CreateNewVersion';Expression={'True'}}
    
    # Replace the original first element with the modified one
    $newBody.General[0] = $firstItem
} elseif ($newBody.General -ne $null) {
    # If General is not an array but a single object
    # Reconstruct the General object and add the new property
    $newBody.General = $newBody.General | Select-Object *,@{Name='CreateNewVersion';Expression={'True'}}
} else {
    # If General is null or doesn't exist, initialize it as an array with the new object
    $newBody.General = @(@{CreateNewVersion = "True"})
}

# Convert back to JSON string

# Processing each AllowedApplication entry
$newBody.AndroidForWorkKiosk.AllowedApplications = $newBody.AndroidForWorkKiosk.AllowedApplications | ForEach-Object {
    $entry = $_.Trim("{} ")
    $pairs = $entry -split ';'
    $appObj = @{}
    foreach ($pair in $pairs) {
        $parts = $pair.Split("=", 2)
        if ($parts.Count -eq 2) {
            $key = $parts[0].Trim()
            $value = $parts[1].Trim()
            $appObj[$key] = $value
        }
    }
    return $appObj
}

$newBody.AndroidForWorkKiosk.GroupId = "{" + $newBody.AndroidForWorkKiosk.GroupId.Trim("{}") + "}"
$newBody.AndroidForWorkKiosk.UserName = "{" + $newBody.AndroidForWorkKiosk.UserName.Trim("{}") + "}"


# Convert the entire object back to a JSON string

$newVersionBody = $newBody | ConvertTo-Json -Depth 10



function Generate-RandomPassword {
    param (
        [int]$length = 16
    )

    $characters = 'abcdefghiklmnoprstuvwxyzABCDEFGHJKLMNOPQRSTUVWXYZ1234567890!@$?_-'
    $bytes = new-object "System.Byte[]" $length
    $rng = [System.Security.Cryptography.RNGCryptoServiceProvider]::Create()
    $rng.GetBytes($bytes)
    $password = ($bytes | ForEach-Object { $characters[$_ % $characters.Length] }) -join ''
    return $password
}

$newPassword = Generate-RandomPassword -length 16
$updatedbody = $newVersionBody -replace "\*\*\*\*\*", $NewPassword
$newPasswordBody = $updatedbody
# Define a regex pattern to match the specific structure
# This pattern looks for the "{SmartGroupId=number; Name=text}" structure
$pattern = '"\{SmartGroupId=(\d+);\s*Name=([^}]+)\}"'

# Replacement string reformats the matched strings into a valid JSON object
$replacement = '{"SmartGroupId": "$1", "Name": "$2"}'
$finalBody = $newPasswordBody -replace $pattern, $replacement


Invoke-RestMethod -Method Post -Uri "https://asXXX.awmdm.com/api/mdm/profiles/platforms/android/update" -ContentType "application/json" -body $finalBody -Header $header

In addition, you could do something like this to email the new password to your team:

##$mailParams = @{
    ##SmtpServer               = 'smtp.office365.com'
    ##Port                     = '587' 
    ##UseSSL                   = $true
    ##Credential               = New-Object System.Management.Automation.PSCredential ("", "")
    ##From                     = ''
    ##To                       = ''
    ##Subject                  = "Android Launcher Admin Passcode Updated"
    ##Body                     = "The Passcode has been updated. The new Passcode is $NewPassword"
    ##BodyAsHtml               = $true
  ##  Priority                 = 'High'
##    DeliveryNotificationOption = 'OnFailure', 'OnSuccess'
##}

##Send-MailMessage @mailParams

You could even use this old code of mine, to write the password to CyberArk. This could be written in a few different ways, but this code (which I wrote for Microsoft Teams Room rotation, should work fairly similarly).

##Get Username##
$username = Get-Content C:\temp\username.txt
$CyberArkUsername = “CyberArkMTRPolicyName” + $Username
##Retrieve Local Account Password##
$localpasswordretrieval = Invoke-RestMethod -Method Get -Uri https://cyberarkURL/AIMWebService/Api/Accounts?AppID=VAULTNAME&”Object=“PolicyName-ServiceAccountName”
$localpassword = $localpasswordretrieval.content
$EscapedPassword = [System.Security.SecurityElement]::Escape($localpassword)

##Authenticate##
$AuthBody = @{username=‘SERVICEACCOUNTNAME’;password=$EscapedPassword} | ConvertTo-Json
$CyberArkAuthToken = Invoke-RestMethod -Method Post -Uri https://cyberarkURL/api/auth/Ldap/logon -Body $AuthBody -ContentType "application/json"

##Get Account ID##
$header = @{"Authorization" = $CyberArkAuthToken}
$body = @{"NewCredentials" = $NewPassword}
$AccountID = Invoke-RestMethod -Uri https://cyberarkURL/passwordvault/api/Accounts?$CyberArkUsername -Headers $header
$UserID = $AccountID.value.id
##Change Password##
Invoke-RestMethod -Method Post -Uri https://cyberarkURL/passwordvault/api/Accounts/$UserID/Password/Update -Headers $header -Body $body

##Delete Password File##
##Remove-Item -Path C:\temp\newpassword.txt -Force
##Restart-Computer

Video Demo

Final Words from Mobile Jon

The script is the script, so I don’t need to dig too deep on that. The demo above shows you what it looks like and it does a nice job. It’s been a pleasure helping and working with so many diverse people since 2017. I’ve always said that I’m not from everyone, I’m an acquired taste, and you either love me or hate me. All of that is fine, because I know exactly who I am. I’ve never changed and I’ve always been consistent. Others have changed and that’s okay.

It has meant the world to me to help so many people over the last 7 years. Over those 7 years, I had many people come up to me and tell me how my blog has helped them. That has meant a lot to me. Let’s finish things up with a short goodbye.

Facebook
Twitter
LinkedIn
We can automate the rotation of the Workspace ONE Android Secure Launcher password via API with this tool that anyone can use.

Let me know what you think

Scroll to Top

Discover more from Mobile Jon's Blog

Subscribe now to keep reading and get access to the full archive.

Continue reading