Use BeyondTrust PRA for scripted windows tasks

BeyondTrust Privileged Remote Access controls, manages, and audits privileged accounts and credentials. This enables just-in-time, zero trust access to on-premises and cloud resources by internal, external, and third-party users. I leverage underlying jumpoint technology to remote into systems hosted in Azure.

Typically, PRA is used to elevate a task that requires some form of privileged access. In a traditional sense, this may be an engineer connecting to a remote system in a privileged manner. However, in this blog post, we’ll cover how to authenticate to the API, leverage it to retrieve a credential we require from the Vault, and use that account to effectively perform tasks on remote systems.

We will leverage the following products to automate end-end automation on a destination system:

  • PowerShell
  • BeyondTrust PRA API
  • BeyondTrust PRA Vault
  • <Whatever you use to run the script(s)>

This blog post assumes you have a working PRA environment, with a (windows) vault account imported from a windows domain. Same principles apply for other accounts though.

1. BeyondTrust API key

Log into /login (admin console), go to Management – API Configuration

Add an API account that has sufficient privileges (Command API: Full Access, Manage Vault Accounts, …), you can tweak privileges to your liking of course.

Copy OAuth Client Secret Value, and store it securely.

Yes, I went a bit overkill on Permissions 🙂

2. Authentication using PowerShell

I’ve written the following code snippet to make sure we can authenticate to BT API using the previously created OAuth credentials. You need to edit $PRAHostName, $ClientID, and $ClientSecret. You can always report out on $authSuccess = $false for troubleshooting purposes 🙂

### AUTHENTICATION

## VARIABLES
# EDIT THE FOLLOWING:
$PRAHostName = "https://beyondtrust.midp.cloud"
$ClientID = "<oauthclientid>"
$ClientSecret= "<oauthsecret>"

# LEAVE AS IS:
$AuthURL = $PRAHostName + "/oauth2/token"

## GET TOKEN
$authSuccess = $false
$creds = @{
    client_id = $clientid
    client_secret = $clientsecret
    grant_type = "client_credentials"    
};

$header_token = @{"Content-Type" = "application/x-www-form-urlencoded"}

try {
    $response = Invoke-RestMethod "$authurl" -Method Post -Body $creds -Headers $header_token
    $token = $response.access_token;
    $authSuccess = $true

    "  Authorized. Token is: $token"
}
catch {
    
    $authSuccess = $false
    "  ERROR - Unable to get an access token."
}

Congratulations! We now have a bearer $token that is valid for 3600s (or one hour).

3. BeyondTrust Vault credentials

Next up, retrieving the vault credentials we require and successfully checking them out. Let’s expand on our previous script. Simply add this section below the previous code snippet and make sure you properly edit $AccountName to reflect the vault account credential you want to use:

### VAULT CREDENTIALS
## VARIABLES
# EDIT THE FOLLOWING:
$AccountName = "[email protected]"

# LEAVE AS IS:
$ApiURL = $PRAHostName + "/api/config/v1"
$VaultURL = $ApiURL + "/vault/account?name=" + $AccountName
$headers = @{
  "Authorization" = "Bearer" + "$token"
  "Content-Type" = "application/json"
}

As you can see, we enter an account name that is present in our vault, and define some variables we’ll need to use to look up the account first. Next, let’s get the ID of the vault account:

### GET ID
$VaultAccountID = (Invoke-RestMethod $vaulturl -method 'GET' -headers $headers).id 

That was easy! We need this vault ID since the API for rotating, checking out or checking in credentials needs this as an identifier. Let’s go ahead and start by force checking in the account and rotating the credentials just to be sure it’s not in use anywhere.

### FORCE CHECK IN & Rotate
$VaultCheckInURL = $ApiURL + "/vault/account/" + $VaultAccountID + "/force-check-in"
$VaultRotateURL + $ApiURL + "/vault/account/" + $VaultAccountID + "/rotate"
Invoke-RestMethod $VaultCheckInURL -method 'POST' -headers $headers
Start-Sleep -Seconds 5
Invoke-RestMethod $VaultRotateURL -method 'POST' -headers $headers

The API returns 200. Life is good. Let’s check out the vault account, and retrieve its password.

### CHECK OUT
$VaultCheckOutURL = $ApiURL + "/vault/account/" + $VaultAccountID + "/check-out"
$AccountPassword = (Invoke-RestMethod $$VaultCheckoutURL -method 'POST' -headers $headers).password

$AccountPassword is returned. Great to see the complexity our beloved BeyondTrust PRA applies to vault account passwords :-). Once you’re done automating, don’t forget to check the account in again:

### CHECK IN
$VaultCheckInAgainURL = $ApiURL + "/vault/account/" + $VaultAccountID + "/check-in"
Invoke-RestMethod $VaultCheckInAgainURL -method 'POST' -headers $headers

Tip: if you actually set up rotation after check in in PRA on your account policy setting, you don’t have to even rotate. PRA does it for you after check in!

Full script below:

### AUTHENTICATION

## VARIABLES
# EDIT THE FOLLOWING:
$PRAHostName = "https://beyondtrust.midp.cloud"
$ClientID = "<oauthclientid>"
$ClientSecret= "<oauthsecret>"

# LEAVE AS IS:
$AuthURL = $PRAHostName + "/oauth2/token"

## GET TOKEN
$authSuccess = $false
$creds = @{
    client_id = $clientid
    client_secret = $clientsecret
    grant_type = "client_credentials"    
};

$header_token = @{"Content-Type" = "application/x-www-form-urlencoded"}

try {
    $response = Invoke-RestMethod "$authurl" -Method Post -Body $creds -Headers $header_token
    $token = $response.access_token;
    $authSuccess = $true

    "  Authorized. Token is: $token"
}
catch {
    
    $authSuccess = $false
    "  ERROR - Unable to get an access token."
}

### VAULT CREDENTIALS
## VARIABLES
# EDIT THE FOLLOWING:
$AccountName = "[email protected]"

# LEAVE AS IS:
$ApiURL = $PRAHostName + "/api/config/v1"
$VaultURL = $ApiURL + "/vault/account?name=" + $AccountName
$headers = @{
  "Authorization" = "Bearer" + "$token"
  "Content-Type" = "application/json"
}

### GET ID
$VaultAccountID = (Invoke-RestMethod $vaulturl -method 'GET' -headers $headers).id 

### FORCE CHECK IN & Rotate
$VaultCheckInURL = $ApiURL + "/vault/account/" + $VaultAccountID + "/force-check-in"
$VaultRotateURL + $ApiURL + "/vault/account/" + $VaultAccountID + "/rotate"
Invoke-RestMethod $VaultCheckInURL -method 'POST' -headers $headers
Start-Sleep -Seconds 5
Invoke-RestMethod $VaultRotateURL -method 'POST' -headers $headers

### CHECK OUT
$VaultCheckOutURL = $ApiURL + "/vault/account/" + $VaultAccountID + "/check-out"
$AccountPassword = (Invoke-RestMethod $$VaultCheckoutURL -method 'POST' -headers $headers).password

### CHECK IN
$VaultCheckInAgainURL = $ApiURL + "/vault/account/" + $VaultAccountID + "/check-in"
Invoke-RestMethod $VaultCheckInAgainURL -method 'POST' -headers $headers

4. What’s Next?

So, what’s next? Well, we’ve successfully authenticated to the API and retrieved a $token. We’ve used the API to retrieve the $AccountPassword for $AccountName. What’s next, is up to you! Perhaps you can leverage Azure DevOps windows agents to use this account to create AD users, groups, scheduled tasks, whatever. The sky is the limit :-).

That wraps up this post! Hopefully you learned something today. In real life, I use this method to automate the creation of service accounts. For questions, feel free send over a virtual beer at [email protected].